diff --git a/internal/api/grpc/admin/idp.go b/internal/api/grpc/admin/idp.go index 1e0e9ccdf5..dab331b793 100644 --- a/internal/api/grpc/admin/idp.go +++ b/internal/api/grpc/admin/idp.go @@ -283,6 +283,48 @@ func (s *Server) UpdateGitHubEnterpriseServerProvider(ctx context.Context, req * }, nil } +func (s *Server) AddGitLabProvider(ctx context.Context, req *admin_pb.AddGitLabProviderRequest) (*admin_pb.AddGitLabProviderResponse, error) { + id, details, err := s.command.AddInstanceGitLabProvider(ctx, addGitLabProviderToCommand(req)) + if err != nil { + return nil, err + } + return &admin_pb.AddGitLabProviderResponse{ + Id: id, + Details: object_pb.DomainToAddDetailsPb(details), + }, nil +} + +func (s *Server) UpdateGitLabProvider(ctx context.Context, req *admin_pb.UpdateGitLabProviderRequest) (*admin_pb.UpdateGitLabProviderResponse, error) { + details, err := s.command.UpdateInstanceGitLabProvider(ctx, req.Id, updateGitLabProviderToCommand(req)) + if err != nil { + return nil, err + } + return &admin_pb.UpdateGitLabProviderResponse{ + Details: object_pb.DomainToChangeDetailsPb(details), + }, nil +} + +func (s *Server) AddGitLabSelfHostedProvider(ctx context.Context, req *admin_pb.AddGitLabSelfHostedProviderRequest) (*admin_pb.AddGitLabSelfHostedProviderResponse, error) { + id, details, err := s.command.AddInstanceGitLabSelfHostedProvider(ctx, addGitLabSelfHostedProviderToCommand(req)) + if err != nil { + return nil, err + } + return &admin_pb.AddGitLabSelfHostedProviderResponse{ + Id: id, + Details: object_pb.DomainToAddDetailsPb(details), + }, nil +} + +func (s *Server) UpdateGitLabSelfHostedProvider(ctx context.Context, req *admin_pb.UpdateGitLabSelfHostedProviderRequest) (*admin_pb.UpdateGitLabSelfHostedProviderResponse, error) { + details, err := s.command.UpdateInstanceGitLabSelfHostedProvider(ctx, req.Id, updateGitLabSelfHostedProviderToCommand(req)) + if err != nil { + return nil, err + } + return &admin_pb.UpdateGitLabSelfHostedProviderResponse{ + Details: object_pb.DomainToChangeDetailsPb(details), + }, nil +} + func (s *Server) AddGoogleProvider(ctx context.Context, req *admin_pb.AddGoogleProviderRequest) (*admin_pb.AddGoogleProviderResponse, error) { id, details, err := s.command.AddInstanceGoogleProvider(ctx, addGoogleProviderToCommand(req)) if err != nil { diff --git a/internal/api/grpc/admin/idp_converter.go b/internal/api/grpc/admin/idp_converter.go index 48cc17cfe8..9a46f21069 100644 --- a/internal/api/grpc/admin/idp_converter.go +++ b/internal/api/grpc/admin/idp_converter.go @@ -319,6 +319,48 @@ func updateGitHubEnterpriseProviderToCommand(req *admin_pb.UpdateGitHubEnterpris } } +func addGitLabProviderToCommand(req *admin_pb.AddGitLabProviderRequest) command.GitLabProvider { + return command.GitLabProvider{ + Name: req.Name, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + Scopes: req.Scopes, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + } +} + +func updateGitLabProviderToCommand(req *admin_pb.UpdateGitLabProviderRequest) command.GitLabProvider { + return command.GitLabProvider{ + Name: req.Name, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + Scopes: req.Scopes, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + } +} + +func addGitLabSelfHostedProviderToCommand(req *admin_pb.AddGitLabSelfHostedProviderRequest) command.GitLabSelfHostedProvider { + return command.GitLabSelfHostedProvider{ + Name: req.Name, + Issuer: req.Issuer, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + Scopes: req.Scopes, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + } +} + +func updateGitLabSelfHostedProviderToCommand(req *admin_pb.UpdateGitLabSelfHostedProviderRequest) command.GitLabSelfHostedProvider { + return command.GitLabSelfHostedProvider{ + Name: req.Name, + Issuer: req.Issuer, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + Scopes: req.Scopes, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + } +} + func addGoogleProviderToCommand(req *admin_pb.AddGoogleProviderRequest) command.GoogleProvider { return command.GoogleProvider{ Name: req.Name, diff --git a/internal/api/grpc/idp/converter.go b/internal/api/grpc/idp/converter.go index fb17ae044b..5dbd53c553 100644 --- a/internal/api/grpc/idp/converter.go +++ b/internal/api/grpc/idp/converter.go @@ -420,6 +420,14 @@ func configToPb(config *query.IDPTemplate) *idp_pb.ProviderConfig { githubEnterpriseConfigToPb(providerConfig, config.GitHubEnterpriseIDPTemplate) return providerConfig } + if config.GitLabIDPTemplate != nil { + gitlabConfigToPb(providerConfig, config.GitLabIDPTemplate) + return providerConfig + } + if config.GitLabSelfHostedIDPTemplate != nil { + gitlabSelfHostedConfigToPb(providerConfig, config.GitLabSelfHostedIDPTemplate) + return providerConfig + } if config.GoogleIDPTemplate != nil { googleConfigToPb(providerConfig, config.GoogleIDPTemplate) return providerConfig @@ -486,6 +494,25 @@ func githubEnterpriseConfigToPb(providerConfig *idp_pb.ProviderConfig, template } } +func gitlabConfigToPb(providerConfig *idp_pb.ProviderConfig, template *query.GitLabIDPTemplate) { + providerConfig.Config = &idp_pb.ProviderConfig_Gitlab{ + Gitlab: &idp_pb.GitLabConfig{ + ClientId: template.ClientID, + Scopes: template.Scopes, + }, + } +} + +func gitlabSelfHostedConfigToPb(providerConfig *idp_pb.ProviderConfig, template *query.GitLabSelfHostedIDPTemplate) { + providerConfig.Config = &idp_pb.ProviderConfig_GitlabSelfHosted{ + GitlabSelfHosted: &idp_pb.GitLabSelfHostedConfig{ + ClientId: template.ClientID, + Issuer: template.Issuer, + Scopes: template.Scopes, + }, + } +} + func googleConfigToPb(providerConfig *idp_pb.ProviderConfig, template *query.GoogleIDPTemplate) { providerConfig.Config = &idp_pb.ProviderConfig_Google{ Google: &idp_pb.GoogleConfig{ diff --git a/internal/api/grpc/management/idp.go b/internal/api/grpc/management/idp.go index 7c787eb7e2..62e0e9a91b 100644 --- a/internal/api/grpc/management/idp.go +++ b/internal/api/grpc/management/idp.go @@ -275,6 +275,48 @@ func (s *Server) UpdateGitHubEnterpriseServerProvider(ctx context.Context, req * }, nil } +func (s *Server) AddGitLabProvider(ctx context.Context, req *mgmt_pb.AddGitLabProviderRequest) (*mgmt_pb.AddGitLabProviderResponse, error) { + id, details, err := s.command.AddOrgGitLabProvider(ctx, authz.GetCtxData(ctx).OrgID, addGitLabProviderToCommand(req)) + if err != nil { + return nil, err + } + return &mgmt_pb.AddGitLabProviderResponse{ + Id: id, + Details: object_pb.DomainToAddDetailsPb(details), + }, nil +} + +func (s *Server) UpdateGitLabProvider(ctx context.Context, req *mgmt_pb.UpdateGitLabProviderRequest) (*mgmt_pb.UpdateGitLabProviderResponse, error) { + details, err := s.command.UpdateOrgGitLabProvider(ctx, authz.GetCtxData(ctx).OrgID, req.Id, updateGitLabProviderToCommand(req)) + if err != nil { + return nil, err + } + return &mgmt_pb.UpdateGitLabProviderResponse{ + Details: object_pb.DomainToChangeDetailsPb(details), + }, nil +} + +func (s *Server) AddGitLabSelfHostedProvider(ctx context.Context, req *mgmt_pb.AddGitLabSelfHostedProviderRequest) (*mgmt_pb.AddGitLabSelfHostedProviderResponse, error) { + id, details, err := s.command.AddOrgGitLabSelfHostedProvider(ctx, authz.GetCtxData(ctx).OrgID, addGitLabSelfHostedProviderToCommand(req)) + if err != nil { + return nil, err + } + return &mgmt_pb.AddGitLabSelfHostedProviderResponse{ + Id: id, + Details: object_pb.DomainToAddDetailsPb(details), + }, nil +} + +func (s *Server) UpdateGitLabSelfHostedProvider(ctx context.Context, req *mgmt_pb.UpdateGitLabSelfHostedProviderRequest) (*mgmt_pb.UpdateGitLabSelfHostedProviderResponse, error) { + details, err := s.command.UpdateOrgGitLabSelfHostedProvider(ctx, authz.GetCtxData(ctx).OrgID, req.Id, updateGitLabSelfHostedProviderToCommand(req)) + if err != nil { + return nil, err + } + return &mgmt_pb.UpdateGitLabSelfHostedProviderResponse{ + Details: object_pb.DomainToChangeDetailsPb(details), + }, nil +} + func (s *Server) AddGoogleProvider(ctx context.Context, req *mgmt_pb.AddGoogleProviderRequest) (*mgmt_pb.AddGoogleProviderResponse, error) { id, details, err := s.command.AddOrgGoogleProvider(ctx, authz.GetCtxData(ctx).OrgID, addGoogleProviderToCommand(req)) if err != nil { diff --git a/internal/api/grpc/management/idp_converter.go b/internal/api/grpc/management/idp_converter.go index b402ee5e07..f2466c8a9b 100644 --- a/internal/api/grpc/management/idp_converter.go +++ b/internal/api/grpc/management/idp_converter.go @@ -336,6 +336,48 @@ func updateGitHubEnterpriseProviderToCommand(req *mgmt_pb.UpdateGitHubEnterprise } } +func addGitLabProviderToCommand(req *mgmt_pb.AddGitLabProviderRequest) command.GitLabProvider { + return command.GitLabProvider{ + Name: req.Name, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + Scopes: req.Scopes, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + } +} + +func updateGitLabProviderToCommand(req *mgmt_pb.UpdateGitLabProviderRequest) command.GitLabProvider { + return command.GitLabProvider{ + Name: req.Name, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + Scopes: req.Scopes, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + } +} + +func addGitLabSelfHostedProviderToCommand(req *mgmt_pb.AddGitLabSelfHostedProviderRequest) command.GitLabSelfHostedProvider { + return command.GitLabSelfHostedProvider{ + Name: req.Name, + Issuer: req.Issuer, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + Scopes: req.Scopes, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + } +} + +func updateGitLabSelfHostedProviderToCommand(req *mgmt_pb.UpdateGitLabSelfHostedProviderRequest) command.GitLabSelfHostedProvider { + return command.GitLabSelfHostedProvider{ + Name: req.Name, + Issuer: req.Issuer, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + Scopes: req.Scopes, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + } +} + func addGoogleProviderToCommand(req *mgmt_pb.AddGoogleProviderRequest) command.GoogleProvider { return command.GoogleProvider{ Name: req.Name, diff --git a/internal/api/ui/login/external_provider_handler.go b/internal/api/ui/login/external_provider_handler.go index 42ee14396d..5ea64e0bae 100644 --- a/internal/api/ui/login/external_provider_handler.go +++ b/internal/api/ui/login/external_provider_handler.go @@ -19,6 +19,7 @@ import ( "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/idp" "github.com/zitadel/zitadel/internal/idp/providers/github" + "github.com/zitadel/zitadel/internal/idp/providers/gitlab" "github.com/zitadel/zitadel/internal/idp/providers/google" "github.com/zitadel/zitadel/internal/idp/providers/jwt" "github.com/zitadel/zitadel/internal/idp/providers/oauth" @@ -146,12 +147,14 @@ func (l *Login) handleIDP(w http.ResponseWriter, r *http.Request, authReq *domai provider, err = l.githubProvider(r.Context(), identityProvider) case domain.IDPTypeGitHubEnterprise: provider, err = l.githubEnterpriseProvider(r.Context(), identityProvider) + case domain.IDPTypeGitLab: + provider, err = l.gitlabProvider(r.Context(), identityProvider) + case domain.IDPTypeGitLabSelfHosted: + provider, err = l.gitlabSelfHostedProvider(r.Context(), identityProvider) case domain.IDPTypeGoogle: provider, err = l.googleProvider(r.Context(), identityProvider) case domain.IDPTypeLDAP, domain.IDPTypeAzureAD, - domain.IDPTypeGitLab, - domain.IDPTypeGitLabSelfHosted, domain.IDPTypeUnspecified: fallthrough default: @@ -221,6 +224,20 @@ func (l *Login) handleExternalLoginCallback(w http.ResponseWriter, r *http.Reque return } session = &oauth.Session{Provider: provider.(*github.Provider).Provider, Code: data.Code} + case domain.IDPTypeGitLab: + provider, err = l.gitlabProvider(r.Context(), identityProvider) + if err != nil { + l.externalAuthFailed(w, r, authReq, nil, nil, err) + return + } + session = &openid.Session{Provider: provider.(*gitlab.Provider).Provider, Code: data.Code} + case domain.IDPTypeGitLabSelfHosted: + provider, err = l.gitlabSelfHostedProvider(r.Context(), identityProvider) + if err != nil { + l.externalAuthFailed(w, r, authReq, nil, nil, err) + return + } + session = &openid.Session{Provider: provider.(*gitlab.Provider).Provider, Code: data.Code} case domain.IDPTypeGoogle: provider, err = l.googleProvider(r.Context(), identityProvider) if err != nil { @@ -231,8 +248,6 @@ func (l *Login) handleExternalLoginCallback(w http.ResponseWriter, r *http.Reque case domain.IDPTypeJWT, domain.IDPTypeLDAP, domain.IDPTypeAzureAD, - domain.IDPTypeGitLab, - domain.IDPTypeGitLabSelfHosted, domain.IDPTypeUnspecified: fallthrough default: @@ -609,6 +624,7 @@ func (l *Login) oidcProvider(ctx context.Context, identityProvider *query.IDPTem l.baseURL(ctx)+EndpointExternalLoginCallback, identityProvider.OIDCIDPTemplate.Scopes, openid.DefaultMapper, + openid.WithSelectAccount(), ) } @@ -678,6 +694,34 @@ func (l *Login) githubEnterpriseProvider(ctx context.Context, identityProvider * ) } +func (l *Login) gitlabProvider(ctx context.Context, identityProvider *query.IDPTemplate) (*gitlab.Provider, error) { + secret, err := crypto.DecryptString(identityProvider.GitLabIDPTemplate.ClientSecret, l.idpConfigAlg) + if err != nil { + return nil, err + } + return gitlab.New( + identityProvider.GitLabIDPTemplate.ClientID, + secret, + l.baseURL(ctx)+EndpointExternalLoginCallback, + identityProvider.GitLabIDPTemplate.Scopes, + ) +} + +func (l *Login) gitlabSelfHostedProvider(ctx context.Context, identityProvider *query.IDPTemplate) (*gitlab.Provider, error) { + secret, err := crypto.DecryptString(identityProvider.GitLabSelfHostedIDPTemplate.ClientSecret, l.idpConfigAlg) + if err != nil { + return nil, err + } + return gitlab.NewCustomIssuer( + identityProvider.Name, + identityProvider.GitLabSelfHostedIDPTemplate.Issuer, + identityProvider.GitLabSelfHostedIDPTemplate.ClientID, + secret, + l.baseURL(ctx)+EndpointExternalLoginCallback, + identityProvider.GitLabSelfHostedIDPTemplate.Scopes, + ) +} + func (l *Login) appendUserGrants(ctx context.Context, userGrants []*domain.UserGrant, resourceOwner string) error { if len(userGrants) == 0 { return nil diff --git a/internal/api/ui/login/static/resources/images/idp/gitlab.png b/internal/api/ui/login/static/resources/images/idp/gitlab.png new file mode 100644 index 0000000000..b4c82efed9 Binary files /dev/null and b/internal/api/ui/login/static/resources/images/idp/gitlab.png differ diff --git a/internal/api/ui/login/static/resources/themes/scss/styles/identity_provider/identity_provider_base.scss b/internal/api/ui/login/static/resources/themes/scss/styles/identity_provider/identity_provider_base.scss index b459308b69..074ebd3460 100644 --- a/internal/api/ui/login/static/resources/themes/scss/styles/identity_provider/identity_provider_base.scss +++ b/internal/api/ui/login/static/resources/themes/scss/styles/identity_provider/identity_provider_base.scss @@ -4,6 +4,7 @@ $lgn-idp-provider-name-line-height: 36px; $lgn-idp-border-radius: .5rem; $googlelogosource: '../../../images/idp/google'; $githublogosource: '../../../images/idp/github'; +$gitlablogosource: '../../../images/idp/gitlab'; @mixin lgn-idp-base { display: block; @@ -52,4 +53,16 @@ $githublogosource: '../../../images/idp/github'; border-radius: 5px; } } + + &.gitlab { + span.logo { + height: 46px; + width: 46px; + background-image: url($gitlablogosource + '.png'); + background-size: 100%; + background-position: center; + background-repeat: no-repeat; + border-radius: 5px; + } + } } diff --git a/internal/api/ui/login/static/resources/themes/scss/styles/identity_provider/identity_provider_theme.scss b/internal/api/ui/login/static/resources/themes/scss/styles/identity_provider/identity_provider_theme.scss index 2c4c746228..0d29a038ce 100644 --- a/internal/api/ui/login/static/resources/themes/scss/styles/identity_provider/identity_provider_theme.scss +++ b/internal/api/ui/login/static/resources/themes/scss/styles/identity_provider/identity_provider_theme.scss @@ -26,6 +26,11 @@ color: var(--zitadel-color-github-text); background-color: var(--zitadel-color-github-background); } + + &.gitlab { + color: var(--zitadel-color-gitlab-text); + background-color: var(--zitadel-color-gitlab-background); + } } .lgn-idp-providers { diff --git a/internal/api/ui/login/static/resources/themes/scss/styles/vars.scss b/internal/api/ui/login/static/resources/themes/scss/styles/vars.scss index 8f3582c38a..304ed8a4f7 100644 --- a/internal/api/ui/login/static/resources/themes/scss/styles/vars.scss +++ b/internal/api/ui/login/static/resources/themes/scss/styles/vars.scss @@ -115,6 +115,8 @@ --zitadel-color-google-background: #ffffff; --zitadel-color-github-text: #8b8d8d; --zitadel-color-github-background: #ffffff; + --zitadel-color-gitlab-text: #8b8d8d; + --zitadel-color-gitlab-background: #ffffff; --zitadel-color-qr: var(--zitadel-color-black); --zitadel-color-qr-background: var(--zitadel-color-white); @@ -218,4 +220,6 @@ --zitadel-color-google-background: #ffffff; --zitadel-color-github-text: #8b8d8d; --zitadel-color-github-background: #ffffff; + --zitadel-color-gitlab-text: #8b8d8d; + --zitadel-color-gitlab-background: #ffffff; } 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 bce99cadb6..acbae860e9 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 @@ -100,6 +100,8 @@ --zitadel-color-google-background: #ffffff; --zitadel-color-github-text: #8b8d8d; --zitadel-color-github-background: #ffffff; + --zitadel-color-gitlab-text: #8b8d8d; + --zitadel-color-gitlab-background: #ffffff; --zitadel-color-qr: var(--zitadel-color-black); --zitadel-color-qr-background: var(--zitadel-color-white); } @@ -188,6 +190,8 @@ --zitadel-color-google-background: #ffffff; --zitadel-color-github-text: #8b8d8d; --zitadel-color-github-background: #ffffff; + --zitadel-color-gitlab-text: #8b8d8d; + --zitadel-color-gitlab-background: #ffffff; } body { @@ -572,6 +576,15 @@ a.sub-formfield-link { background-repeat: no-repeat; border-radius: 5px; } +.lgn-idp.gitlab span.logo { + height: 46px; + width: 46px; + background-image: url("../../../images/idp/gitlab.png"); + background-size: 100%; + background-position: center; + background-repeat: no-repeat; + border-radius: 5px; +} .lgn-error { display: flex; @@ -1556,6 +1569,15 @@ a.sub-formfield-link { background-repeat: no-repeat; border-radius: 5px; } +.lgn-idp.gitlab span.logo { + height: 46px; + width: 46px; + background-image: url("../../../images/idp/gitlab.png"); + background-size: 100%; + background-position: center; + background-repeat: no-repeat; + border-radius: 5px; +} .lgn-error { display: flex; @@ -3073,6 +3095,10 @@ ul li i.lgn-valid { color: var(--zitadel-color-github-text); background-color: var(--zitadel-color-github-background); } +.lgn-idp.gitlab { + color: var(--zitadel-color-gitlab-text); + background-color: var(--zitadel-color-gitlab-background); +} .lgn-idp-providers .lgn-idp-desc { color: var(--zitadel-color-label); 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 8619068f9c..c1bd90d0c0 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;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;;;AC3NF;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;ECMI;EACA,QATa;EAUb;EACA;EACA;EACA;EACA,SAbc;EAcd,eAZoB;EAapB;EACA;;AAEA;EACI;EACA;;AAGJ;EACI,aAvB4B;EAwB5B;EACA;EACA;;AAIA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;ACnDZ;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;ECMI;EACA,QATa;EAUb;EACA;EACA;EACA;EACA,SAbc;EAcd,eAZoB;EAapB;EACA;;AAEA;EACI;EACA;;AAGJ;EACI,aAvB4B;EAwB5B;EACA;EACA;;AAIA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;ACnDZ;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;;;AAKF;EACE;;;ACxBJ;EACE;EACA;;;ACAQ;EACE;EACA;;AAKN;EACE;;AAGF;EACE;EACA;EACA","file":"zitadel.css"} \ No newline at end of file +{"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"} diff --git a/internal/api/ui/login/static/templates/login.html b/internal/api/ui/login/static/templates/login.html index c811e001cb..33c81a005e 100644 --- a/internal/api/ui/login/static/templates/login.html +++ b/internal/api/ui/login/static/templates/login.html @@ -49,7 +49,7 @@ - {{$provider.Name}} + {{$provider.DisplayName}} {{end}} diff --git a/internal/api/ui/login/static/templates/register_option.html b/internal/api/ui/login/static/templates/register_option.html index 8ff122d9e0..24de130a33 100644 --- a/internal/api/ui/login/static/templates/register_option.html +++ b/internal/api/ui/login/static/templates/register_option.html @@ -29,7 +29,7 @@ - {{$provider.Name}} + {{$provider.DisplayName}} {{end}} {{end}} diff --git a/internal/command/idp.go b/internal/command/idp.go index 360ca6a9af..660d0cc7c3 100644 --- a/internal/command/idp.go +++ b/internal/command/idp.go @@ -57,6 +57,23 @@ type GitHubEnterpriseProvider struct { IDPOptions idp.Options } +type GitLabProvider struct { + Name string + ClientID string + ClientSecret string + Scopes []string + IDPOptions idp.Options +} + +type GitLabSelfHostedProvider struct { + Name string + Issuer string + ClientID string + ClientSecret string + Scopes []string + IDPOptions idp.Options +} + type GoogleProvider struct { Name string ClientID string diff --git a/internal/command/idp_model.go b/internal/command/idp_model.go index 4d253abb94..147df0291a 100644 --- a/internal/command/idp_model.go +++ b/internal/command/idp_model.go @@ -613,6 +613,188 @@ func (wm *GitHubEnterpriseIDPWriteModel) NewChanges( return changes, nil } +type GitLabIDPWriteModel struct { + eventstore.WriteModel + + ID string + Name string + ClientID string + ClientSecret *crypto.CryptoValue + Scopes []string + idp.Options + + State domain.IDPState +} + +func (wm *GitLabIDPWriteModel) Reduce() error { + for _, event := range wm.Events { + switch e := event.(type) { + case *idp.GitLabIDPAddedEvent: + wm.reduceAddedEvent(e) + case *idp.GitLabIDPChangedEvent: + wm.reduceChangedEvent(e) + case *idp.RemovedEvent: + wm.State = domain.IDPStateRemoved + } + } + return wm.WriteModel.Reduce() +} + +func (wm *GitLabIDPWriteModel) reduceAddedEvent(e *idp.GitLabIDPAddedEvent) { + wm.Name = e.Name + wm.ClientID = e.ClientID + wm.ClientSecret = e.ClientSecret + wm.Scopes = e.Scopes + wm.Options = e.Options + wm.State = domain.IDPStateActive +} + +func (wm *GitLabIDPWriteModel) reduceChangedEvent(e *idp.GitLabIDPChangedEvent) { + if e.Name != nil { + wm.Name = *e.Name + } + if e.ClientID != nil { + wm.ClientID = *e.ClientID + } + if e.ClientSecret != nil { + wm.ClientSecret = e.ClientSecret + } + if e.Scopes != nil { + wm.Scopes = e.Scopes + } + wm.Options.ReduceChanges(e.OptionChanges) +} + +func (wm *GitLabIDPWriteModel) NewChanges( + name, + clientID, + clientSecretString string, + secretCrypto crypto.Crypto, + scopes []string, + options idp.Options, +) ([]idp.GitLabIDPChanges, error) { + changes := make([]idp.GitLabIDPChanges, 0) + var clientSecret *crypto.CryptoValue + var err error + if clientSecretString != "" { + clientSecret, err = crypto.Crypt([]byte(clientSecretString), secretCrypto) + if err != nil { + return nil, err + } + changes = append(changes, idp.ChangeGitLabClientSecret(clientSecret)) + } + if wm.Name != name { + changes = append(changes, idp.ChangeGitLabName(name)) + } + if wm.ClientID != clientID { + changes = append(changes, idp.ChangeGitLabClientID(clientID)) + } + if !reflect.DeepEqual(wm.Scopes, scopes) { + changes = append(changes, idp.ChangeGitLabScopes(scopes)) + } + + opts := wm.Options.Changes(options) + if !opts.IsZero() { + changes = append(changes, idp.ChangeGitLabOptions(opts)) + } + return changes, nil +} + +type GitLabSelfHostedIDPWriteModel struct { + eventstore.WriteModel + + ID string + Name string + Issuer string + ClientID string + ClientSecret *crypto.CryptoValue + Scopes []string + idp.Options + + State domain.IDPState +} + +func (wm *GitLabSelfHostedIDPWriteModel) Reduce() error { + for _, event := range wm.Events { + switch e := event.(type) { + case *idp.GitLabSelfHostedIDPAddedEvent: + wm.reduceAddedEvent(e) + case *idp.GitLabSelfHostedIDPChangedEvent: + wm.reduceChangedEvent(e) + case *idp.RemovedEvent: + wm.State = domain.IDPStateRemoved + } + } + return wm.WriteModel.Reduce() +} + +func (wm *GitLabSelfHostedIDPWriteModel) reduceAddedEvent(e *idp.GitLabSelfHostedIDPAddedEvent) { + wm.Name = e.Name + wm.Issuer = e.Issuer + wm.ClientID = e.ClientID + wm.ClientSecret = e.ClientSecret + wm.Scopes = e.Scopes + wm.Options = e.Options + wm.State = domain.IDPStateActive +} + +func (wm *GitLabSelfHostedIDPWriteModel) reduceChangedEvent(e *idp.GitLabSelfHostedIDPChangedEvent) { + if e.ClientID != nil { + wm.ClientID = *e.ClientID + } + if e.ClientSecret != nil { + wm.ClientSecret = e.ClientSecret + } + if e.Name != nil { + wm.Name = *e.Name + } + if e.Issuer != nil { + wm.Issuer = *e.Issuer + } + if e.Scopes != nil { + wm.Scopes = e.Scopes + } + wm.Options.ReduceChanges(e.OptionChanges) +} + +func (wm *GitLabSelfHostedIDPWriteModel) NewChanges( + name string, + issuer string, + clientID string, + clientSecretString string, + secretCrypto crypto.Crypto, + scopes []string, + options idp.Options, +) ([]idp.GitLabSelfHostedIDPChanges, error) { + changes := make([]idp.GitLabSelfHostedIDPChanges, 0) + var clientSecret *crypto.CryptoValue + var err error + if clientSecretString != "" { + clientSecret, err = crypto.Crypt([]byte(clientSecretString), secretCrypto) + if err != nil { + return nil, err + } + changes = append(changes, idp.ChangeGitLabSelfHostedClientSecret(clientSecret)) + } + if wm.ClientID != clientID { + changes = append(changes, idp.ChangeGitLabSelfHostedClientID(clientID)) + } + if wm.Name != name { + changes = append(changes, idp.ChangeGitLabSelfHostedName(name)) + } + if wm.Issuer != issuer { + changes = append(changes, idp.ChangeGitLabSelfHostedIssuer(issuer)) + } + if !reflect.DeepEqual(wm.Scopes, scopes) { + changes = append(changes, idp.ChangeGitLabSelfHostedScopes(scopes)) + } + opts := wm.Options.Changes(options) + if !opts.IsZero() { + changes = append(changes, idp.ChangeGitLabSelfHostedOptions(opts)) + } + return changes, nil +} + type GoogleIDPWriteModel struct { eventstore.WriteModel @@ -871,6 +1053,10 @@ func (wm *IDPRemoveWriteModel) Reduce() error { wm.reduceAdded(e.ID) case *idp.GitHubEnterpriseIDPAddedEvent: wm.reduceAdded(e.ID) + case *idp.GitLabIDPAddedEvent: + wm.reduceAdded(e.ID) + case *idp.GitLabSelfHostedIDPAddedEvent: + wm.reduceAdded(e.ID) case *idp.GoogleIDPAddedEvent: wm.reduceAdded(e.ID) case *idp.LDAPIDPAddedEvent: diff --git a/internal/command/instance_idp.go b/internal/command/instance_idp.go index 15c38a8f25..422ca44215 100644 --- a/internal/command/instance_idp.go +++ b/internal/command/instance_idp.go @@ -223,6 +223,90 @@ func (c *Commands) UpdateInstanceGitHubEnterpriseProvider(ctx context.Context, i return pushedEventsToObjectDetails(pushedEvents), nil } +func (c *Commands) AddInstanceGitLabProvider(ctx context.Context, provider GitLabProvider) (string, *domain.ObjectDetails, error) { + instanceID := authz.GetInstance(ctx).InstanceID() + instanceAgg := instance.NewAggregate(instanceID) + id, err := c.idGenerator.Next() + if err != nil { + return "", nil, err + } + writeModel := NewGitLabInstanceIDPWriteModel(instanceID, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareAddInstanceGitLabProvider(instanceAgg, writeModel, provider)) + if err != nil { + return "", nil, err + } + pushedEvents, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return "", nil, err + } + return id, pushedEventsToObjectDetails(pushedEvents), nil +} + +func (c *Commands) UpdateInstanceGitLabProvider(ctx context.Context, id string, provider GitLabProvider) (*domain.ObjectDetails, error) { + instanceID := authz.GetInstance(ctx).InstanceID() + instanceAgg := instance.NewAggregate(instanceID) + writeModel := NewGitLabInstanceIDPWriteModel(instanceID, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareUpdateInstanceGitLabProvider(instanceAgg, writeModel, provider)) + if err != nil { + return nil, err + } + if len(cmds) == 0 { + // no change, so return directly + return &domain.ObjectDetails{ + Sequence: writeModel.ProcessedSequence, + EventDate: writeModel.ChangeDate, + ResourceOwner: writeModel.ResourceOwner, + }, nil + } + pushedEvents, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return nil, err + } + return pushedEventsToObjectDetails(pushedEvents), nil +} + +func (c *Commands) AddInstanceGitLabSelfHostedProvider(ctx context.Context, provider GitLabSelfHostedProvider) (string, *domain.ObjectDetails, error) { + instanceID := authz.GetInstance(ctx).InstanceID() + instanceAgg := instance.NewAggregate(instanceID) + id, err := c.idGenerator.Next() + if err != nil { + return "", nil, err + } + writeModel := NewGitLabSelfHostedInstanceIDPWriteModel(instanceID, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareAddInstanceGitLabSelfHostedProvider(instanceAgg, writeModel, provider)) + if err != nil { + return "", nil, err + } + pushedEvents, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return "", nil, err + } + return id, pushedEventsToObjectDetails(pushedEvents), nil +} + +func (c *Commands) UpdateInstanceGitLabSelfHostedProvider(ctx context.Context, id string, provider GitLabSelfHostedProvider) (*domain.ObjectDetails, error) { + instanceID := authz.GetInstance(ctx).InstanceID() + instanceAgg := instance.NewAggregate(instanceID) + writeModel := NewGitLabSelfHostedInstanceIDPWriteModel(instanceID, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareUpdateInstanceGitLabSelfHostedProvider(instanceAgg, writeModel, provider)) + if err != nil { + return nil, err + } + if len(cmds) == 0 { + // no change, so return directly + return &domain.ObjectDetails{ + Sequence: writeModel.ProcessedSequence, + EventDate: writeModel.ChangeDate, + ResourceOwner: writeModel.ResourceOwner, + }, nil + } + pushedEvents, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return nil, err + } + return pushedEventsToObjectDetails(pushedEvents), nil +} + func (c *Commands) AddInstanceGoogleProvider(ctx context.Context, provider GoogleProvider) (string, *domain.ObjectDetails, error) { instanceID := authz.GetInstance(ctx).InstanceID() instanceAgg := instance.NewAggregate(instanceID) @@ -395,23 +479,26 @@ func (c *Commands) prepareAddInstanceOAuthProvider(a *instance.Aggregate, writeM func (c *Commands) prepareUpdateInstanceOAuthProvider(a *instance.Aggregate, writeModel *InstanceOAuthIDPWriteModel, provider GenericOAuthProvider) preparation.Validation { return func() (preparation.CreateCommands, error) { + if writeModel.ID = strings.TrimSpace(writeModel.ID); writeModel.ID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-SAffg", "Errors.Invalid.Argument") + } if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { - return nil, caos_errs.ThrowInvalidArgument(nil, "INST-D32ef", "Errors.Invalid.Argument") + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-Sf3gh", "Errors.Invalid.Argument") } if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { - return nil, caos_errs.ThrowInvalidArgument(nil, "INST-Dbgzf", "Errors.Invalid.Argument") + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-SHJ3ui", "Errors.Invalid.Argument") } if provider.AuthorizationEndpoint = strings.TrimSpace(provider.AuthorizationEndpoint); provider.AuthorizationEndpoint == "" { - return nil, caos_errs.ThrowInvalidArgument(nil, "INST-B23bs", "Errors.Invalid.Argument") + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-SVrgh", "Errors.Invalid.Argument") } if provider.TokenEndpoint = strings.TrimSpace(provider.TokenEndpoint); provider.TokenEndpoint == "" { - return nil, caos_errs.ThrowInvalidArgument(nil, "INST-D2gj8", "Errors.Invalid.Argument") + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-DJKeio", "Errors.Invalid.Argument") } if provider.UserEndpoint = strings.TrimSpace(provider.UserEndpoint); provider.UserEndpoint == "" { - return nil, caos_errs.ThrowInvalidArgument(nil, "INST-Fb8jk", "Errors.Invalid.Argument") + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-ILSJi", "Errors.Invalid.Argument") } if provider.IDAttribute = strings.TrimSpace(provider.IDAttribute); provider.IDAttribute == "" { - return nil, caos_errs.ThrowInvalidArgument(nil, "INST-asf3fs", "Errors.Invalid.Argument") + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-JKD3h", "Errors.Invalid.Argument") } return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { events, err := filter(ctx, writeModel.Query()) @@ -700,12 +787,9 @@ func (c *Commands) prepareUpdateInstanceGitHubProvider(a *instance.Aggregate, wr provider.Scopes, provider.IDPOptions, ) - if err != nil { + if err != nil || event == nil { return nil, err } - if event == nil { - return nil, nil - } return []eventstore.Command{event}, nil }, nil } @@ -809,11 +893,174 @@ func (c *Commands) prepareUpdateInstanceGitHubEnterpriseProvider(a *instance.Agg provider.Scopes, provider.IDPOptions, ) + if err != nil || event == nil { + return nil, err + } + return []eventstore.Command{event}, nil + }, nil + } +} + +func (c *Commands) prepareAddInstanceGitLabProvider(a *instance.Aggregate, writeModel *InstanceGitLabIDPWriteModel, provider GitLabProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-adsg2", "Errors.Invalid.Argument") + } + if provider.ClientSecret = strings.TrimSpace(provider.ClientSecret); provider.ClientSecret == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-GD1j2", "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 } - if event == nil { - return nil, nil + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + secret, err := crypto.Encrypt([]byte(provider.ClientSecret), c.idpConfigEncryption) + if err != nil { + return nil, err + } + return []eventstore.Command{ + instance.NewGitLabIDPAddedEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.ClientID, + secret, + provider.Scopes, + provider.IDPOptions, + ), + }, nil + }, nil + } +} + +func (c *Commands) prepareUpdateInstanceGitLabProvider(a *instance.Aggregate, writeModel *InstanceGitLabIDPWriteModel, provider GitLabProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if writeModel.ID = strings.TrimSpace(writeModel.ID); writeModel.ID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-HJK91", "Errors.Invalid.Argument") + } + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-D12t6", "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-HBReq", "Errors.Instance.IDPConfig.NotExisting") + } + event, err := writeModel.NewChangedEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.ClientID, + provider.ClientSecret, + c.idpConfigEncryption, + provider.Scopes, + provider.IDPOptions, + ) + if err != nil || event == nil { + return nil, err + } + return []eventstore.Command{event}, nil + }, nil + } +} + +func (c *Commands) prepareAddInstanceGitLabSelfHostedProvider(a *instance.Aggregate, writeModel *InstanceGitLabSelfHostedIDPWriteModel, provider GitLabSelfHostedProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-jw4ZT", "Errors.Invalid.Argument") + } + if provider.Issuer = strings.TrimSpace(provider.Issuer); provider.Issuer == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-AST4S", "Errors.Invalid.Argument") + } + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-DBZHJ", "Errors.Invalid.Argument") + } + if provider.ClientSecret = strings.TrimSpace(provider.ClientSecret); provider.ClientSecret == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-SDGJ4", "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 + } + secret, err := crypto.Encrypt([]byte(provider.ClientSecret), c.idpConfigEncryption) + if err != nil { + return nil, err + } + return []eventstore.Command{ + instance.NewGitLabSelfHostedIDPAddedEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.Issuer, + provider.ClientID, + secret, + provider.Scopes, + provider.IDPOptions, + ), + }, nil + }, nil + } +} + +func (c *Commands) prepareUpdateInstanceGitLabSelfHostedProvider(a *instance.Aggregate, writeModel *InstanceGitLabSelfHostedIDPWriteModel, provider GitLabSelfHostedProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if writeModel.ID = strings.TrimSpace(writeModel.ID); writeModel.ID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-SAFG4", "Errors.Invalid.Argument") + } + if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-DG4H", "Errors.Invalid.Argument") + } + if provider.Issuer = strings.TrimSpace(provider.Issuer); provider.Issuer == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-SD4eb", "Errors.Invalid.Argument") + } + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-GHWE3", "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-D2tg1", "Errors.Instance.IDPConfig.NotExisting") + } + event, err := writeModel.NewChangedEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.Issuer, + provider.ClientID, + provider.ClientSecret, + c.idpConfigEncryption, + provider.Scopes, + provider.IDPOptions, + ) + if err != nil || event == nil { + return nil, err } return []eventstore.Command{event}, nil }, nil diff --git a/internal/command/instance_idp_model.go b/internal/command/instance_idp_model.go index e18d6ee9f1..d8eb93eb83 100644 --- a/internal/command/instance_idp_model.go +++ b/internal/command/instance_idp_model.go @@ -25,10 +25,6 @@ func NewOAuthInstanceIDPWriteModel(instanceID, id string) *InstanceOAuthIDPWrite } } -func (wm *InstanceOAuthIDPWriteModel) Reduce() error { - return wm.OAuthIDPWriteModel.Reduce() -} - func (wm *InstanceOAuthIDPWriteModel) AppendEvents(events ...eventstore.Event) { for _, event := range events { switch e := event.(type) { @@ -107,10 +103,6 @@ func NewOIDCInstanceIDPWriteModel(instanceID, id string) *InstanceOIDCIDPWriteMo } } -func (wm *InstanceOIDCIDPWriteModel) Reduce() error { - return wm.OIDCIDPWriteModel.Reduce() -} - func (wm *InstanceOIDCIDPWriteModel) AppendEvents(events ...eventstore.Event) { for _, event := range events { switch e := event.(type) { @@ -208,10 +200,6 @@ func NewJWTInstanceIDPWriteModel(instanceID, id string) *InstanceJWTIDPWriteMode } } -func (wm *InstanceJWTIDPWriteModel) Reduce() error { - return wm.JWTIDPWriteModel.Reduce() -} - func (wm *InstanceJWTIDPWriteModel) AppendEvents(events ...eventstore.Event) { for _, event := range events { switch e := event.(type) { @@ -307,10 +295,6 @@ func NewGitHubInstanceIDPWriteModel(instanceID, id string) *InstanceGitHubIDPWri } } -func (wm *InstanceGitHubIDPWriteModel) Reduce() error { - return wm.GitHubIDPWriteModel.Reduce() -} - func (wm *InstanceGitHubIDPWriteModel) AppendEvents(events ...eventstore.Event) { for _, event := range events { switch e := event.(type) { @@ -376,10 +360,6 @@ func NewGitHubEnterpriseInstanceIDPWriteModel(instanceID, id string) *InstanceGi } } -func (wm *InstanceGitHubEnterpriseIDPWriteModel) Reduce() error { - return wm.GitHubEnterpriseIDPWriteModel.Reduce() -} - func (wm *InstanceGitHubEnterpriseIDPWriteModel) AppendEvents(events ...eventstore.Event) { for _, event := range events { switch e := event.(type) { @@ -442,6 +422,137 @@ func (wm *InstanceGitHubEnterpriseIDPWriteModel) NewChangedEvent( return instance.NewGitHubEnterpriseIDPChangedEvent(ctx, aggregate, id, changes) } +type InstanceGitLabIDPWriteModel struct { + GitLabIDPWriteModel +} + +func NewGitLabInstanceIDPWriteModel(instanceID, id string) *InstanceGitLabIDPWriteModel { + return &InstanceGitLabIDPWriteModel{ + GitLabIDPWriteModel{ + WriteModel: eventstore.WriteModel{ + AggregateID: instanceID, + ResourceOwner: instanceID, + }, + ID: id, + }, + } +} + +func (wm *InstanceGitLabIDPWriteModel) AppendEvents(events ...eventstore.Event) { + for _, event := range events { + switch e := event.(type) { + case *instance.GitLabIDPAddedEvent: + wm.GitLabIDPWriteModel.AppendEvents(&e.GitLabIDPAddedEvent) + case *instance.GitLabIDPChangedEvent: + wm.GitLabIDPWriteModel.AppendEvents(&e.GitLabIDPChangedEvent) + case *instance.IDPRemovedEvent: + wm.GitLabIDPWriteModel.AppendEvents(&e.RemovedEvent) + default: + wm.GitLabIDPWriteModel.AppendEvents(e) + } + } +} + +func (wm *InstanceGitLabIDPWriteModel) Query() *eventstore.SearchQueryBuilder { + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + ResourceOwner(wm.ResourceOwner). + AddQuery(). + AggregateTypes(instance.AggregateType). + AggregateIDs(wm.AggregateID). + EventTypes( + instance.GitLabIDPAddedEventType, + instance.GitLabIDPChangedEventType, + instance.IDPRemovedEventType, + ). + EventData(map[string]interface{}{"id": wm.ID}). + Builder() +} + +func (wm *InstanceGitLabIDPWriteModel) NewChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + clientID string, + clientSecretString string, + secretCrypto crypto.Crypto, + scopes []string, + options idp.Options, +) (*instance.GitLabIDPChangedEvent, error) { + + changes, err := wm.GitLabIDPWriteModel.NewChanges(name, clientID, clientSecretString, secretCrypto, scopes, options) + if err != nil || len(changes) == 0 { + return nil, err + } + return instance.NewGitLabIDPChangedEvent(ctx, aggregate, id, changes) +} + +type InstanceGitLabSelfHostedIDPWriteModel struct { + GitLabSelfHostedIDPWriteModel +} + +func NewGitLabSelfHostedInstanceIDPWriteModel(instanceID, id string) *InstanceGitLabSelfHostedIDPWriteModel { + return &InstanceGitLabSelfHostedIDPWriteModel{ + GitLabSelfHostedIDPWriteModel{ + WriteModel: eventstore.WriteModel{ + AggregateID: instanceID, + ResourceOwner: instanceID, + }, + ID: id, + }, + } +} + +func (wm *InstanceGitLabSelfHostedIDPWriteModel) AppendEvents(events ...eventstore.Event) { + for _, event := range events { + switch e := event.(type) { + case *instance.GitLabSelfHostedIDPAddedEvent: + wm.GitLabSelfHostedIDPWriteModel.AppendEvents(&e.GitLabSelfHostedIDPAddedEvent) + case *instance.GitLabSelfHostedIDPChangedEvent: + wm.GitLabSelfHostedIDPWriteModel.AppendEvents(&e.GitLabSelfHostedIDPChangedEvent) + case *instance.IDPRemovedEvent: + wm.GitLabSelfHostedIDPWriteModel.AppendEvents(&e.RemovedEvent) + default: + wm.GitLabSelfHostedIDPWriteModel.AppendEvents(e) + } + } +} + +func (wm *InstanceGitLabSelfHostedIDPWriteModel) Query() *eventstore.SearchQueryBuilder { + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + ResourceOwner(wm.ResourceOwner). + AddQuery(). + AggregateTypes(instance.AggregateType). + AggregateIDs(wm.AggregateID). + EventTypes( + instance.GitLabSelfHostedIDPAddedEventType, + instance.GitLabSelfHostedIDPChangedEventType, + instance.IDPRemovedEventType, + ). + EventData(map[string]interface{}{"id": wm.ID}). + Builder() +} + +func (wm *InstanceGitLabSelfHostedIDPWriteModel) NewChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + issuer, + clientID string, + clientSecretString string, + secretCrypto crypto.Crypto, + scopes []string, + options idp.Options, +) (*instance.GitLabSelfHostedIDPChangedEvent, error) { + + changes, err := wm.GitLabSelfHostedIDPWriteModel.NewChanges(name, issuer, clientID, clientSecretString, secretCrypto, scopes, options) + if err != nil || len(changes) == 0 { + return nil, err + } + return instance.NewGitLabSelfHostedIDPChangedEvent(ctx, aggregate, id, changes) +} + type InstanceGoogleIDPWriteModel struct { GoogleIDPWriteModel } @@ -458,10 +569,6 @@ func NewGoogleInstanceIDPWriteModel(instanceID, id string) *InstanceGoogleIDPWri } } -func (wm *InstanceGoogleIDPWriteModel) Reduce() error { - return wm.GoogleIDPWriteModel.Reduce() -} - func (wm *InstanceGoogleIDPWriteModel) AppendEvents(events ...eventstore.Event) { for _, event := range events { switch e := event.(type) { @@ -525,10 +632,6 @@ func NewLDAPInstanceIDPWriteModel(instanceID, id string) *InstanceLDAPIDPWriteMo } } -func (wm *InstanceLDAPIDPWriteModel) Reduce() error { - return wm.LDAPIDPWriteModel.Reduce() -} - func (wm *InstanceLDAPIDPWriteModel) AppendEvents(events ...eventstore.Event) { for _, event := range events { switch e := event.(type) { @@ -614,10 +717,6 @@ func NewInstanceIDPRemoveWriteModel(instanceID, id string) *InstanceIDPRemoveWri } } -func (wm *InstanceIDPRemoveWriteModel) Reduce() error { - return wm.IDPRemoveWriteModel.Reduce() -} - func (wm *InstanceIDPRemoveWriteModel) AppendEvents(events ...eventstore.Event) { for _, event := range events { switch e := event.(type) { @@ -631,6 +730,10 @@ func (wm *InstanceIDPRemoveWriteModel) AppendEvents(events ...eventstore.Event) wm.IDPRemoveWriteModel.AppendEvents(&e.GitHubIDPAddedEvent) case *instance.GitHubEnterpriseIDPAddedEvent: wm.IDPRemoveWriteModel.AppendEvents(&e.GitHubEnterpriseIDPAddedEvent) + case *instance.GitLabIDPAddedEvent: + wm.IDPRemoveWriteModel.AppendEvents(&e.GitLabIDPAddedEvent) + case *instance.GitLabSelfHostedIDPAddedEvent: + wm.IDPRemoveWriteModel.AppendEvents(&e.GitLabSelfHostedIDPAddedEvent) case *instance.GoogleIDPAddedEvent: wm.IDPRemoveWriteModel.AppendEvents(&e.GoogleIDPAddedEvent) case *instance.LDAPIDPAddedEvent: @@ -659,6 +762,8 @@ func (wm *InstanceIDPRemoveWriteModel) Query() *eventstore.SearchQueryBuilder { instance.JWTIDPAddedEventType, instance.GitHubIDPAddedEventType, instance.GitHubEnterpriseIDPAddedEventType, + instance.GitLabIDPAddedEventType, + instance.GitLabSelfHostedIDPAddedEventType, instance.GoogleIDPAddedEventType, instance.LDAPIDPAddedEventType, instance.IDPRemovedEventType, diff --git a/internal/command/instance_idp_test.go b/internal/command/instance_idp_test.go index 6ade24c2ad..e609a80b33 100644 --- a/internal/command/instance_idp_test.go +++ b/internal/command/instance_idp_test.go @@ -2,6 +2,7 @@ package command import ( "context" + "errors" "testing" "github.com/golang/mock/gomock" @@ -52,7 +53,9 @@ func TestCommandSide_AddInstanceGenericOAuthIDP(t *testing.T) { provider: GenericOAuthProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-D32ef", "")) + }, }, }, { @@ -68,7 +71,9 @@ func TestCommandSide_AddInstanceGenericOAuthIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-Dbgzf", "")) + }, }, }, { @@ -85,7 +90,9 @@ func TestCommandSide_AddInstanceGenericOAuthIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-DF4ga", "")) + }, }, }, { @@ -103,7 +110,9 @@ func TestCommandSide_AddInstanceGenericOAuthIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-B23bs", "")) + }, }, }, { @@ -122,7 +131,9 @@ func TestCommandSide_AddInstanceGenericOAuthIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-D2gj8", "")) + }, }, }, { @@ -142,7 +153,9 @@ func TestCommandSide_AddInstanceGenericOAuthIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-Fb8jk", "")) + }, }, }, { @@ -163,7 +176,9 @@ func TestCommandSide_AddInstanceGenericOAuthIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-sdf3f", "")) + }, }, }, { @@ -329,7 +344,9 @@ func TestCommandSide_UpdateInstanceGenericOAuthIDP(t *testing.T) { provider: GenericOAuthProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-SAffg", "")) + }, }, }, { @@ -343,7 +360,9 @@ func TestCommandSide_UpdateInstanceGenericOAuthIDP(t *testing.T) { provider: GenericOAuthProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-Sf3gh", "")) + }, }, }, { @@ -359,7 +378,9 @@ func TestCommandSide_UpdateInstanceGenericOAuthIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-SHJ3ui", "")) + }, }, }, { @@ -371,11 +392,14 @@ func TestCommandSide_UpdateInstanceGenericOAuthIDP(t *testing.T) { ctx: authz.WithInstanceID(context.Background(), "instance1"), id: "id1", provider: GenericOAuthProvider{ - Name: "name", + Name: "name", + ClientID: "clientID", }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-SVrgh", "")) + }, }, }, { @@ -393,7 +417,9 @@ func TestCommandSide_UpdateInstanceGenericOAuthIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-DJKeio", "")) + }, }, }, { @@ -412,7 +438,9 @@ func TestCommandSide_UpdateInstanceGenericOAuthIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-ILSJi", "")) + }, }, }, { @@ -432,7 +460,9 @@ func TestCommandSide_UpdateInstanceGenericOAuthIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-JKD3h", "")) + }, }, }, { @@ -639,7 +669,9 @@ func TestCommandSide_AddInstanceGitHubIDP(t *testing.T) { provider: GitHubProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-Jdsgf", "")) + }, }, }, { @@ -655,7 +687,9 @@ func TestCommandSide_AddInstanceGitHubIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-dsgz3", "")) + }, }, }, { @@ -804,7 +838,9 @@ func TestCommandSide_UpdateInstanceGitHubIDP(t *testing.T) { provider: GitHubProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-sdf4h", "")) + }, }, }, { @@ -818,7 +854,9 @@ func TestCommandSide_UpdateInstanceGitHubIDP(t *testing.T) { provider: GitHubProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-fdh5z", "")) + }, }, }, { @@ -999,7 +1037,9 @@ func TestCommandSide_AddInstanceGitHubEnterpriseIDP(t *testing.T) { provider: GitHubEnterpriseProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-Dg4td", "")) + }, }, }, { @@ -1015,7 +1055,9 @@ func TestCommandSide_AddInstanceGitHubEnterpriseIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-dgj53", "")) + }, }, }, { @@ -1032,7 +1074,9 @@ func TestCommandSide_AddInstanceGitHubEnterpriseIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-Ghjjs", "")) + }, }, }, { @@ -1050,7 +1094,9 @@ func TestCommandSide_AddInstanceGitHubEnterpriseIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-sani2", "")) + }, }, }, { @@ -1069,7 +1115,9 @@ func TestCommandSide_AddInstanceGitHubEnterpriseIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-agj42", "")) + }, }, }, { @@ -1089,7 +1137,9 @@ func TestCommandSide_AddInstanceGitHubEnterpriseIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-sd5hn", "")) + }, }, }, { @@ -1251,7 +1301,9 @@ func TestCommandSide_UpdateInstanceGitHubEnterpriseIDP(t *testing.T) { provider: GitHubEnterpriseProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-sdfh3", "")) + }, }, }, { @@ -1265,7 +1317,9 @@ func TestCommandSide_UpdateInstanceGitHubEnterpriseIDP(t *testing.T) { provider: GitHubEnterpriseProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-shj42", "")) + }, }, }, { @@ -1281,7 +1335,9 @@ func TestCommandSide_UpdateInstanceGitHubEnterpriseIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-sdh73", "")) + }, }, }, { @@ -1293,11 +1349,14 @@ func TestCommandSide_UpdateInstanceGitHubEnterpriseIDP(t *testing.T) { ctx: authz.WithInstanceID(context.Background(), "instance1"), id: "id1", provider: GitHubEnterpriseProvider{ - Name: "name", + Name: "name", + ClientID: "clientID", }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-acx2w", "")) + }, }, }, { @@ -1315,7 +1374,9 @@ func TestCommandSide_UpdateInstanceGitHubEnterpriseIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-dgj6q", "")) + }, }, }, { @@ -1334,7 +1395,9 @@ func TestCommandSide_UpdateInstanceGitHubEnterpriseIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-ybj62", "")) + }, }, }, { @@ -1503,6 +1566,828 @@ func TestCommandSide_UpdateInstanceGitHubEnterpriseIDP(t *testing.T) { } } +func TestCommandSide_AddInstanceGitLabIDP(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + idGenerator id.Generator + secretCrypto crypto.EncryptionAlgorithm + } + type args struct { + ctx context.Context + provider GitLabProvider + } + type res struct { + id string + want *domain.ObjectDetails + err func(error) bool + } + tests := []struct { + name string + fields fields + args args + res res + }{ + { + "invalid clientID", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitLabProvider{}, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-adsg2", "")) + }, + }, + }, + { + "invalid clientSecret", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitLabProvider{ + ClientID: "clientID", + }, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-GD1j2", "")) + }, + }, + }, + { + name: "ok", + fields: fields{ + 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{}, + )), + }, + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitLabProvider{ + ClientID: "clientID", + ClientSecret: "clientSecret", + }, + }, + res: res{ + id: "id1", + want: &domain.ObjectDetails{ResourceOwner: "instance1"}, + }, + }, + { + name: "ok all set", + fields: fields{ + 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, + }, + )), + }, + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitLabProvider{ + ClientID: "clientID", + ClientSecret: "clientSecret", + Scopes: []string{"openid"}, + IDPOptions: idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + }, + }, + res: res{ + id: "id1", + want: &domain.ObjectDetails{ResourceOwner: "instance1"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Commands{ + eventstore: tt.fields.eventstore, + idGenerator: tt.fields.idGenerator, + idpConfigEncryption: tt.fields.secretCrypto, + } + id, got, err := c.AddInstanceGitLabProvider(tt.args.ctx, 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.id, id) + assert.Equal(t, tt.res.want, got) + } + }) + } +} + +func TestCommandSide_UpdateInstanceGitLabIDP(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + secretCrypto crypto.EncryptionAlgorithm + } + type args struct { + ctx context.Context + id string + provider GitLabProvider + } + type res struct { + want *domain.ObjectDetails + err func(error) bool + } + tests := []struct { + name string + fields fields + args args + res res + }{ + { + "invalid id", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitLabProvider{}, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-HJK91", "")) + }, + }, + }, + { + "invalid clientID", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GitLabProvider{}, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-D12t6", "")) + }, + }, + }, + { + name: "not found", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter(), + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GitLabProvider{ + ClientID: "clientID", + }, + }, + res: res{ + err: caos_errors.IsNotFound, + }, + }, + { + name: "no changes", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + 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{}, + )), + ), + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GitLabProvider{ + ClientID: "clientID", + }, + }, + res: res{ + want: &domain.ObjectDetails{ResourceOwner: "instance1"}, + }, + }, + { + name: "change ok", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + 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{}, + )), + ), + 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 + }(), + ), + }, + ), + ), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GitLabProvider{ + ClientID: "clientID2", + ClientSecret: "newSecret", + Scopes: []string{"openid", "profile"}, + 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.UpdateInstanceGitLabProvider(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_AddInstanceGitLabSelfHostedIDP(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + idGenerator id.Generator + secretCrypto crypto.EncryptionAlgorithm + } + type args struct { + ctx context.Context + provider GitLabSelfHostedProvider + } + type res struct { + id string + want *domain.ObjectDetails + err func(error) bool + } + tests := []struct { + name string + fields fields + args args + res res + }{ + { + "invalid name", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitLabSelfHostedProvider{}, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-jw4ZT", "")) + }, + }, + }, + { + "invalid issuer", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitLabSelfHostedProvider{ + Name: "name", + }, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-AST4S", "")) + }, + }, + }, + { + "invalid clientID", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitLabSelfHostedProvider{ + Name: "name", + Issuer: "issuer", + }, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-DBZHJ", "")) + }, + }, + }, + { + "invalid clientSecret", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitLabSelfHostedProvider{ + Name: "name", + Issuer: "issuer", + ClientID: "clientID", + }, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-SDGJ4", "")) + }, + }, + }, + { + name: "ok", + fields: fields{ + 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{}, + )), + }, + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitLabSelfHostedProvider{ + Name: "name", + Issuer: "issuer", + ClientID: "clientID", + ClientSecret: "clientSecret", + }, + }, + res: res{ + id: "id1", + want: &domain.ObjectDetails{ResourceOwner: "instance1"}, + }, + }, + { + name: "ok all set", + fields: fields{ + 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, + }, + )), + }, + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitLabSelfHostedProvider{ + Name: "name", + Issuer: "issuer", + ClientID: "clientID", + ClientSecret: "clientSecret", + Scopes: []string{"openid"}, + IDPOptions: idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + }, + }, + res: res{ + id: "id1", + want: &domain.ObjectDetails{ResourceOwner: "instance1"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Commands{ + eventstore: tt.fields.eventstore, + idGenerator: tt.fields.idGenerator, + idpConfigEncryption: tt.fields.secretCrypto, + } + id, got, err := c.AddInstanceGitLabSelfHostedProvider(tt.args.ctx, 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.id, id) + assert.Equal(t, tt.res.want, got) + } + }) + } +} + +func TestCommandSide_UpdateInstanceGitLabSelfHostedIDP(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + secretCrypto crypto.EncryptionAlgorithm + } + type args struct { + ctx context.Context + id string + provider GitLabSelfHostedProvider + } + type res struct { + want *domain.ObjectDetails + err func(error) bool + } + tests := []struct { + name string + fields fields + args args + res res + }{ + { + "invalid id", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitLabSelfHostedProvider{}, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-SAFG4", "")) + }, + }, + }, + { + "invalid name", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GitLabSelfHostedProvider{}, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-DG4H", "")) + }, + }, + }, + { + "invalid issuer", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GitLabSelfHostedProvider{ + Name: "name", + }, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-SD4eb", "")) + }, + }, + }, + { + "invalid clientID", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GitLabSelfHostedProvider{ + Name: "name", + Issuer: "issuer", + }, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-GHWE3", "")) + }, + }, + }, + { + name: "not found", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter(), + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GitLabSelfHostedProvider{ + Name: "name", + Issuer: "issuer", + ClientID: "clientID", + }, + }, + res: res{ + err: caos_errors.IsNotFound, + }, + }, + { + name: "no changes", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + 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{}, + )), + ), + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GitLabSelfHostedProvider{ + Name: "name", + Issuer: "issuer", + ClientID: "clientID", + }, + }, + res: res{ + want: &domain.ObjectDetails{ResourceOwner: "instance1"}, + }, + }, + { + name: "change ok", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + 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{}, + )), + ), + 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 + }(), + ), + }, + ), + ), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GitLabSelfHostedProvider{ + Issuer: "newIssuer", + Name: "newName", + ClientID: "clientID2", + ClientSecret: "newSecret", + Scopes: []string{"openid", "profile"}, + 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.UpdateInstanceGitLabSelfHostedProvider(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_AddInstanceGoogleIDP(t *testing.T) { type fields struct { eventstore *eventstore.Eventstore @@ -1535,7 +2420,9 @@ func TestCommandSide_AddInstanceGoogleIDP(t *testing.T) { provider: GoogleProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-D3fvs", "")) + }, }, }, { @@ -1551,7 +2438,9 @@ func TestCommandSide_AddInstanceGoogleIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-W2vqs", "")) + }, }, }, { @@ -1699,7 +2588,9 @@ func TestCommandSide_UpdateInstanceGoogleIDP(t *testing.T) { provider: GoogleProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-S32t1", "")) + }, }, }, { @@ -1713,7 +2604,9 @@ func TestCommandSide_UpdateInstanceGoogleIDP(t *testing.T) { provider: GoogleProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-ds432", "")) + }, }, }, { @@ -1892,7 +2785,9 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) { provider: LDAPProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-SAfdd", "")) + }, }, }, { @@ -1908,7 +2803,9 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-SDVg2", "")) + }, }, }, { @@ -1925,7 +2822,9 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-sv31s", "")) + }, }, }, { @@ -1943,7 +2842,9 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-sdgf4", "")) + }, }, }, { @@ -1962,7 +2863,9 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-AEG2w", "")) + }, }, }, { @@ -1982,7 +2885,9 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-SAD5n", "")) + }, }, }, { @@ -2003,7 +2908,9 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-sdf5h", "")) + }, }, }, { @@ -2205,7 +3112,9 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) { provider: LDAPProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-Dgdbs", "")) + }, }, }, { @@ -2219,7 +3128,9 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) { provider: LDAPProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-Sffgd", "")) + }, }, }, { @@ -2235,7 +3146,9 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-Dz62d", "")) + }, }, }, { @@ -2252,7 +3165,9 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-vb3ss", "")) + }, }, }, { @@ -2270,7 +3185,9 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-hbere", "")) + }, }, }, { @@ -2289,7 +3206,9 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-ASFt6", "")) + }, }, }, { @@ -2309,7 +3228,9 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-DG45z", "")) + }, }, }, { diff --git a/internal/command/org_idp.go b/internal/command/org_idp.go index b244d7d33a..ca7ad66d27 100644 --- a/internal/command/org_idp.go +++ b/internal/command/org_idp.go @@ -212,6 +212,86 @@ func (c *Commands) UpdateOrgGitHubEnterpriseProvider(ctx context.Context, resour return pushedEventsToObjectDetails(pushedEvents), nil } +func (c *Commands) AddOrgGitLabProvider(ctx context.Context, resourceOwner string, provider GitLabProvider) (string, *domain.ObjectDetails, error) { + orgAgg := org.NewAggregate(resourceOwner) + id, err := c.idGenerator.Next() + if err != nil { + return "", nil, err + } + writeModel := NewGitLabOrgIDPWriteModel(resourceOwner, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareAddOrgGitLabProvider(orgAgg, writeModel, provider)) + if err != nil { + return "", nil, err + } + pushedEvents, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return "", nil, err + } + return id, pushedEventsToObjectDetails(pushedEvents), nil +} + +func (c *Commands) UpdateOrgGitLabProvider(ctx context.Context, resourceOwner, id string, provider GitLabProvider) (*domain.ObjectDetails, error) { + orgAgg := org.NewAggregate(resourceOwner) + writeModel := NewGitLabOrgIDPWriteModel(resourceOwner, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareUpdateOrgGitLabProvider(orgAgg, writeModel, provider)) + if err != nil { + return nil, err + } + if len(cmds) == 0 { + // no change, so return directly + return &domain.ObjectDetails{ + Sequence: writeModel.ProcessedSequence, + EventDate: writeModel.ChangeDate, + ResourceOwner: writeModel.ResourceOwner, + }, nil + } + pushedEvents, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return nil, err + } + return pushedEventsToObjectDetails(pushedEvents), nil +} + +func (c *Commands) AddOrgGitLabSelfHostedProvider(ctx context.Context, resourceOwner string, provider GitLabSelfHostedProvider) (string, *domain.ObjectDetails, error) { + orgAgg := org.NewAggregate(resourceOwner) + id, err := c.idGenerator.Next() + if err != nil { + return "", nil, err + } + writeModel := NewGitLabSelfHostedOrgIDPWriteModel(resourceOwner, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareAddOrgGitLabSelfHostedProvider(orgAgg, writeModel, provider)) + if err != nil { + return "", nil, err + } + pushedEvents, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return "", nil, err + } + return id, pushedEventsToObjectDetails(pushedEvents), nil +} + +func (c *Commands) UpdateOrgGitLabSelfHostedProvider(ctx context.Context, resourceOwner, id string, provider GitLabSelfHostedProvider) (*domain.ObjectDetails, error) { + orgAgg := org.NewAggregate(resourceOwner) + writeModel := NewGitLabSelfHostedOrgIDPWriteModel(resourceOwner, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareUpdateOrgGitLabSelfHostedProvider(orgAgg, writeModel, provider)) + if err != nil { + return nil, err + } + if len(cmds) == 0 { + // no change, so return directly + return &domain.ObjectDetails{ + Sequence: writeModel.ProcessedSequence, + EventDate: writeModel.ChangeDate, + ResourceOwner: writeModel.ResourceOwner, + }, nil + } + pushedEvents, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return nil, err + } + return pushedEventsToObjectDetails(pushedEvents), nil +} + func (c *Commands) AddOrgGoogleProvider(ctx context.Context, resourceOwner string, provider GoogleProvider) (string, *domain.ObjectDetails, error) { orgAgg := org.NewAggregate(resourceOwner) id, err := c.idGenerator.Next() @@ -808,6 +888,178 @@ func (c *Commands) prepareUpdateOrgGitHubEnterpriseProvider(a *org.Aggregate, wr } } +func (c *Commands) prepareAddOrgGitLabProvider(a *org.Aggregate, writeModel *OrgGitLabIDPWriteModel, provider GitLabProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-adsg2", "Errors.Invalid.Argument") + } + if provider.ClientSecret = strings.TrimSpace(provider.ClientSecret); provider.ClientSecret == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-GD1j2", "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 + } + secret, err := crypto.Encrypt([]byte(provider.ClientSecret), c.idpConfigEncryption) + if err != nil { + return nil, err + } + return []eventstore.Command{ + org.NewGitLabIDPAddedEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.ClientID, + secret, + provider.Scopes, + provider.IDPOptions, + ), + }, nil + }, nil + } +} + +func (c *Commands) prepareUpdateOrgGitLabProvider(a *org.Aggregate, writeModel *OrgGitLabIDPWriteModel, provider GitLabProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if writeModel.ID = strings.TrimSpace(writeModel.ID); writeModel.ID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-HJK91", "Errors.Invalid.Argument") + } + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-D12t6", "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, "ORG-HBReq", "Errors.Org.IDPConfig.NotExisting") + } + event, err := writeModel.NewChangedEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.ClientID, + provider.ClientSecret, + c.idpConfigEncryption, + provider.Scopes, + provider.IDPOptions, + ) + if err != nil { + return nil, err + } + if event == nil { + return nil, nil + } + return []eventstore.Command{event}, nil + }, nil + } +} + +func (c *Commands) prepareAddOrgGitLabSelfHostedProvider(a *org.Aggregate, writeModel *OrgGitLabSelfHostedIDPWriteModel, provider GitLabSelfHostedProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-jw4ZT", "Errors.Invalid.Argument") + } + if provider.Issuer = strings.TrimSpace(provider.Issuer); provider.Issuer == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-AST4S", "Errors.Invalid.Argument") + } + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-DBZHJ", "Errors.Invalid.Argument") + } + if provider.ClientSecret = strings.TrimSpace(provider.ClientSecret); provider.ClientSecret == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-SDGJ4", "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 + } + secret, err := crypto.Encrypt([]byte(provider.ClientSecret), c.idpConfigEncryption) + if err != nil { + return nil, err + } + return []eventstore.Command{ + org.NewGitLabSelfHostedIDPAddedEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.Issuer, + provider.ClientID, + secret, + provider.Scopes, + provider.IDPOptions, + ), + }, nil + }, nil + } +} + +func (c *Commands) prepareUpdateOrgGitLabSelfHostedProvider(a *org.Aggregate, writeModel *OrgGitLabSelfHostedIDPWriteModel, provider GitLabSelfHostedProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if writeModel.ID = strings.TrimSpace(writeModel.ID); writeModel.ID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-SAFG4", "Errors.Invalid.Argument") + } + if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-DG4H", "Errors.Invalid.Argument") + } + if provider.Issuer = strings.TrimSpace(provider.Issuer); provider.Issuer == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-SD4eb", "Errors.Invalid.Argument") + } + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-GHWE3", "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, "ORG-D2tg1", "Errors.Org.IDPConfig.NotExisting") + } + event, err := writeModel.NewChangedEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.Issuer, + provider.ClientID, + provider.ClientSecret, + c.idpConfigEncryption, + provider.Scopes, + provider.IDPOptions, + ) + if err != nil { + return nil, err + } + if event == nil { + return nil, nil + } + return []eventstore.Command{event}, nil + }, nil + } +} + func (c *Commands) prepareAddOrgGoogleProvider(a *org.Aggregate, writeModel *OrgGoogleIDPWriteModel, provider GoogleProvider) preparation.Validation { return func() (preparation.CreateCommands, error) { if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { diff --git a/internal/command/org_idp_model.go b/internal/command/org_idp_model.go index c972656e0d..c8335885b2 100644 --- a/internal/command/org_idp_model.go +++ b/internal/command/org_idp_model.go @@ -25,10 +25,6 @@ func NewOAuthOrgIDPWriteModel(orgID, id string) *OrgOAuthIDPWriteModel { } } -func (wm *OrgOAuthIDPWriteModel) Reduce() error { - return wm.OAuthIDPWriteModel.Reduce() -} - func (wm *OrgOAuthIDPWriteModel) AppendEvents(events ...eventstore.Event) { for _, event := range events { switch e := event.(type) { @@ -109,10 +105,6 @@ func NewOIDCOrgIDPWriteModel(orgID, id string) *OrgOIDCIDPWriteModel { } } -func (wm *OrgOIDCIDPWriteModel) Reduce() error { - return wm.OIDCIDPWriteModel.Reduce() -} - func (wm *OrgOIDCIDPWriteModel) AppendEvents(events ...eventstore.Event) { for _, event := range events { switch e := event.(type) { @@ -210,10 +202,6 @@ func NewJWTOrgIDPWriteModel(orgID, id string) *OrgJWTIDPWriteModel { } } -func (wm *OrgJWTIDPWriteModel) Reduce() error { - return wm.JWTIDPWriteModel.Reduce() -} - func (wm *OrgJWTIDPWriteModel) AppendEvents(events ...eventstore.Event) { for _, event := range events { switch e := event.(type) { @@ -309,10 +297,6 @@ func NewGitHubOrgIDPWriteModel(orgID, id string) *OrgGitHubIDPWriteModel { } } -func (wm *OrgGitHubIDPWriteModel) Reduce() error { - return wm.GitHubIDPWriteModel.Reduce() -} - func (wm *OrgGitHubIDPWriteModel) AppendEvents(events ...eventstore.Event) { for _, event := range events { switch e := event.(type) { @@ -379,10 +363,6 @@ func NewGitHubEnterpriseOrgIDPWriteModel(orgID, id string) *OrgGitHubEnterpriseI } } -func (wm *OrgGitHubEnterpriseIDPWriteModel) Reduce() error { - return wm.GitHubEnterpriseIDPWriteModel.Reduce() -} - func (wm *OrgGitHubEnterpriseIDPWriteModel) AppendEvents(events ...eventstore.Event) { for _, event := range events { switch e := event.(type) { @@ -446,6 +426,137 @@ func (wm *OrgGitHubEnterpriseIDPWriteModel) NewChangedEvent( return org.NewGitHubEnterpriseIDPChangedEvent(ctx, aggregate, id, changes) } +type OrgGitLabIDPWriteModel struct { + GitLabIDPWriteModel +} + +func NewGitLabOrgIDPWriteModel(orgID, id string) *OrgGitLabIDPWriteModel { + return &OrgGitLabIDPWriteModel{ + GitLabIDPWriteModel{ + WriteModel: eventstore.WriteModel{ + AggregateID: orgID, + ResourceOwner: orgID, + }, + ID: id, + }, + } +} + +func (wm *OrgGitLabIDPWriteModel) AppendEvents(events ...eventstore.Event) { + for _, event := range events { + switch e := event.(type) { + case *org.GitLabIDPAddedEvent: + wm.GitLabIDPWriteModel.AppendEvents(&e.GitLabIDPAddedEvent) + case *org.GitLabIDPChangedEvent: + wm.GitLabIDPWriteModel.AppendEvents(&e.GitLabIDPChangedEvent) + case *org.IDPRemovedEvent: + wm.GitLabIDPWriteModel.AppendEvents(&e.RemovedEvent) + default: + wm.GitLabIDPWriteModel.AppendEvents(e) + } + } +} + +func (wm *OrgGitLabIDPWriteModel) Query() *eventstore.SearchQueryBuilder { + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + ResourceOwner(wm.ResourceOwner). + AddQuery(). + AggregateTypes(org.AggregateType). + AggregateIDs(wm.AggregateID). + EventTypes( + org.GitLabIDPAddedEventType, + org.GitLabIDPChangedEventType, + org.IDPRemovedEventType, + ). + EventData(map[string]interface{}{"id": wm.ID}). + Builder() +} + +func (wm *OrgGitLabIDPWriteModel) NewChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + clientID, + clientSecretString string, + secretCrypto crypto.Crypto, + scopes []string, + options idp.Options, +) (*org.GitLabIDPChangedEvent, error) { + + changes, err := wm.GitLabIDPWriteModel.NewChanges(name, clientID, clientSecretString, secretCrypto, scopes, options) + if err != nil || len(changes) == 0 { + return nil, err + } + return org.NewGitLabIDPChangedEvent(ctx, aggregate, id, changes) +} + +type OrgGitLabSelfHostedIDPWriteModel struct { + GitLabSelfHostedIDPWriteModel +} + +func NewGitLabSelfHostedOrgIDPWriteModel(orgID, id string) *OrgGitLabSelfHostedIDPWriteModel { + return &OrgGitLabSelfHostedIDPWriteModel{ + GitLabSelfHostedIDPWriteModel{ + WriteModel: eventstore.WriteModel{ + AggregateID: orgID, + ResourceOwner: orgID, + }, + ID: id, + }, + } +} + +func (wm *OrgGitLabSelfHostedIDPWriteModel) AppendEvents(events ...eventstore.Event) { + for _, event := range events { + switch e := event.(type) { + case *org.GitLabSelfHostedIDPAddedEvent: + wm.GitLabSelfHostedIDPWriteModel.AppendEvents(&e.GitLabSelfHostedIDPAddedEvent) + case *org.GitLabSelfHostedIDPChangedEvent: + wm.GitLabSelfHostedIDPWriteModel.AppendEvents(&e.GitLabSelfHostedIDPChangedEvent) + case *org.IDPRemovedEvent: + wm.GitLabSelfHostedIDPWriteModel.AppendEvents(&e.RemovedEvent) + default: + wm.GitLabSelfHostedIDPWriteModel.AppendEvents(e) + } + } +} + +func (wm *OrgGitLabSelfHostedIDPWriteModel) Query() *eventstore.SearchQueryBuilder { + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + ResourceOwner(wm.ResourceOwner). + AddQuery(). + AggregateTypes(org.AggregateType). + AggregateIDs(wm.AggregateID). + EventTypes( + org.GitLabSelfHostedIDPAddedEventType, + org.GitLabSelfHostedIDPChangedEventType, + org.IDPRemovedEventType, + ). + EventData(map[string]interface{}{"id": wm.ID}). + Builder() +} + +func (wm *OrgGitLabSelfHostedIDPWriteModel) NewChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + issuer, + clientID string, + clientSecretString string, + secretCrypto crypto.Crypto, + scopes []string, + options idp.Options, +) (*org.GitLabSelfHostedIDPChangedEvent, error) { + + changes, err := wm.GitLabSelfHostedIDPWriteModel.NewChanges(name, issuer, clientID, clientSecretString, secretCrypto, scopes, options) + if err != nil || len(changes) == 0 { + return nil, err + } + return org.NewGitLabSelfHostedIDPChangedEvent(ctx, aggregate, id, changes) +} + type OrgGoogleIDPWriteModel struct { GoogleIDPWriteModel } @@ -462,10 +573,6 @@ func NewGoogleOrgIDPWriteModel(orgID, id string) *OrgGoogleIDPWriteModel { } } -func (wm *OrgGoogleIDPWriteModel) Reduce() error { - return wm.GoogleIDPWriteModel.Reduce() -} - func (wm *OrgGoogleIDPWriteModel) AppendEvents(events ...eventstore.Event) { for _, event := range events { switch e := event.(type) { @@ -531,10 +638,6 @@ func NewLDAPOrgIDPWriteModel(orgID, id string) *OrgLDAPIDPWriteModel { } } -func (wm *OrgLDAPIDPWriteModel) Reduce() error { - return wm.LDAPIDPWriteModel.Reduce() -} - func (wm *OrgLDAPIDPWriteModel) AppendEvents(events ...eventstore.Event) { for _, event := range events { switch e := event.(type) { @@ -620,10 +723,6 @@ func NewOrgIDPRemoveWriteModel(orgID, id string) *OrgIDPRemoveWriteModel { } } -func (wm *OrgIDPRemoveWriteModel) Reduce() error { - return wm.IDPRemoveWriteModel.Reduce() -} - func (wm *OrgIDPRemoveWriteModel) AppendEvents(events ...eventstore.Event) { for _, event := range events { switch e := event.(type) { @@ -637,6 +736,10 @@ func (wm *OrgIDPRemoveWriteModel) AppendEvents(events ...eventstore.Event) { wm.IDPRemoveWriteModel.AppendEvents(&e.GitHubIDPAddedEvent) case *org.GitHubEnterpriseIDPAddedEvent: wm.IDPRemoveWriteModel.AppendEvents(&e.GitHubEnterpriseIDPAddedEvent) + case *org.GitLabIDPAddedEvent: + wm.IDPRemoveWriteModel.AppendEvents(&e.GitLabIDPAddedEvent) + case *org.GitLabSelfHostedIDPAddedEvent: + wm.IDPRemoveWriteModel.AppendEvents(&e.GitLabSelfHostedIDPAddedEvent) case *org.GoogleIDPAddedEvent: wm.IDPRemoveWriteModel.AppendEvents(&e.GoogleIDPAddedEvent) case *org.LDAPIDPAddedEvent: @@ -665,6 +768,8 @@ func (wm *OrgIDPRemoveWriteModel) Query() *eventstore.SearchQueryBuilder { org.JWTIDPAddedEventType, org.GitHubIDPAddedEventType, org.GitHubEnterpriseIDPAddedEventType, + org.GitLabIDPAddedEventType, + org.GitLabSelfHostedIDPAddedEventType, org.GoogleIDPAddedEventType, org.LDAPIDPAddedEventType, org.IDPRemovedEventType, diff --git a/internal/command/org_idp_test.go b/internal/command/org_idp_test.go index 9337fc61e6..b15f8e515c 100644 --- a/internal/command/org_idp_test.go +++ b/internal/command/org_idp_test.go @@ -2,6 +2,7 @@ package command import ( "context" + "errors" "testing" "github.com/golang/mock/gomock" @@ -11,6 +12,7 @@ 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" @@ -52,7 +54,9 @@ func TestCommandSide_AddOrgGenericOAuthIDP(t *testing.T) { provider: GenericOAuthProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-D32ef", "")) + }, }, }, { @@ -69,7 +73,9 @@ func TestCommandSide_AddOrgGenericOAuthIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-Dbgzf", "")) + }, }, }, { @@ -87,7 +93,9 @@ func TestCommandSide_AddOrgGenericOAuthIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-DF4ga", "")) + }, }, }, { @@ -106,7 +114,9 @@ func TestCommandSide_AddOrgGenericOAuthIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-B23bs", "")) + }, }, }, { @@ -126,7 +136,9 @@ func TestCommandSide_AddOrgGenericOAuthIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-D2gj8", "")) + }, }, }, { @@ -147,7 +159,9 @@ func TestCommandSide_AddOrgGenericOAuthIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-Fb8jk", "")) + }, }, }, { @@ -169,7 +183,9 @@ func TestCommandSide_AddOrgGenericOAuthIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-sadf3d", "")) + }, }, }, { @@ -333,7 +349,9 @@ func TestCommandSide_UpdateOrgGenericOAuthIDP(t *testing.T) { provider: GenericOAuthProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-asfsa", "")) + }, }, }, { @@ -348,7 +366,9 @@ func TestCommandSide_UpdateOrgGenericOAuthIDP(t *testing.T) { provider: GenericOAuthProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-D32ef", "")) + }, }, }, { @@ -365,7 +385,9 @@ func TestCommandSide_UpdateOrgGenericOAuthIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-Dbgzf", "")) + }, }, }, { @@ -378,11 +400,14 @@ func TestCommandSide_UpdateOrgGenericOAuthIDP(t *testing.T) { resourceOwner: "org1", id: "id1", provider: GenericOAuthProvider{ - Name: "name", + Name: "name", + ClientID: "clientID", }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-B23bs", "")) + }, }, }, { @@ -401,7 +426,9 @@ func TestCommandSide_UpdateOrgGenericOAuthIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-D2gj8", "")) + }, }, }, { @@ -421,7 +448,9 @@ func TestCommandSide_UpdateOrgGenericOAuthIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-Fb8jk", "")) + }, }, }, { @@ -442,7 +471,9 @@ func TestCommandSide_UpdateOrgGenericOAuthIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-SAe4gh", "")) + }, }, }, { @@ -651,7 +682,9 @@ func TestCommandSide_AddOrgGitHubIDP(t *testing.T) { provider: GitHubProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-Jdsgf", "")) + }, }, }, { @@ -668,7 +701,9 @@ func TestCommandSide_AddOrgGitHubIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-dsgz3", "")) + }, }, }, { @@ -815,7 +850,9 @@ func TestCommandSide_UpdateOrgGitHubIDP(t *testing.T) { provider: GitHubProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-sdf4h", "")) + }, }, }, { @@ -830,7 +867,9 @@ func TestCommandSide_UpdateOrgGitHubIDP(t *testing.T) { provider: GitHubProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-fdh5z", "")) + }, }, }, { @@ -1013,7 +1052,9 @@ func TestCommandSide_AddOrgGitHubEnterpriseIDP(t *testing.T) { provider: GitHubEnterpriseProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-Dg4td", "")) + }, }, }, { @@ -1030,7 +1071,9 @@ func TestCommandSide_AddOrgGitHubEnterpriseIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-dgj53", "")) + }, }, }, { @@ -1048,7 +1091,9 @@ func TestCommandSide_AddOrgGitHubEnterpriseIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-Ghjjs", "")) + }, }, }, { @@ -1067,7 +1112,9 @@ func TestCommandSide_AddOrgGitHubEnterpriseIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-sani2", "")) + }, }, }, { @@ -1087,7 +1134,9 @@ func TestCommandSide_AddOrgGitHubEnterpriseIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-agj42", "")) + }, }, }, { @@ -1108,7 +1157,9 @@ func TestCommandSide_AddOrgGitHubEnterpriseIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-sd5hn", "")) + }, }, }, { @@ -1268,7 +1319,9 @@ func TestCommandSide_UpdateOrgGitHubEnterpriseIDP(t *testing.T) { provider: GitHubEnterpriseProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-sdfh3", "")) + }, }, }, { @@ -1283,7 +1336,9 @@ func TestCommandSide_UpdateOrgGitHubEnterpriseIDP(t *testing.T) { provider: GitHubEnterpriseProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-shj42", "")) + }, }, }, { @@ -1300,7 +1355,9 @@ func TestCommandSide_UpdateOrgGitHubEnterpriseIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-sdh73", "")) + }, }, }, { @@ -1313,11 +1370,14 @@ func TestCommandSide_UpdateOrgGitHubEnterpriseIDP(t *testing.T) { resourceOwner: "org1", id: "id1", provider: GitHubEnterpriseProvider{ - Name: "name", + Name: "name", + ClientID: "clientID", }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-acx2w", "")) + }, }, }, { @@ -1336,7 +1396,9 @@ func TestCommandSide_UpdateOrgGitHubEnterpriseIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-dgj6q", "")) + }, }, }, { @@ -1356,7 +1418,9 @@ func TestCommandSide_UpdateOrgGitHubEnterpriseIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-ybj62", "")) + }, }, }, { @@ -1525,6 +1589,848 @@ func TestCommandSide_UpdateOrgGitHubEnterpriseIDP(t *testing.T) { } } +func TestCommandSide_AddOrgGitLabIDP(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + idGenerator id.Generator + secretCrypto crypto.EncryptionAlgorithm + } + type args struct { + ctx context.Context + resourceOwner string + provider GitLabProvider + } + type res struct { + id string + want *domain.ObjectDetails + err func(error) bool + } + tests := []struct { + name string + fields fields + args args + res res + }{ + { + "invalid clientID", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitLabProvider{}, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-adsg2", "")) + }, + }, + }, + { + "invalid clientSecret", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitLabProvider{ + ClientID: "clientID", + }, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-GD1j2", "")) + }, + }, + }, + { + name: "ok", + fields: fields{ + 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{}, + )), + }, + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitLabProvider{ + ClientID: "clientID", + ClientSecret: "clientSecret", + }, + }, + res: res{ + id: "id1", + want: &domain.ObjectDetails{ResourceOwner: "org1"}, + }, + }, + { + name: "ok all set", + fields: fields{ + 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, + }, + )), + }, + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitLabProvider{ + ClientID: "clientID", + ClientSecret: "clientSecret", + Scopes: []string{"openid"}, + IDPOptions: idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + }, + }, + res: res{ + id: "id1", + want: &domain.ObjectDetails{ResourceOwner: "org1"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Commands{ + eventstore: tt.fields.eventstore, + idGenerator: tt.fields.idGenerator, + idpConfigEncryption: tt.fields.secretCrypto, + } + id, got, err := c.AddOrgGitLabProvider(tt.args.ctx, tt.args.resourceOwner, 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.id, id) + assert.Equal(t, tt.res.want, got) + } + }) + } +} + +func TestCommandSide_UpdateOrgGitLabIDP(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + secretCrypto crypto.EncryptionAlgorithm + } + type args struct { + ctx context.Context + resourceOwner string + id string + provider GitLabProvider + } + type res struct { + want *domain.ObjectDetails + err func(error) bool + } + tests := []struct { + name string + fields fields + args args + res res + }{ + { + "invalid id", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitLabProvider{}, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-HJK91", "")) + }, + }, + }, + { + "invalid clientID", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GitLabProvider{}, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-D12t6", "")) + }, + }, + }, + { + name: "not found", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter(), + ), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GitLabProvider{ + ClientID: "clientID", + }, + }, + res: res{ + err: caos_errors.IsNotFound, + }, + }, + { + name: "no changes", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + 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{}, + )), + ), + ), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GitLabProvider{ + ClientID: "clientID", + }, + }, + res: res{ + want: &domain.ObjectDetails{ResourceOwner: "org1"}, + }, + }, + { + name: "change ok", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + 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{}, + )), + ), + 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 + }(), + ), + }, + ), + ), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GitLabProvider{ + ClientID: "clientID2", + ClientSecret: "newSecret", + Scopes: []string{"openid", "profile"}, + 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.UpdateOrgGitLabProvider(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_AddOrgGitLabSelfHostedIDP(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + idGenerator id.Generator + secretCrypto crypto.EncryptionAlgorithm + } + type args struct { + ctx context.Context + resourceOwner string + provider GitLabSelfHostedProvider + } + type res struct { + id string + want *domain.ObjectDetails + err func(error) bool + } + tests := []struct { + name string + fields fields + args args + res res + }{ + { + "invalid name", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitLabSelfHostedProvider{}, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-jw4ZT", "")) + }, + }, + }, + { + "invalid issuer", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitLabSelfHostedProvider{ + Name: "name", + }, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-AST4S", "")) + }, + }, + }, + { + "invalid clientID", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitLabSelfHostedProvider{ + Name: "name", + Issuer: "issuer", + }, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-DBZHJ", "")) + }, + }, + }, + { + "invalid clientSecret", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitLabSelfHostedProvider{ + Name: "name", + Issuer: "issuer", + ClientID: "clientID", + }, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-SDGJ4", "")) + }, + }, + }, + { + name: "ok", + fields: fields{ + 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{}, + )), + }, + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitLabSelfHostedProvider{ + Name: "name", + Issuer: "issuer", + ClientID: "clientID", + ClientSecret: "clientSecret", + }, + }, + res: res{ + id: "id1", + want: &domain.ObjectDetails{ResourceOwner: "org1"}, + }, + }, + { + name: "ok all set", + fields: fields{ + 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, + }, + )), + }, + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitLabSelfHostedProvider{ + Name: "name", + Issuer: "issuer", + ClientID: "clientID", + ClientSecret: "clientSecret", + Scopes: []string{"openid"}, + IDPOptions: idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + }, + }, + res: res{ + id: "id1", + want: &domain.ObjectDetails{ResourceOwner: "org1"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Commands{ + eventstore: tt.fields.eventstore, + idGenerator: tt.fields.idGenerator, + idpConfigEncryption: tt.fields.secretCrypto, + } + id, got, err := c.AddOrgGitLabSelfHostedProvider(tt.args.ctx, tt.args.resourceOwner, 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.id, id) + assert.Equal(t, tt.res.want, got) + } + }) + } +} + +func TestCommandSide_UpdateOrgGitLabSelfHostedIDP(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + secretCrypto crypto.EncryptionAlgorithm + } + type args struct { + ctx context.Context + resourceOwner string + id string + provider GitLabSelfHostedProvider + } + type res struct { + want *domain.ObjectDetails + err func(error) bool + } + tests := []struct { + name string + fields fields + args args + res res + }{ + { + "invalid id", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitLabSelfHostedProvider{}, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-SAFG4", "")) + }, + }, + }, + { + "invalid name", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GitLabSelfHostedProvider{}, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-DG4H", "")) + }, + }, + }, + { + "invalid issuer", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GitLabSelfHostedProvider{ + Name: "name", + }, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-SD4eb", "")) + }, + }, + }, + { + "invalid clientID", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GitLabSelfHostedProvider{ + Name: "name", + Issuer: "issuer", + }, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-GHWE3", "")) + }, + }, + }, + { + name: "not found", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter(), + ), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GitLabSelfHostedProvider{ + Name: "name", + Issuer: "issuer", + ClientID: "clientID", + }, + }, + res: res{ + err: caos_errors.IsNotFound, + }, + }, + { + name: "no changes", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + 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{}, + )), + ), + ), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GitLabSelfHostedProvider{ + Name: "name", + Issuer: "issuer", + ClientID: "clientID", + }, + }, + res: res{ + want: &domain.ObjectDetails{ResourceOwner: "org1"}, + }, + }, + { + name: "change ok", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + 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{}, + )), + ), + 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 + }(), + ), + }, + ), + ), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GitLabSelfHostedProvider{ + Issuer: "newIssuer", + Name: "newName", + ClientID: "clientID2", + ClientSecret: "newSecret", + Scopes: []string{"openid", "profile"}, + 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.UpdateOrgGitLabSelfHostedProvider(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_AddOrgGoogleIDP(t *testing.T) { type fields struct { eventstore *eventstore.Eventstore @@ -1559,7 +2465,9 @@ func TestCommandSide_AddOrgGoogleIDP(t *testing.T) { provider: GoogleProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-D3fvs", "")) + }, }, }, { @@ -1576,7 +2484,9 @@ func TestCommandSide_AddOrgGoogleIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-W2vqs", "")) + }, }, }, { @@ -1722,7 +2632,9 @@ func TestCommandSide_UpdateOrgGoogleIDP(t *testing.T) { provider: GoogleProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-S32t1", "")) + }, }, }, { @@ -1737,7 +2649,9 @@ func TestCommandSide_UpdateOrgGoogleIDP(t *testing.T) { provider: GoogleProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-ds432", "")) + }, }, }, { @@ -1918,7 +2832,9 @@ func TestCommandSide_AddOrgLDAPIDP(t *testing.T) { provider: LDAPProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-SAfdd", "")) + }, }, }, { @@ -1935,7 +2851,9 @@ func TestCommandSide_AddOrgLDAPIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-SDVg2", "")) + }, }, }, { @@ -1953,7 +2871,9 @@ func TestCommandSide_AddOrgLDAPIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-sv31s", "")) + }, }, }, { @@ -1972,7 +2892,9 @@ func TestCommandSide_AddOrgLDAPIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-sdgf4", "")) + }, }, }, { @@ -1992,7 +2914,9 @@ func TestCommandSide_AddOrgLDAPIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-AEG2w", "")) + }, }, }, { @@ -2013,7 +2937,9 @@ func TestCommandSide_AddOrgLDAPIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-SAD5n", "")) + }, }, }, { @@ -2035,7 +2961,9 @@ func TestCommandSide_AddOrgLDAPIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-sdf5h", "")) + }, }, }, { @@ -2235,7 +3163,9 @@ func TestCommandSide_UpdateOrgLDAPIDP(t *testing.T) { provider: LDAPProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-Dgdbs", "")) + }, }, }, { @@ -2250,7 +3180,9 @@ func TestCommandSide_UpdateOrgLDAPIDP(t *testing.T) { provider: LDAPProvider{}, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-Sffgd", "")) + }, }, }, { @@ -2267,7 +3199,9 @@ func TestCommandSide_UpdateOrgLDAPIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-Dz62d", "")) + }, }, }, { @@ -2285,7 +3219,9 @@ func TestCommandSide_UpdateOrgLDAPIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-vb3ss", "")) + }, }, }, { @@ -2304,7 +3240,9 @@ func TestCommandSide_UpdateOrgLDAPIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-hbere", "")) + }, }, }, { @@ -2324,7 +3262,9 @@ func TestCommandSide_UpdateOrgLDAPIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-ASFt6", "")) + }, }, }, { @@ -2345,7 +3285,9 @@ func TestCommandSide_UpdateOrgLDAPIDP(t *testing.T) { }, }, res{ - err: caos_errors.IsErrorInvalidArgument, + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-DG45z", "")) + }, }, }, { diff --git a/internal/domain/idp.go b/internal/domain/idp.go index 3c1d33e411..5e4959d182 100644 --- a/internal/domain/idp.go +++ b/internal/domain/idp.go @@ -36,12 +36,22 @@ const ( ) func (t IDPType) GetCSSClass() string { - switch t { //nolint:exhaustive + switch t { case IDPTypeGoogle: return "google" case IDPTypeGitHub, IDPTypeGitHubEnterprise: return "github" + case IDPTypeGitLab, + IDPTypeGitLabSelfHosted: + return "gitlab" + case IDPTypeUnspecified, + IDPTypeOIDC, + IDPTypeJWT, + IDPTypeOAuth, + IDPTypeLDAP, + IDPTypeAzureAD: + fallthrough default: return "" } diff --git a/internal/domain/policy_login.go b/internal/domain/policy_login.go index a0c9d4bce7..b8019974e1 100644 --- a/internal/domain/policy_login.go +++ b/internal/domain/policy_login.go @@ -4,6 +4,8 @@ import ( "net/url" "time" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/eventstore/v1/models" ) @@ -65,6 +67,35 @@ func (p IDPProvider) IsValid() bool { return p.IDPConfigID != "" } +// DisplayName returns the name or a default +// to be used when always a name must be displayed (e.g. login) +func (p IDPProvider) DisplayName() string { + if p.Name != "" { + return p.Name + } + switch p.IDPType { + case IDPTypeGitHub: + return "GitHub" + case IDPTypeGitLab: + return "GitLab" + case IDPTypeGoogle: + return "Google" + case IDPTypeUnspecified, + IDPTypeOIDC, + IDPTypeJWT, + IDPTypeOAuth, + IDPTypeLDAP, + IDPTypeAzureAD, + IDPTypeGitHubEnterprise, + IDPTypeGitLabSelfHosted: + fallthrough + default: + // we should never get here, so log it + logging.Errorf("name of provider (type %d) is empty - id: %s", p.IDPType, p.IDPConfigID) + return "" + } +} + type PasswordlessType int32 const ( diff --git a/internal/idp/providers/gitlab/gitlab.go b/internal/idp/providers/gitlab/gitlab.go index f36aba8f23..1bb02302f3 100644 --- a/internal/idp/providers/gitlab/gitlab.go +++ b/internal/idp/providers/gitlab/gitlab.go @@ -1,6 +1,8 @@ package gitlab import ( + openid "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/zitadel/internal/idp" "github.com/zitadel/zitadel/internal/idp/providers/oidc" ) @@ -25,6 +27,14 @@ func New(clientID, clientSecret, redirectURI string, scopes []string, options .. // NewCustomIssuer creates a GitLab provider using the [oidc.Provider] (OIDC generic provider) // with a custom issuer for self-managed instances func NewCustomIssuer(name, issuer, clientID, clientSecret, redirectURI string, scopes []string, options ...oidc.ProviderOpts) (*Provider, error) { + if len(scopes) == 0 { + // the OIDC provider would set `openid profile email phone` as default scope, + // but since gitlab does not handle unknown scopes correctly (phone) and returns an error, + // we will just set a separate default list + scopes = []string{openid.ScopeOpenID, openid.ScopeProfile, openid.ScopeEmail} + } + // gitlab is currently not able to handle the prompt `select_account`: + // https://gitlab.com/gitlab-org/gitlab/-/issues/377368 rp, err := oidc.New(name, issuer, clientID, clientSecret, redirectURI, scopes, oidc.DefaultMapper, options...) if err != nil { return nil, err diff --git a/internal/idp/providers/gitlab/gitlab_test.go b/internal/idp/providers/gitlab/gitlab_test.go index c6660a3e99..74fd66ccbc 100644 --- a/internal/idp/providers/gitlab/gitlab_test.go +++ b/internal/idp/providers/gitlab/gitlab_test.go @@ -33,7 +33,18 @@ func TestProvider_BeginAuth(t *testing.T) { scopes: []string{"openid"}, }, want: &oidc.Session{ - AuthURL: "https://gitlab.com/oauth/authorize?client_id=clientID&prompt=select_account&redirect_uri=redirectURI&response_type=code&scope=openid&state=testState", + AuthURL: "https://gitlab.com/oauth/authorize?client_id=clientID&redirect_uri=redirectURI&response_type=code&scope=openid&state=testState", + }, + }, + { + name: "successful auth default scopes", + fields: fields{ + clientID: "clientID", + clientSecret: "clientSecret", + redirectURI: "redirectURI", + }, + want: &oidc.Session{ + AuthURL: "https://gitlab.com/oauth/authorize?client_id=clientID&redirect_uri=redirectURI&response_type=code&scope=openid+profile+email&state=testState", }, }, } diff --git a/internal/idp/providers/google/google.go b/internal/idp/providers/google/google.go index 67a958692b..d850a173d2 100644 --- a/internal/idp/providers/google/google.go +++ b/internal/idp/providers/google/google.go @@ -21,7 +21,7 @@ type Provider struct { // New creates a Google provider using the [oidc.Provider] (OIDC generic provider) func New(clientID, clientSecret, redirectURI string, scopes []string, opts ...oidc.ProviderOpts) (*Provider, error) { - rp, err := oidc.New(name, issuer, clientID, clientSecret, redirectURI, scopes, userMapper, opts...) + rp, err := oidc.New(name, issuer, clientID, clientSecret, redirectURI, scopes, userMapper, append(opts, oidc.WithSelectAccount())...) if err != nil { return nil, err } diff --git a/internal/idp/providers/oidc/oidc.go b/internal/idp/providers/oidc/oidc.go index 81d77d5a32..c8f86dd7a3 100644 --- a/internal/idp/providers/oidc/oidc.go +++ b/internal/idp/providers/oidc/oidc.go @@ -21,6 +21,7 @@ type Provider struct { isAutoCreation bool isAutoUpdate bool userInfoMapper func(info oidc.UserInfo) idp.User + authOptions []rp.AuthURLOpt } type ProviderOpts func(provider *Provider) @@ -61,6 +62,13 @@ func WithRelyingPartyOption(option rp.Option) ProviderOpts { } } +// WithSelectAccount adds the select_account prompt to the auth request +func WithSelectAccount() ProviderOpts { + return func(p *Provider) { + p.authOptions = append(p.authOptions, rp.WithPrompt(oidc.PromptSelectAccount)) + } +} + type UserInfoMapper func(info oidc.UserInfo) idp.User var DefaultMapper UserInfoMapper = func(info oidc.UserInfo) idp.User { @@ -105,7 +113,7 @@ func (p *Provider) Name() string { // BeginAuth implements the [idp.Provider] interface. // It will create a [Session] with an OIDC authorization request as AuthURL. func (p *Provider) BeginAuth(ctx context.Context, state string, _ ...any) (idp.Session, error) { - url := rp.AuthURL(state, p.RelyingParty, rp.WithPrompt(oidc.PromptSelectAccount)) + url := rp.AuthURL(state, p.RelyingParty, p.authOptions...) return &Session{AuthURL: url, Provider: p}, nil } diff --git a/internal/idp/providers/oidc/oidc_test.go b/internal/idp/providers/oidc/oidc_test.go index 7875f241b0..71f110a658 100644 --- a/internal/idp/providers/oidc/oidc_test.go +++ b/internal/idp/providers/oidc/oidc_test.go @@ -23,6 +23,7 @@ func TestProvider_BeginAuth(t *testing.T) { scopes []string userMapper func(info oidc.UserInfo) idp.User httpMock func(issuer string) + opts []ProviderOpts } tests := []struct { name string @@ -50,6 +51,7 @@ func TestProvider_BeginAuth(t *testing.T) { UserinfoEndpoint: issuer + "/userinfo", }) }, + opts: []ProviderOpts{WithSelectAccount()}, }, want: &Session{AuthURL: "https://issuer.com/authorize?client_id=clientID&prompt=select_account&redirect_uri=redirectURI&response_type=code&scope=openid&state=testState"}, }, @@ -61,7 +63,7 @@ func TestProvider_BeginAuth(t *testing.T) { a := assert.New(t) r := require.New(t) - provider, err := New(tt.fields.name, tt.fields.issuer, tt.fields.clientID, tt.fields.clientSecret, tt.fields.redirectURI, tt.fields.scopes, tt.fields.userMapper) + provider, err := New(tt.fields.name, tt.fields.issuer, tt.fields.clientID, tt.fields.clientSecret, tt.fields.redirectURI, tt.fields.scopes, tt.fields.userMapper, tt.fields.opts...) r.NoError(err) session, err := provider.BeginAuth(context.Background(), "testState") diff --git a/internal/query/idp_template.go b/internal/query/idp_template.go index e954e1e483..098cf4b749 100644 --- a/internal/query/idp_template.go +++ b/internal/query/idp_template.go @@ -39,6 +39,8 @@ type IDPTemplate struct { *JWTIDPTemplate *GitHubIDPTemplate *GitHubEnterpriseIDPTemplate + *GitLabIDPTemplate + *GitLabSelfHostedIDPTemplate *GoogleIDPTemplate *LDAPIDPTemplate } @@ -92,6 +94,21 @@ type GitHubEnterpriseIDPTemplate struct { Scopes database.StringArray } +type GitLabIDPTemplate struct { + IDPID string + ClientID string + ClientSecret *crypto.CryptoValue + Scopes database.StringArray +} + +type GitLabSelfHostedIDPTemplate struct { + IDPID string + Issuer string + ClientID string + ClientSecret *crypto.CryptoValue + Scopes database.StringArray +} + type GoogleIDPTemplate struct { IDPID string ClientID string @@ -350,6 +367,64 @@ var ( } ) +var ( + gitlabIdpTemplateTable = table{ + name: projection.IDPTemplateGitLabTable, + instanceIDCol: projection.GitLabInstanceIDCol, + } + GitLabIDCol = Column{ + name: projection.GitLabIDCol, + table: gitlabIdpTemplateTable, + } + GitLabInstanceIDCol = Column{ + name: projection.GitLabInstanceIDCol, + table: gitlabIdpTemplateTable, + } + GitLabClientIDCol = Column{ + name: projection.GitLabClientIDCol, + table: gitlabIdpTemplateTable, + } + GitLabClientSecretCol = Column{ + name: projection.GitLabClientSecretCol, + table: gitlabIdpTemplateTable, + } + GitLabScopesCol = Column{ + name: projection.GitLabScopesCol, + table: gitlabIdpTemplateTable, + } +) + +var ( + gitlabSelfHostedIdpTemplateTable = table{ + name: projection.IDPTemplateGitLabSelfHostedTable, + instanceIDCol: projection.GitLabSelfHostedInstanceIDCol, + } + GitLabSelfHostedIDCol = Column{ + name: projection.GitLabSelfHostedIDCol, + table: gitlabSelfHostedIdpTemplateTable, + } + GitLabSelfHostedInstanceIDCol = Column{ + name: projection.GitLabSelfHostedInstanceIDCol, + table: gitlabSelfHostedIdpTemplateTable, + } + GitLabSelfHostedIssuerCol = Column{ + name: projection.GitLabSelfHostedIssuerCol, + table: gitlabSelfHostedIdpTemplateTable, + } + GitLabSelfHostedClientIDCol = Column{ + name: projection.GitLabSelfHostedClientIDCol, + table: gitlabSelfHostedIdpTemplateTable, + } + GitLabSelfHostedClientSecretCol = Column{ + name: projection.GitLabSelfHostedClientSecretCol, + table: gitlabSelfHostedIdpTemplateTable, + } + GitLabSelfHostedScopesCol = Column{ + name: projection.GitLabSelfHostedScopesCol, + table: gitlabSelfHostedIdpTemplateTable, + } +) + var ( googleIdpTemplateTable = table{ name: projection.IDPTemplateGoogleTable, @@ -621,6 +696,17 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se GitHubEnterpriseTokenEndpointCol.identifier(), GitHubEnterpriseUserEndpointCol.identifier(), GitHubEnterpriseScopesCol.identifier(), + // gitlab + GitLabIDCol.identifier(), + GitLabClientIDCol.identifier(), + GitLabClientSecretCol.identifier(), + GitLabScopesCol.identifier(), + // gitlab self hosted + GitLabSelfHostedIDCol.identifier(), + GitLabSelfHostedIssuerCol.identifier(), + GitLabSelfHostedClientIDCol.identifier(), + GitLabSelfHostedClientSecretCol.identifier(), + GitLabSelfHostedScopesCol.identifier(), // google GoogleIDCol.identifier(), GoogleClientIDCol.identifier(), @@ -655,6 +741,8 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se LeftJoin(join(JWTIDCol, IDPTemplateIDCol)). LeftJoin(join(GitHubIDCol, IDPTemplateIDCol)). LeftJoin(join(GitHubEnterpriseIDCol, IDPTemplateIDCol)). + LeftJoin(join(GitLabIDCol, IDPTemplateIDCol)). + LeftJoin(join(GitLabSelfHostedIDCol, IDPTemplateIDCol)). LeftJoin(join(GoogleIDCol, IDPTemplateIDCol)). LeftJoin(join(LDAPIDCol, IDPTemplateIDCol) + db.Timetravel(call.Took(ctx))). PlaceholderFormat(sq.Dollar), @@ -697,6 +785,17 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se githubEnterpriseUserEndpoint := sql.NullString{} githubEnterpriseScopes := database.StringArray{} + gitlabID := sql.NullString{} + gitlabClientID := sql.NullString{} + gitlabClientSecret := new(crypto.CryptoValue) + gitlabScopes := database.StringArray{} + + gitlabSelfHostedID := sql.NullString{} + gitlabSelfHostedIssuer := sql.NullString{} + gitlabSelfHostedClientID := sql.NullString{} + gitlabSelfHostedClientSecret := new(crypto.CryptoValue) + gitlabSelfHostedScopes := database.StringArray{} + googleID := sql.NullString{} googleClientID := sql.NullString{} googleClientSecret := new(crypto.CryptoValue) @@ -773,6 +872,17 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se &githubEnterpriseTokenEndpoint, &githubEnterpriseUserEndpoint, &githubEnterpriseScopes, + // gitlab + &gitlabID, + &gitlabClientID, + &gitlabClientSecret, + &gitlabScopes, + // gitlab self hosted + &gitlabSelfHostedID, + &gitlabSelfHostedIssuer, + &gitlabSelfHostedClientID, + &gitlabSelfHostedClientSecret, + &gitlabSelfHostedScopes, // google &googleID, &googleClientID, @@ -860,6 +970,23 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se Scopes: githubEnterpriseScopes, } } + if gitlabID.Valid { + idpTemplate.GitLabIDPTemplate = &GitLabIDPTemplate{ + IDPID: gitlabID.String, + ClientID: gitlabClientID.String, + ClientSecret: gitlabClientSecret, + Scopes: gitlabScopes, + } + } + if gitlabSelfHostedID.Valid { + idpTemplate.GitLabSelfHostedIDPTemplate = &GitLabSelfHostedIDPTemplate{ + IDPID: gitlabSelfHostedID.String, + Issuer: gitlabSelfHostedIssuer.String, + ClientID: gitlabSelfHostedClientID.String, + ClientSecret: gitlabSelfHostedClientSecret, + Scopes: gitlabSelfHostedScopes, + } + } if googleID.Valid { idpTemplate.GoogleIDPTemplate = &GoogleIDPTemplate{ IDPID: googleID.String, @@ -950,6 +1077,17 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec GitHubEnterpriseTokenEndpointCol.identifier(), GitHubEnterpriseUserEndpointCol.identifier(), GitHubEnterpriseScopesCol.identifier(), + // gitlab + GitLabIDCol.identifier(), + GitLabClientIDCol.identifier(), + GitLabClientSecretCol.identifier(), + GitLabScopesCol.identifier(), + // gitlab self hosted + GitLabSelfHostedIDCol.identifier(), + GitLabSelfHostedIssuerCol.identifier(), + GitLabSelfHostedClientIDCol.identifier(), + GitLabSelfHostedClientSecretCol.identifier(), + GitLabSelfHostedScopesCol.identifier(), // google GoogleIDCol.identifier(), GoogleClientIDCol.identifier(), @@ -985,6 +1123,8 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec LeftJoin(join(JWTIDCol, IDPTemplateIDCol)). LeftJoin(join(GitHubIDCol, IDPTemplateIDCol)). LeftJoin(join(GitHubEnterpriseIDCol, IDPTemplateIDCol)). + LeftJoin(join(GitLabIDCol, IDPTemplateIDCol)). + LeftJoin(join(GitLabSelfHostedIDCol, IDPTemplateIDCol)). LeftJoin(join(GoogleIDCol, IDPTemplateIDCol)). LeftJoin(join(LDAPIDCol, IDPTemplateIDCol) + db.Timetravel(call.Took(ctx))). PlaceholderFormat(sq.Dollar), @@ -1030,6 +1170,17 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec githubEnterpriseUserEndpoint := sql.NullString{} githubEnterpriseScopes := database.StringArray{} + gitlabID := sql.NullString{} + gitlabClientID := sql.NullString{} + gitlabClientSecret := new(crypto.CryptoValue) + gitlabScopes := database.StringArray{} + + gitlabSelfHostedID := sql.NullString{} + gitlabSelfHostedIssuer := sql.NullString{} + gitlabSelfHostedClientID := sql.NullString{} + gitlabSelfHostedClientSecret := new(crypto.CryptoValue) + gitlabSelfHostedScopes := database.StringArray{} + googleID := sql.NullString{} googleClientID := sql.NullString{} googleClientSecret := new(crypto.CryptoValue) @@ -1106,6 +1257,17 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec &githubEnterpriseTokenEndpoint, &githubEnterpriseUserEndpoint, &githubEnterpriseScopes, + // gitlab + &gitlabID, + &gitlabClientID, + &gitlabClientSecret, + &gitlabScopes, + // gitlab self hosted + &gitlabSelfHostedID, + &gitlabSelfHostedIssuer, + &gitlabSelfHostedClientID, + &gitlabSelfHostedClientSecret, + &gitlabSelfHostedScopes, // google &googleID, &googleClientID, @@ -1192,6 +1354,23 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec Scopes: githubEnterpriseScopes, } } + if gitlabID.Valid { + idpTemplate.GitLabIDPTemplate = &GitLabIDPTemplate{ + IDPID: gitlabID.String, + ClientID: gitlabClientID.String, + ClientSecret: gitlabClientSecret, + Scopes: gitlabScopes, + } + } + if gitlabSelfHostedID.Valid { + idpTemplate.GitLabSelfHostedIDPTemplate = &GitLabSelfHostedIDPTemplate{ + IDPID: gitlabSelfHostedID.String, + Issuer: gitlabSelfHostedIssuer.String, + ClientID: gitlabSelfHostedClientID.String, + ClientSecret: gitlabSelfHostedClientSecret, + Scopes: gitlabSelfHostedScopes, + } + } if googleID.Valid { idpTemplate.GoogleIDPTemplate = &GoogleIDPTemplate{ IDPID: googleID.String, diff --git a/internal/query/idp_template_test.go b/internal/query/idp_template_test.go index cf59d153ce..aab57dfdb4 100644 --- a/internal/query/idp_template_test.go +++ b/internal/query/idp_template_test.go @@ -62,6 +62,17 @@ var ( ` projections.idp_templates3_github_enterprise.token_endpoint,` + ` projections.idp_templates3_github_enterprise.user_endpoint,` + ` projections.idp_templates3_github_enterprise.scopes,` + + // gitlab + ` projections.idp_templates3_gitlab.idp_id,` + + ` projections.idp_templates3_gitlab.client_id,` + + ` projections.idp_templates3_gitlab.client_secret,` + + ` projections.idp_templates3_gitlab.scopes,` + + // gitlab self hosted + ` projections.idp_templates3_gitlab_self_hosted.idp_id,` + + ` projections.idp_templates3_gitlab_self_hosted.issuer,` + + ` projections.idp_templates3_gitlab_self_hosted.client_id,` + + ` projections.idp_templates3_gitlab_self_hosted.client_secret,` + + ` projections.idp_templates3_gitlab_self_hosted.scopes,` + // google ` projections.idp_templates3_google.idp_id,` + ` projections.idp_templates3_google.client_id,` + @@ -96,6 +107,8 @@ var ( ` LEFT JOIN projections.idp_templates3_jwt ON projections.idp_templates3.id = projections.idp_templates3_jwt.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_jwt.instance_id` + ` LEFT JOIN projections.idp_templates3_github ON projections.idp_templates3.id = projections.idp_templates3_github.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_github.instance_id` + ` LEFT JOIN projections.idp_templates3_github_enterprise ON projections.idp_templates3.id = projections.idp_templates3_github_enterprise.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_github_enterprise.instance_id` + + ` LEFT JOIN projections.idp_templates3_gitlab ON projections.idp_templates3.id = projections.idp_templates3_gitlab.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_gitlab.instance_id` + + ` LEFT JOIN projections.idp_templates3_gitlab_self_hosted ON projections.idp_templates3.id = projections.idp_templates3_gitlab_self_hosted.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_gitlab_self_hosted.instance_id` + ` LEFT JOIN projections.idp_templates3_google ON projections.idp_templates3.id = projections.idp_templates3_google.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_google.instance_id` + ` LEFT JOIN projections.idp_templates3_ldap ON projections.idp_templates3.id = projections.idp_templates3_ldap.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_ldap.instance_id` + ` AS OF SYSTEM TIME '-1 ms'` @@ -147,6 +160,17 @@ var ( "token_endpoint", "user_endpoint", "scopes", + // gitlab config + "idp_id", + "client_id", + "client_secret", + "scopes", + // gitlab self hosted config + "idp_id", + "issuer", + "client_id", + "client_secret", + "scopes", // google config "idp_id", "client_id", @@ -223,6 +247,17 @@ var ( ` projections.idp_templates3_github_enterprise.token_endpoint,` + ` projections.idp_templates3_github_enterprise.user_endpoint,` + ` projections.idp_templates3_github_enterprise.scopes,` + + // gitlab + ` projections.idp_templates3_gitlab.idp_id,` + + ` projections.idp_templates3_gitlab.client_id,` + + ` projections.idp_templates3_gitlab.client_secret,` + + ` projections.idp_templates3_gitlab.scopes,` + + // gitlab self hosted + ` projections.idp_templates3_gitlab_self_hosted.idp_id,` + + ` projections.idp_templates3_gitlab_self_hosted.issuer,` + + ` projections.idp_templates3_gitlab_self_hosted.client_id,` + + ` projections.idp_templates3_gitlab_self_hosted.client_secret,` + + ` projections.idp_templates3_gitlab_self_hosted.scopes,` + // google ` projections.idp_templates3_google.idp_id,` + ` projections.idp_templates3_google.client_id,` + @@ -258,6 +293,8 @@ var ( ` LEFT JOIN projections.idp_templates3_jwt ON projections.idp_templates3.id = projections.idp_templates3_jwt.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_jwt.instance_id` + ` LEFT JOIN projections.idp_templates3_github ON projections.idp_templates3.id = projections.idp_templates3_github.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_github.instance_id` + ` LEFT JOIN projections.idp_templates3_github_enterprise ON projections.idp_templates3.id = projections.idp_templates3_github_enterprise.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_github_enterprise.instance_id` + + ` LEFT JOIN projections.idp_templates3_gitlab ON projections.idp_templates3.id = projections.idp_templates3_gitlab.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_gitlab.instance_id` + + ` LEFT JOIN projections.idp_templates3_gitlab_self_hosted ON projections.idp_templates3.id = projections.idp_templates3_gitlab_self_hosted.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_gitlab_self_hosted.instance_id` + ` LEFT JOIN projections.idp_templates3_google ON projections.idp_templates3.id = projections.idp_templates3_google.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_google.instance_id` + ` LEFT JOIN projections.idp_templates3_ldap ON projections.idp_templates3.id = projections.idp_templates3_ldap.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_ldap.instance_id` + ` AS OF SYSTEM TIME '-1 ms'` @@ -309,6 +346,17 @@ var ( "token_endpoint", "user_endpoint", "scopes", + // gitlab config + "idp_id", + "client_id", + "client_secret", + "scopes", + // gitlab self hosted config + "idp_id", + "issuer", + "client_id", + "client_secret", + "scopes", // google config "idp_id", "client_id", @@ -425,6 +473,17 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // gitlab + nil, + nil, + nil, + nil, + // gitlab self hosted + nil, + nil, + nil, + nil, + nil, // google nil, nil, @@ -537,6 +596,17 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // gitlab + nil, + nil, + nil, + nil, + // gitlab self hosted + nil, + nil, + nil, + nil, + nil, // google nil, nil, @@ -646,6 +716,17 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // gitlab + nil, + nil, + nil, + nil, + // gitlab self hosted + nil, + nil, + nil, + nil, + nil, // google nil, nil, @@ -755,6 +836,17 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // gitlab + nil, + nil, + nil, + nil, + // gitlab self hosted + nil, + nil, + nil, + nil, + nil, // google nil, nil, @@ -808,6 +900,245 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { }, }, }, + { + name: "prepareIDPTemplateByIDQuery gitlab idp", + prepare: prepareIDPTemplateByIDQuery, + want: want{ + sqlExpectations: mockQuery( + regexp.QuoteMeta(idpTemplateQuery), + idpTemplateCols, + []driver.Value{ + "idp-id", + "ro", + testNow, + testNow, + uint64(20211109), + domain.IDPConfigStateActive, + "idp-name", + domain.IDPTypeGitLab, + domain.IdentityProviderTypeOrg, + true, + true, + true, + true, + // oauth + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + // oidc + nil, + nil, + nil, + nil, + nil, + // jwt + nil, + nil, + nil, + nil, + nil, + // github + nil, + nil, + nil, + nil, + // github enterprise + nil, + nil, + nil, + nil, + nil, + nil, + nil, + // gitlab + "idp-id", + "client_id", + nil, + database.StringArray{"profile"}, + // gitlab self hosted + nil, + nil, + nil, + nil, + nil, + // google + nil, + nil, + nil, + nil, + // ldap config + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + }, + ), + }, + object: &IDPTemplate{ + CreationDate: testNow, + ChangeDate: testNow, + Sequence: 20211109, + ResourceOwner: "ro", + ID: "idp-id", + State: domain.IDPStateActive, + Name: "idp-name", + Type: domain.IDPTypeGitLab, + OwnerType: domain.IdentityProviderTypeOrg, + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + GitLabIDPTemplate: &GitLabIDPTemplate{ + IDPID: "idp-id", + ClientID: "client_id", + ClientSecret: nil, + Scopes: []string{"profile"}, + }, + }, + }, + { + name: "prepareIDPTemplateByIDQuery gitlab self hosted idp", + prepare: prepareIDPTemplateByIDQuery, + want: want{ + sqlExpectations: mockQuery( + regexp.QuoteMeta(idpTemplateQuery), + idpTemplateCols, + []driver.Value{ + "idp-id", + "ro", + testNow, + testNow, + uint64(20211109), + domain.IDPConfigStateActive, + "idp-name", + domain.IDPTypeGitLabSelfHosted, + domain.IdentityProviderTypeOrg, + true, + true, + true, + true, + // oauth + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + // oidc + nil, + nil, + nil, + nil, + nil, + // jwt + nil, + nil, + nil, + nil, + nil, + // github + nil, + nil, + nil, + nil, + // github enterprise + nil, + nil, + nil, + nil, + nil, + nil, + nil, + // gitlab + nil, + nil, + nil, + nil, + // gitlab self hosted + "idp-id", + "issuer", + "client_id", + nil, + database.StringArray{"profile"}, + // google + nil, + nil, + nil, + nil, + // ldap config + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + }, + ), + }, + object: &IDPTemplate{ + CreationDate: testNow, + ChangeDate: testNow, + Sequence: 20211109, + ResourceOwner: "ro", + ID: "idp-id", + State: domain.IDPStateActive, + Name: "idp-name", + Type: domain.IDPTypeGitLabSelfHosted, + OwnerType: domain.IdentityProviderTypeOrg, + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + GitLabSelfHostedIDPTemplate: &GitLabSelfHostedIDPTemplate{ + IDPID: "idp-id", + Issuer: "issuer", + ClientID: "client_id", + ClientSecret: nil, + Scopes: []string{"profile"}, + }, + }, + }, { name: "prepareIDPTemplateByIDQuery google idp", prepare: prepareIDPTemplateByIDQuery, @@ -863,6 +1194,17 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // gitlab + nil, + nil, + nil, + nil, + // gitlab self hosted + nil, + nil, + nil, + nil, + nil, // google "idp-id", "client_id", @@ -971,6 +1313,17 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // gitlab + nil, + nil, + nil, + nil, + // gitlab self hosted + nil, + nil, + nil, + nil, + nil, // google nil, nil, @@ -1098,6 +1451,17 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // gitlab + nil, + nil, + nil, + nil, + // gitlab self hosted + nil, + nil, + nil, + nil, + nil, // google config nil, nil, @@ -1236,6 +1600,17 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // gitlab + nil, + nil, + nil, + nil, + // gitlab self hosted + nil, + nil, + nil, + nil, + nil, // google config nil, nil, @@ -1372,6 +1747,17 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // gitlab + nil, + nil, + nil, + nil, + // gitlab self hosted + nil, + nil, + nil, + nil, + nil, // google config nil, nil, @@ -1483,6 +1869,17 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // gitlab + nil, + nil, + nil, + nil, + // gitlab self hosted + nil, + nil, + nil, + nil, + nil, // google config nil, nil, @@ -1560,6 +1957,17 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // gitlab + nil, + nil, + nil, + nil, + // gitlab self hosted + nil, + nil, + nil, + nil, + nil, // google "idp-id-google", "client_id", @@ -1637,6 +2045,17 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // gitlab + nil, + nil, + nil, + nil, + // gitlab self hosted + nil, + nil, + nil, + nil, + nil, // google nil, nil, @@ -1714,6 +2133,17 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // gitlab + nil, + nil, + nil, + nil, + // gitlab self hosted + nil, + nil, + nil, + nil, + nil, // google nil, nil, @@ -1791,6 +2221,17 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // gitlab + nil, + nil, + nil, + nil, + // gitlab self hosted + nil, + nil, + nil, + nil, + nil, // google nil, nil, diff --git a/internal/query/projection/idp_template.go b/internal/query/projection/idp_template.go index b90ee12076..59217b4352 100644 --- a/internal/query/projection/idp_template.go +++ b/internal/query/projection/idp_template.go @@ -17,22 +17,26 @@ import ( ) const ( - IDPTemplateTable = "projections.idp_templates3" - IDPTemplateOAuthTable = IDPTemplateTable + "_" + IDPTemplateOAuthSuffix - IDPTemplateOIDCTable = IDPTemplateTable + "_" + IDPTemplateOIDCSuffix - IDPTemplateJWTTable = IDPTemplateTable + "_" + IDPTemplateJWTSuffix + IDPTemplateTable = "projections.idp_templates3" + IDPTemplateOAuthTable = IDPTemplateTable + "_" + IDPTemplateOAuthSuffix + IDPTemplateOIDCTable = IDPTemplateTable + "_" + IDPTemplateOIDCSuffix + IDPTemplateJWTTable = IDPTemplateTable + "_" + IDPTemplateJWTSuffix IDPTemplateGitHubTable = IDPTemplateTable + "_" + IDPTemplateGitHubSuffix IDPTemplateGitHubEnterpriseTable = IDPTemplateTable + "_" + IDPTemplateGitHubEnterpriseSuffix - IDPTemplateGoogleTable = IDPTemplateTable + "_" + IDPTemplateGoogleSuffix - IDPTemplateLDAPTable = IDPTemplateTable + "_" + IDPTemplateLDAPSuffix + IDPTemplateGitLabTable = IDPTemplateTable + "_" + IDPTemplateGitLabSuffix + IDPTemplateGitLabSelfHostedTable = IDPTemplateTable + "_" + IDPTemplateGitLabSelfHostedSuffix + IDPTemplateGoogleTable = IDPTemplateTable + "_" + IDPTemplateGoogleSuffix + IDPTemplateLDAPTable = IDPTemplateTable + "_" + IDPTemplateLDAPSuffix - IDPTemplateOAuthSuffix = "oauth2" - IDPTemplateOIDCSuffix = "oidc" - IDPTemplateJWTSuffix = "jwt" + IDPTemplateOAuthSuffix = "oauth2" + IDPTemplateOIDCSuffix = "oidc" + IDPTemplateJWTSuffix = "jwt" IDPTemplateGitHubSuffix = "github" IDPTemplateGitHubEnterpriseSuffix = "github_enterprise" - IDPTemplateGoogleSuffix = "google" - IDPTemplateLDAPSuffix = "ldap" + IDPTemplateGitLabSuffix = "gitlab" + IDPTemplateGitLabSelfHostedSuffix = "gitlab_self_hosted" + IDPTemplateGoogleSuffix = "google" + IDPTemplateLDAPSuffix = "ldap" IDPTemplateIDCol = "id" IDPTemplateCreationDateCol = "creation_date" @@ -89,6 +93,19 @@ const ( GitHubEnterpriseUserEndpointCol = "user_endpoint" GitHubEnterpriseScopesCol = "scopes" + GitLabIDCol = "idp_id" + GitLabInstanceIDCol = "instance_id" + GitLabClientIDCol = "client_id" + GitLabClientSecretCol = "client_secret" + GitLabScopesCol = "scopes" + + GitLabSelfHostedIDCol = "idp_id" + GitLabSelfHostedInstanceIDCol = "instance_id" + GitLabSelfHostedIssuerCol = "issuer" + GitLabSelfHostedClientIDCol = "client_id" + GitLabSelfHostedClientSecretCol = "client_secret" + GitLabSelfHostedScopesCol = "scopes" + GoogleIDCol = "idp_id" GoogleInstanceIDCol = "instance_id" GoogleClientIDCol = "client_id" @@ -214,6 +231,29 @@ func newIDPTemplateProjection(ctx context.Context, config crdb.StatementHandlerC IDPTemplateGitHubEnterpriseSuffix, crdb.WithForeignKey(crdb.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()), + }, + crdb.NewPrimaryKey(GitLabInstanceIDCol, GitLabIDCol), + IDPTemplateGitLabSuffix, + crdb.WithForeignKey(crdb.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()), + }, + crdb.NewPrimaryKey(GitLabSelfHostedInstanceIDCol, GitLabSelfHostedIDCol), + IDPTemplateGitLabSelfHostedSuffix, + crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), + ), crdb.NewSuffixedTable([]*crdb.Column{ crdb.NewColumn(GoogleIDCol, crdb.ColumnTypeText), crdb.NewColumn(GoogleInstanceIDCol, crdb.ColumnTypeText), @@ -328,6 +368,22 @@ func (p *idpTemplateProjection) reducers() []handler.AggregateReducer { Event: instance.GitHubEnterpriseIDPChangedEventType, Reduce: p.reduceGitHubEnterpriseIDPChanged, }, + { + Event: instance.GitLabIDPAddedEventType, + Reduce: p.reduceGitLabIDPAdded, + }, + { + Event: instance.GitLabIDPChangedEventType, + Reduce: p.reduceGitLabIDPChanged, + }, + { + Event: instance.GitLabSelfHostedIDPAddedEventType, + Reduce: p.reduceGitLabSelfHostedIDPAdded, + }, + { + Event: instance.GitLabSelfHostedIDPChangedEventType, + Reduce: p.reduceGitLabSelfHostedIDPChanged, + }, { Event: instance.GoogleIDPAddedEventType, Reduce: p.reduceGoogleIDPAdded, @@ -422,6 +478,22 @@ func (p *idpTemplateProjection) reducers() []handler.AggregateReducer { Event: org.GitHubEnterpriseIDPChangedEventType, Reduce: p.reduceGitHubEnterpriseIDPChanged, }, + { + Event: org.GitLabIDPAddedEventType, + Reduce: p.reduceGitLabIDPAdded, + }, + { + Event: org.GitLabIDPChangedEventType, + Reduce: p.reduceGitLabIDPChanged, + }, + { + Event: org.GitLabSelfHostedIDPAddedEventType, + Reduce: p.reduceGitLabSelfHostedIDPAdded, + }, + { + Event: org.GitLabSelfHostedIDPChangedEventType, + Reduce: p.reduceGitLabSelfHostedIDPChanged, + }, { Event: org.GoogleIDPAddedEventType, Reduce: p.reduceGoogleIDPAdded, @@ -1156,6 +1228,183 @@ func (p *idpTemplateProjection) reduceGitHubEnterpriseIDPChanged(event eventstor ), nil } +func (p *idpTemplateProjection) reduceGitLabIDPAdded(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idp.GitLabIDPAddedEvent + var idpOwnerType domain.IdentityProviderType + switch e := event.(type) { + case *org.GitLabIDPAddedEvent: + idpEvent = e.GitLabIDPAddedEvent + idpOwnerType = domain.IdentityProviderTypeOrg + case *instance.GitLabIDPAddedEvent: + idpEvent = e.GitLabIDPAddedEvent + idpOwnerType = domain.IdentityProviderTypeSystem + default: + return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-x9a022b", "reduce.wrong.event.type %v", []eventstore.EventType{org.GitLabIDPAddedEventType, instance.GitLabIDPAddedEventType}) + } + + return crdb.NewMultiStatement( + &idpEvent, + crdb.AddCreateStatement( + []handler.Column{ + handler.NewCol(IDPTemplateIDCol, idpEvent.ID), + handler.NewCol(IDPTemplateCreationDateCol, idpEvent.CreationDate()), + handler.NewCol(IDPTemplateChangeDateCol, idpEvent.CreationDate()), + handler.NewCol(IDPTemplateSequenceCol, idpEvent.Sequence()), + handler.NewCol(IDPTemplateResourceOwnerCol, idpEvent.Aggregate().ResourceOwner), + handler.NewCol(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCol(IDPTemplateStateCol, domain.IDPStateActive), + handler.NewCol(IDPTemplateNameCol, idpEvent.Name), + handler.NewCol(IDPTemplateOwnerTypeCol, idpOwnerType), + handler.NewCol(IDPTemplateTypeCol, domain.IDPTypeGitLab), + handler.NewCol(IDPTemplateIsCreationAllowedCol, idpEvent.IsCreationAllowed), + handler.NewCol(IDPTemplateIsLinkingAllowedCol, idpEvent.IsLinkingAllowed), + handler.NewCol(IDPTemplateIsAutoCreationCol, idpEvent.IsAutoCreation), + handler.NewCol(IDPTemplateIsAutoUpdateCol, idpEvent.IsAutoUpdate), + }, + ), + crdb.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)), + }, + crdb.WithTableSuffix(IDPTemplateGitLabSuffix), + ), + ), nil +} + +func (p *idpTemplateProjection) reduceGitLabIDPChanged(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idp.GitLabIDPChangedEvent + switch e := event.(type) { + case *org.GitLabIDPChangedEvent: + idpEvent = e.GitLabIDPChangedEvent + case *instance.GitLabIDPChangedEvent: + idpEvent = e.GitLabIDPChangedEvent + default: + 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 = append(ops, + crdb.AddUpdateStatement( + reduceIDPChangedTemplateColumns(idpEvent.Name, idpEvent.CreationDate(), idpEvent.Sequence(), idpEvent.OptionChanges), + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + }, + ), + ) + gitlabCols := reduceGitLabIDPChangedColumns(idpEvent) + if len(gitlabCols) > 0 { + ops = append(ops, + crdb.AddUpdateStatement( + gitlabCols, + []handler.Condition{ + handler.NewCond(GitLabIDCol, idpEvent.ID), + handler.NewCond(GitLabInstanceIDCol, idpEvent.Aggregate().InstanceID), + }, + crdb.WithTableSuffix(IDPTemplateGitLabSuffix), + ), + ) + } + + return crdb.NewMultiStatement( + &idpEvent, + ops..., + ), nil +} + +func (p *idpTemplateProjection) reduceGitLabSelfHostedIDPAdded(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idp.GitLabSelfHostedIDPAddedEvent + var idpOwnerType domain.IdentityProviderType + switch e := event.(type) { + case *org.GitLabSelfHostedIDPAddedEvent: + idpEvent = e.GitLabSelfHostedIDPAddedEvent + idpOwnerType = domain.IdentityProviderTypeOrg + case *instance.GitLabSelfHostedIDPAddedEvent: + idpEvent = e.GitLabSelfHostedIDPAddedEvent + idpOwnerType = domain.IdentityProviderTypeSystem + default: + return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SAF3gw", "reduce.wrong.event.type %v", []eventstore.EventType{org.GitLabSelfHostedIDPAddedEventType, instance.GitLabSelfHostedIDPAddedEventType}) + } + + return crdb.NewMultiStatement( + &idpEvent, + crdb.AddCreateStatement( + []handler.Column{ + handler.NewCol(IDPTemplateIDCol, idpEvent.ID), + handler.NewCol(IDPTemplateCreationDateCol, idpEvent.CreationDate()), + handler.NewCol(IDPTemplateChangeDateCol, idpEvent.CreationDate()), + handler.NewCol(IDPTemplateSequenceCol, idpEvent.Sequence()), + handler.NewCol(IDPTemplateResourceOwnerCol, idpEvent.Aggregate().ResourceOwner), + handler.NewCol(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCol(IDPTemplateStateCol, domain.IDPStateActive), + handler.NewCol(IDPTemplateNameCol, idpEvent.Name), + handler.NewCol(IDPTemplateOwnerTypeCol, idpOwnerType), + handler.NewCol(IDPTemplateTypeCol, domain.IDPTypeGitLabSelfHosted), + handler.NewCol(IDPTemplateIsCreationAllowedCol, idpEvent.IsCreationAllowed), + handler.NewCol(IDPTemplateIsLinkingAllowedCol, idpEvent.IsLinkingAllowed), + handler.NewCol(IDPTemplateIsAutoCreationCol, idpEvent.IsAutoCreation), + handler.NewCol(IDPTemplateIsAutoUpdateCol, idpEvent.IsAutoUpdate), + }, + ), + crdb.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)), + }, + crdb.WithTableSuffix(IDPTemplateGitLabSelfHostedSuffix), + ), + ), nil +} + +func (p *idpTemplateProjection) reduceGitLabSelfHostedIDPChanged(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idp.GitLabSelfHostedIDPChangedEvent + switch e := event.(type) { + case *org.GitLabSelfHostedIDPChangedEvent: + idpEvent = e.GitLabSelfHostedIDPChangedEvent + case *instance.GitLabSelfHostedIDPChangedEvent: + idpEvent = e.GitLabSelfHostedIDPChangedEvent + default: + 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 = append(ops, + crdb.AddUpdateStatement( + reduceIDPChangedTemplateColumns(idpEvent.Name, idpEvent.CreationDate(), idpEvent.Sequence(), idpEvent.OptionChanges), + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + }, + ), + ) + gitlabCols := reduceGitLabSelfHostedIDPChangedColumns(idpEvent) + if len(gitlabCols) > 0 { + ops = append(ops, + crdb.AddUpdateStatement( + gitlabCols, + []handler.Condition{ + handler.NewCond(GitLabSelfHostedIDCol, idpEvent.ID), + handler.NewCond(GitLabSelfHostedInstanceIDCol, idpEvent.Aggregate().InstanceID), + }, + crdb.WithTableSuffix(IDPTemplateGitLabSelfHostedSuffix), + ), + ) + } + + return crdb.NewMultiStatement( + &idpEvent, + ops..., + ), nil +} + func (p *idpTemplateProjection) reduceGoogleIDPAdded(event eventstore.Event) (*handler.Statement, error) { var idpEvent idp.GoogleIDPAddedEvent var idpOwnerType domain.IdentityProviderType @@ -1511,6 +1760,37 @@ func reduceGitHubEnterpriseIDPChangedColumns(idpEvent idp.GitHubEnterpriseIDPCha return oauthCols } +func reduceGitLabIDPChangedColumns(idpEvent idp.GitLabIDPChangedEvent) []handler.Column { + gitlabCols := make([]handler.Column, 0, 3) + if idpEvent.ClientID != nil { + gitlabCols = append(gitlabCols, handler.NewCol(GitLabClientIDCol, *idpEvent.ClientID)) + } + if idpEvent.ClientSecret != nil { + gitlabCols = append(gitlabCols, handler.NewCol(GitLabClientSecretCol, *idpEvent.ClientSecret)) + } + if idpEvent.Scopes != nil { + gitlabCols = append(gitlabCols, handler.NewCol(GitLabScopesCol, database.StringArray(idpEvent.Scopes))) + } + return gitlabCols +} + +func reduceGitLabSelfHostedIDPChangedColumns(idpEvent idp.GitLabSelfHostedIDPChangedEvent) []handler.Column { + gitlabCols := make([]handler.Column, 0, 4) + if idpEvent.Issuer != nil { + gitlabCols = append(gitlabCols, handler.NewCol(GitLabSelfHostedIssuerCol, *idpEvent.Issuer)) + } + if idpEvent.ClientID != nil { + gitlabCols = append(gitlabCols, handler.NewCol(GitLabSelfHostedClientIDCol, *idpEvent.ClientID)) + } + if idpEvent.ClientSecret != nil { + gitlabCols = append(gitlabCols, handler.NewCol(GitLabSelfHostedClientSecretCol, *idpEvent.ClientSecret)) + } + if idpEvent.Scopes != nil { + gitlabCols = append(gitlabCols, handler.NewCol(GitLabSelfHostedScopesCol, database.StringArray(idpEvent.Scopes))) + } + return gitlabCols +} + func reduceGoogleIDPChangedColumns(idpEvent idp.GoogleIDPChangedEvent) []handler.Column { googleCols := make([]handler.Column, 0, 3) if idpEvent.ClientID != nil { diff --git a/internal/query/projection/idp_template_test.go b/internal/query/projection/idp_template_test.go index 70f49932fe..00fc50cb0e 100644 --- a/internal/query/projection/idp_template_test.go +++ b/internal/query/projection/idp_template_test.go @@ -13,6 +13,15 @@ import ( "github.com/zitadel/zitadel/internal/repository/org" ) +var ( + idpTemplateInsertStmt = `INSERT INTO projections.idp_templates3` + + ` (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update)` + + ` VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)` + idpTemplateUpdateMinimalStmt = `UPDATE projections.idp_templates3 SET (is_creation_allowed, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)` + idpTemplateUpdateStmt = `UPDATE projections.idp_templates3 SET (name, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update, change_date, sequence)` + + ` = ($1, $2, $3, $4, $5, $6, $7) WHERE (id = $8) AND (instance_id = $9)` +) + func TestIDPTemplateProjection_reducesRemove(t *testing.T) { type args struct { event func(t *testing.T) eventstore.Event @@ -170,7 +179,7 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_templates3 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedStmt: idpTemplateInsertStmt, expectedArgs: []interface{}{ "idp-id", anyArg{}, @@ -241,7 +250,7 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_templates3 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedStmt: idpTemplateInsertStmt, expectedArgs: []interface{}{ "idp-id", anyArg{}, @@ -298,7 +307,7 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idp_templates3 SET (is_creation_allowed, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: idpTemplateUpdateMinimalStmt, expectedArgs: []interface{}{ true, anyArg{}, @@ -354,7 +363,7 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idp_templates3 SET (name, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update, change_date, sequence) = ($1, $2, $3, $4, $5, $6, $7) WHERE (id = $8) AND (instance_id = $9)", + expectedStmt: idpTemplateUpdateStmt, expectedArgs: []interface{}{ "custom-zitadel-instance", true, @@ -442,7 +451,7 @@ func TestIDPTemplateProjection_reducesGitHub(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_templates3 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedStmt: idpTemplateInsertStmt, expectedArgs: []interface{}{ "idp-id", anyArg{}, @@ -505,7 +514,7 @@ func TestIDPTemplateProjection_reducesGitHub(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_templates3 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedStmt: idpTemplateInsertStmt, expectedArgs: []interface{}{ "idp-id", anyArg{}, @@ -558,7 +567,7 @@ func TestIDPTemplateProjection_reducesGitHub(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idp_templates3 SET (is_creation_allowed, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: idpTemplateUpdateMinimalStmt, expectedArgs: []interface{}{ true, anyArg{}, @@ -610,7 +619,7 @@ func TestIDPTemplateProjection_reducesGitHub(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idp_templates3 SET (name, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update, change_date, sequence) = ($1, $2, $3, $4, $5, $6, $7) WHERE (id = $8) AND (instance_id = $9)", + expectedStmt: idpTemplateUpdateStmt, expectedArgs: []interface{}{ "name", true, @@ -697,7 +706,7 @@ func TestIDPTemplateProjection_reducesGitHubEnterprise(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_templates3 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedStmt: idpTemplateInsertStmt, expectedArgs: []interface{}{ "idp-id", anyArg{}, @@ -766,7 +775,7 @@ func TestIDPTemplateProjection_reducesGitHubEnterprise(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_templates3 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedStmt: idpTemplateInsertStmt, expectedArgs: []interface{}{ "idp-id", anyArg{}, @@ -822,7 +831,7 @@ func TestIDPTemplateProjection_reducesGitHubEnterprise(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idp_templates3 SET (is_creation_allowed, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: idpTemplateUpdateMinimalStmt, expectedArgs: []interface{}{ true, anyArg{}, @@ -877,7 +886,7 @@ func TestIDPTemplateProjection_reducesGitHubEnterprise(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idp_templates3 SET (name, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update, change_date, sequence) = ($1, $2, $3, $4, $5, $6, $7) WHERE (id = $8) AND (instance_id = $9)", + expectedStmt: idpTemplateUpdateStmt, expectedArgs: []interface{}{ "name", true, @@ -923,6 +932,514 @@ func TestIDPTemplateProjection_reducesGitHubEnterprise(t *testing.T) { } } +func TestIDPTemplateProjection_reducesGitLab(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: "instance reduceGitLabIDPAdded", + args: args{ + event: getEvent(testEvent( + repository.EventType(instance.GitLabIDPAddedEventType), + instance.AggregateType, + []byte(`{ + "id": "idp-id", + "client_id": "client_id", + "client_secret": { + "cryptoType": 0, + "algorithm": "RSA-265", + "keyId": "key-id" + }, + "scopes": ["profile"], + "isCreationAllowed": true, + "isLinkingAllowed": true, + "isAutoCreation": true, + "isAutoUpdate": true +}`), + ), instance.GitLabIDPAddedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceGitLabIDPAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: idpTemplateInsertStmt, + expectedArgs: []interface{}{ + "idp-id", + anyArg{}, + anyArg{}, + uint64(15), + "ro-id", + "instance-id", + domain.IDPStateActive, + "", + domain.IdentityProviderTypeSystem, + domain.IDPTypeGitLab, + true, + true, + true, + true, + }, + }, + { + expectedStmt: "INSERT INTO projections.idp_templates3_gitlab (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 reduceGitLabIDPAdded", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.GitLabIDPAddedEventType), + org.AggregateType, + []byte(`{ + "id": "idp-id", + "client_id": "client_id", + "client_secret": { + "cryptoType": 0, + "algorithm": "RSA-265", + "keyId": "key-id" + }, + "scopes": ["profile"], + "isCreationAllowed": true, + "isLinkingAllowed": true, + "isAutoCreation": true, + "isAutoUpdate": true +}`), + ), org.GitLabIDPAddedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceGitLabIDPAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: idpTemplateInsertStmt, + expectedArgs: []interface{}{ + "idp-id", + anyArg{}, + anyArg{}, + uint64(15), + "ro-id", + "instance-id", + domain.IDPStateActive, + "", + domain.IdentityProviderTypeOrg, + domain.IDPTypeGitLab, + true, + true, + true, + true, + }, + }, + { + expectedStmt: "INSERT INTO projections.idp_templates3_gitlab (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: "instance reduceGitLabIDPChanged minimal", + args: args{ + event: getEvent(testEvent( + repository.EventType(instance.GitLabIDPChangedEventType), + instance.AggregateType, + []byte(`{ + "id": "idp-id", + "isCreationAllowed": true, + "client_id": "id" +}`), + ), instance.GitLabIDPChangedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceGitLabIDPChanged, + want: wantReduce{ + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: idpTemplateUpdateMinimalStmt, + expectedArgs: []interface{}{ + true, + anyArg{}, + uint64(15), + "idp-id", + "instance-id", + }, + }, + { + expectedStmt: "UPDATE projections.idp_templates3_gitlab SET client_id = $1 WHERE (idp_id = $2) AND (instance_id = $3)", + expectedArgs: []interface{}{ + "id", + "idp-id", + "instance-id", + }, + }, + }, + }, + }, + }, + { + name: "instance reduceGitLabIDPChanged", + args: args{ + event: getEvent(testEvent( + repository.EventType(instance.GitLabIDPChangedEventType), + instance.AggregateType, + []byte(`{ + "id": "idp-id", + "name": "name", + "client_id": "client_id", + "client_secret": { + "cryptoType": 0, + "algorithm": "RSA-265", + "keyId": "key-id" + }, + "scopes": ["profile"], + "isCreationAllowed": true, + "isLinkingAllowed": true, + "isAutoCreation": true, + "isAutoUpdate": true +}`), + ), instance.GitLabIDPChangedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceGitLabIDPChanged, + want: wantReduce{ + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: idpTemplateUpdateStmt, + expectedArgs: []interface{}{ + "name", + true, + true, + true, + true, + anyArg{}, + uint64(15), + "idp-id", + "instance-id", + }, + }, + { + expectedStmt: "UPDATE projections.idp_templates3_gitlab SET (client_id, client_secret, scopes) = ($1, $2, $3) WHERE (idp_id = $4) AND (instance_id = $5)", + expectedArgs: []interface{}{ + "client_id", + anyArg{}, + database.StringArray{"profile"}, + "idp-id", + "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, IDPTemplateTable, tt.want) + }) + } +} + +func TestIDPTemplateProjection_reducesGitLabSelfHosted(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: "instance reduceGitLabSelfHostedIDPAdded", + args: args{ + event: getEvent(testEvent( + repository.EventType(instance.GitLabSelfHostedIDPAddedEventType), + instance.AggregateType, + []byte(`{ + "id": "idp-id", + "name": "name", + "issuer": "issuer", + "client_id": "client_id", + "client_secret": { + "cryptoType": 0, + "algorithm": "RSA-265", + "keyId": "key-id" + }, + "scopes": ["profile"], + "isCreationAllowed": true, + "isLinkingAllowed": true, + "isAutoCreation": true, + "isAutoUpdate": true +}`), + ), instance.GitLabSelfHostedIDPAddedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceGitLabSelfHostedIDPAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: idpTemplateInsertStmt, + expectedArgs: []interface{}{ + "idp-id", + anyArg{}, + anyArg{}, + uint64(15), + "ro-id", + "instance-id", + domain.IDPStateActive, + "name", + domain.IdentityProviderTypeSystem, + domain.IDPTypeGitLabSelfHosted, + true, + true, + true, + true, + }, + }, + { + expectedStmt: "INSERT INTO projections.idp_templates3_gitlab_self_hosted (idp_id, instance_id, issuer, client_id, client_secret, scopes) VALUES ($1, $2, $3, $4, $5, $6)", + expectedArgs: []interface{}{ + "idp-id", + "instance-id", + "issuer", + "client_id", + anyArg{}, + database.StringArray{"profile"}, + }, + }, + }, + }, + }, + }, + { + name: "org reduceGitLabSelfHostedIDPAdded", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.GitLabSelfHostedIDPAddedEventType), + org.AggregateType, + []byte(`{ + "id": "idp-id", + "name": "name", + "issuer": "issuer", + "client_id": "client_id", + "client_secret": { + "cryptoType": 0, + "algorithm": "RSA-265", + "keyId": "key-id" + }, + "scopes": ["profile"], + "isCreationAllowed": true, + "isLinkingAllowed": true, + "isAutoCreation": true, + "isAutoUpdate": true +}`), + ), org.GitLabSelfHostedIDPAddedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceGitLabSelfHostedIDPAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: idpTemplateInsertStmt, + expectedArgs: []interface{}{ + "idp-id", + anyArg{}, + anyArg{}, + uint64(15), + "ro-id", + "instance-id", + domain.IDPStateActive, + "name", + domain.IdentityProviderTypeOrg, + domain.IDPTypeGitLabSelfHosted, + true, + true, + true, + true, + }, + }, + { + expectedStmt: "INSERT INTO projections.idp_templates3_gitlab_self_hosted (idp_id, instance_id, issuer, client_id, client_secret, scopes) VALUES ($1, $2, $3, $4, $5, $6)", + expectedArgs: []interface{}{ + "idp-id", + "instance-id", + "issuer", + "client_id", + anyArg{}, + database.StringArray{"profile"}, + }, + }, + }, + }, + }, + }, + { + name: "instance reduceGitLabSelfHostedIDPChanged minimal", + args: args{ + event: getEvent(testEvent( + repository.EventType(instance.GitLabSelfHostedIDPChangedEventType), + instance.AggregateType, + []byte(`{ + "id": "idp-id", + "isCreationAllowed": true, + "issuer": "issuer" +}`), + ), instance.GitLabSelfHostedIDPChangedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceGitLabSelfHostedIDPChanged, + want: wantReduce{ + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: idpTemplateUpdateMinimalStmt, + expectedArgs: []interface{}{ + true, + anyArg{}, + uint64(15), + "idp-id", + "instance-id", + }, + }, + { + expectedStmt: "UPDATE projections.idp_templates3_gitlab_self_hosted SET issuer = $1 WHERE (idp_id = $2) AND (instance_id = $3)", + expectedArgs: []interface{}{ + "issuer", + "idp-id", + "instance-id", + }, + }, + }, + }, + }, + }, + { + name: "instance reduceGitLabSelfHostedIDPChanged", + args: args{ + event: getEvent(testEvent( + repository.EventType(instance.GitLabSelfHostedIDPChangedEventType), + instance.AggregateType, + []byte(`{ + "id": "idp-id", + "name": "name", + "issuer": "issuer", + "client_id": "client_id", + "client_secret": { + "cryptoType": 0, + "algorithm": "RSA-265", + "keyId": "key-id" + }, + "scopes": ["profile"], + "isCreationAllowed": true, + "isLinkingAllowed": true, + "isAutoCreation": true, + "isAutoUpdate": true +}`), + ), instance.GitLabSelfHostedIDPChangedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceGitLabSelfHostedIDPChanged, + want: wantReduce{ + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: idpTemplateUpdateStmt, + expectedArgs: []interface{}{ + "name", + true, + true, + true, + true, + anyArg{}, + uint64(15), + "idp-id", + "instance-id", + }, + }, + { + expectedStmt: "UPDATE projections.idp_templates3_gitlab_self_hosted SET (issuer, client_id, client_secret, scopes) = ($1, $2, $3, $4) WHERE (idp_id = $5) AND (instance_id = $6)", + expectedArgs: []interface{}{ + "issuer", + "client_id", + anyArg{}, + database.StringArray{"profile"}, + "idp-id", + "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, IDPTemplateTable, tt.want) + }) + } +} + func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { type args struct { event func(t *testing.T) eventstore.Event @@ -963,7 +1480,7 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_templates3 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedStmt: idpTemplateInsertStmt, expectedArgs: []interface{}{ "idp-id", anyArg{}, @@ -1025,7 +1542,7 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_templates3 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedStmt: idpTemplateInsertStmt, expectedArgs: []interface{}{ "idp-id", anyArg{}, @@ -1078,7 +1595,7 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idp_templates3 SET (is_creation_allowed, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: idpTemplateUpdateMinimalStmt, expectedArgs: []interface{}{ true, anyArg{}, @@ -1107,6 +1624,7 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { instance.AggregateType, []byte(`{ "id": "idp-id", + "name": "name", "clientId": "client_id", "clientSecret": { "cryptoType": 0, @@ -1129,8 +1647,9 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idp_templates3 SET (is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update, change_date, sequence) = ($1, $2, $3, $4, $5, $6) WHERE (id = $7) AND (instance_id = $8)", + expectedStmt: idpTemplateUpdateStmt, expectedArgs: []interface{}{ + "name", true, true, true, @@ -1230,7 +1749,7 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_templates3 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedStmt: idpTemplateInsertStmt, expectedArgs: []interface{}{ "idp-id", anyArg{}, @@ -1329,7 +1848,7 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_templates3 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedStmt: idpTemplateInsertStmt, expectedArgs: []interface{}{ "idp-id", anyArg{}, @@ -1470,7 +1989,7 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idp_templates3 SET (name, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update, change_date, sequence) = ($1, $2, $3, $4, $5, $6, $7) WHERE (id = $8) AND (instance_id = $9)", + expectedStmt: idpTemplateUpdateStmt, expectedArgs: []interface{}{ "custom-zitadel-instance", true, @@ -1602,7 +2121,7 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_templates3 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedStmt: idpTemplateInsertStmt, expectedArgs: []interface{}{ "idp-id", anyArg{}, @@ -1666,7 +2185,7 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_templates3 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedStmt: idpTemplateInsertStmt, expectedArgs: []interface{}{ "idp-id", anyArg{}, @@ -1720,7 +2239,7 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idp_templates3 SET (is_creation_allowed, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: idpTemplateUpdateMinimalStmt, expectedArgs: []interface{}{ true, anyArg{}, @@ -1749,6 +2268,7 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { instance.AggregateType, []byte(`{ "id": "idp-id", + "name": "name", "issuer": "issuer", "clientId": "client_id", "clientSecret": { @@ -1772,8 +2292,9 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idp_templates3 SET (is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update, change_date, sequence) = ($1, $2, $3, $4, $5, $6) WHERE (id = $7) AND (instance_id = $8)", + expectedStmt: idpTemplateUpdateStmt, expectedArgs: []interface{}{ + "name", true, true, true, @@ -1848,7 +2369,7 @@ func TestIDPTemplateProjection_reducesOldConfig(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_templates3 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedStmt: idpTemplateInsertStmt, expectedArgs: []interface{}{ "idp-config-id", anyArg{}, @@ -1893,7 +2414,7 @@ func TestIDPTemplateProjection_reducesOldConfig(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_templates3 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedStmt: idpTemplateInsertStmt, expectedArgs: []interface{}{ "idp-config-id", anyArg{}, @@ -2437,7 +2958,7 @@ func TestIDPTemplateProjection_reducesJWT(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_templates3 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedStmt: idpTemplateInsertStmt, expectedArgs: []interface{}{ "idp-id", anyArg{}, @@ -2497,7 +3018,7 @@ func TestIDPTemplateProjection_reducesJWT(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_templates3 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedStmt: idpTemplateInsertStmt, expectedArgs: []interface{}{ "idp-id", anyArg{}, @@ -2551,7 +3072,7 @@ func TestIDPTemplateProjection_reducesJWT(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idp_templates3 SET (is_creation_allowed, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: idpTemplateUpdateMinimalStmt, expectedArgs: []interface{}{ true, anyArg{}, diff --git a/internal/repository/idp/gitlab.go b/internal/repository/idp/gitlab.go new file mode 100644 index 0000000000..78ef99c4b0 --- /dev/null +++ b/internal/repository/idp/gitlab.go @@ -0,0 +1,288 @@ +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 { + eventstore.BaseEvent `json:"-"` + + ID string `json:"id"` + Name string `json:"name,omitempty"` + ClientID string `json:"client_id"` + ClientSecret *crypto.CryptoValue `json:"client_secret"` + Scopes []string `json:"scopes,omitempty"` + Options +} + +func NewGitLabIDPAddedEvent( + base *eventstore.BaseEvent, + id, + name, + clientID string, + clientSecret *crypto.CryptoValue, + scopes []string, + options Options, +) *GitLabIDPAddedEvent { + return &GitLabIDPAddedEvent{ + BaseEvent: *base, + ID: id, + Name: name, + ClientID: clientID, + ClientSecret: clientSecret, + Scopes: scopes, + Options: options, + } +} + +func (e *GitLabIDPAddedEvent) Data() interface{} { + return e +} + +func (e *GitLabIDPAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func GitLabIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { + e := &GitLabIDPAddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "IDP-KLewio", "unable to unmarshal event") + } + + return e, nil +} + +type GitLabIDPChangedEvent struct { + eventstore.BaseEvent `json:"-"` + + ID string `json:"id"` + Name *string `json:"name,omitempty"` + ClientID *string `json:"client_id,omitempty"` + ClientSecret *crypto.CryptoValue `json:"client_secret,omitempty"` + Scopes []string `json:"scopes,omitempty"` + OptionChanges +} + +func NewGitLabIDPChangedEvent( + base *eventstore.BaseEvent, + id string, + changes []GitLabIDPChanges, +) (*GitLabIDPChangedEvent, error) { + if len(changes) == 0 { + return nil, errors.ThrowPreconditionFailed(nil, "IDP-K2gje", "Errors.NoChangesFound") + } + changedEvent := &GitLabIDPChangedEvent{ + BaseEvent: *base, + ID: id, + } + for _, change := range changes { + change(changedEvent) + } + return changedEvent, nil +} + +type GitLabIDPChanges func(*GitLabIDPChangedEvent) + +func ChangeGitLabName(name string) func(*GitLabIDPChangedEvent) { + return func(e *GitLabIDPChangedEvent) { + e.Name = &name + } +} + +func ChangeGitLabClientID(clientID string) func(*GitLabIDPChangedEvent) { + return func(e *GitLabIDPChangedEvent) { + e.ClientID = &clientID + } +} + +func ChangeGitLabClientSecret(clientSecret *crypto.CryptoValue) func(*GitLabIDPChangedEvent) { + return func(e *GitLabIDPChangedEvent) { + e.ClientSecret = clientSecret + } +} + +func ChangeGitLabScopes(scopes []string) func(*GitLabIDPChangedEvent) { + return func(e *GitLabIDPChangedEvent) { + e.Scopes = scopes + } +} + +func ChangeGitLabOptions(options OptionChanges) func(*GitLabIDPChangedEvent) { + return func(e *GitLabIDPChangedEvent) { + e.OptionChanges = options + } +} + +func (e *GitLabIDPChangedEvent) Data() interface{} { + return e +} + +func (e *GitLabIDPChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func GitLabIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { + e := &GitLabIDPChangedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "IDP-Sfhjk", "unable to unmarshal event") + } + + return e, nil +} + +type GitLabSelfHostedIDPAddedEvent struct { + eventstore.BaseEvent `json:"-"` + + ID string `json:"id"` + Name string `json:"name"` + Issuer string `json:"issuer"` + ClientID string `json:"client_id"` + ClientSecret *crypto.CryptoValue `json:"client_secret"` + Scopes []string `json:"scopes,omitempty"` + Options +} + +func NewGitLabSelfHostedIDPAddedEvent( + base *eventstore.BaseEvent, + id, + name, + issuer, + clientID string, + clientSecret *crypto.CryptoValue, + scopes []string, + options Options, +) *GitLabSelfHostedIDPAddedEvent { + return &GitLabSelfHostedIDPAddedEvent{ + BaseEvent: *base, + ID: id, + Name: name, + Issuer: issuer, + ClientID: clientID, + ClientSecret: clientSecret, + Scopes: scopes, + Options: options, + } +} + +func (e *GitLabSelfHostedIDPAddedEvent) Data() interface{} { + return e +} + +func (e *GitLabSelfHostedIDPAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func GitLabSelfHostedIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { + e := &GitLabSelfHostedIDPAddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "IDP-S1efv", "unable to unmarshal event") + } + + return e, nil +} + +type GitLabSelfHostedIDPChangedEvent struct { + eventstore.BaseEvent `json:"-"` + + ID string `json:"id"` + Name *string `json:"name,omitempty"` + Issuer *string `json:"issuer,omitempty"` + ClientID *string `json:"client_id,omitempty"` + ClientSecret *crypto.CryptoValue `json:"client_secret,omitempty"` + Scopes []string `json:"scopes,omitempty"` + OptionChanges +} + +func NewGitLabSelfHostedIDPChangedEvent( + base *eventstore.BaseEvent, + id string, + changes []GitLabSelfHostedIDPChanges, +) (*GitLabSelfHostedIDPChangedEvent, error) { + if len(changes) == 0 { + return nil, errors.ThrowPreconditionFailed(nil, "IDP-Dghj6", "Errors.NoChangesFound") + } + changedEvent := &GitLabSelfHostedIDPChangedEvent{ + BaseEvent: *base, + ID: id, + } + for _, change := range changes { + change(changedEvent) + } + return changedEvent, nil +} + +type GitLabSelfHostedIDPChanges func(*GitLabSelfHostedIDPChangedEvent) + +func ChangeGitLabSelfHostedName(name string) func(*GitLabSelfHostedIDPChangedEvent) { + return func(e *GitLabSelfHostedIDPChangedEvent) { + e.Name = &name + } +} + +func ChangeGitLabSelfHostedIssuer(issuer string) func(*GitLabSelfHostedIDPChangedEvent) { + return func(e *GitLabSelfHostedIDPChangedEvent) { + e.Issuer = &issuer + } +} + +func ChangeGitLabSelfHostedClientID(clientID string) func(*GitLabSelfHostedIDPChangedEvent) { + return func(e *GitLabSelfHostedIDPChangedEvent) { + e.ClientID = &clientID + } +} + +func ChangeGitLabSelfHostedClientSecret(clientSecret *crypto.CryptoValue) func(*GitLabSelfHostedIDPChangedEvent) { + return func(e *GitLabSelfHostedIDPChangedEvent) { + e.ClientSecret = clientSecret + } +} + +func ChangeGitLabSelfHostedScopes(scopes []string) func(*GitLabSelfHostedIDPChangedEvent) { + return func(e *GitLabSelfHostedIDPChangedEvent) { + e.Scopes = scopes + } +} + +func ChangeGitLabSelfHostedOptions(options OptionChanges) func(*GitLabSelfHostedIDPChangedEvent) { + return func(e *GitLabSelfHostedIDPChangedEvent) { + e.OptionChanges = options + } +} + +func (e *GitLabSelfHostedIDPChangedEvent) Data() interface{} { + return e +} + +func (e *GitLabSelfHostedIDPChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func GitLabSelfHostedIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { + e := &GitLabSelfHostedIDPChangedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "IDP-SFrhj", "unable to unmarshal event") + } + + return e, nil +} diff --git a/internal/repository/instance/eventstore.go b/internal/repository/instance/eventstore.go index 50c430ac75..60417121e8 100644 --- a/internal/repository/instance/eventstore.go +++ b/internal/repository/instance/eventstore.go @@ -80,6 +80,10 @@ func RegisterEventMappers(es *eventstore.Eventstore) { RegisterFilterEventMapper(AggregateType, GitHubIDPChangedEventType, GitHubIDPChangedEventMapper). RegisterFilterEventMapper(AggregateType, GitHubEnterpriseIDPAddedEventType, GitHubEnterpriseIDPAddedEventMapper). RegisterFilterEventMapper(AggregateType, GitHubEnterpriseIDPChangedEventType, GitHubEnterpriseIDPChangedEventMapper). + RegisterFilterEventMapper(AggregateType, GitLabIDPAddedEventType, GitLabIDPAddedEventMapper). + RegisterFilterEventMapper(AggregateType, GitLabIDPChangedEventType, GitLabIDPChangedEventMapper). + RegisterFilterEventMapper(AggregateType, GitLabSelfHostedIDPAddedEventType, GitLabSelfHostedIDPAddedEventMapper). + RegisterFilterEventMapper(AggregateType, GitLabSelfHostedIDPChangedEventType, GitLabSelfHostedIDPChangedEventMapper). RegisterFilterEventMapper(AggregateType, GoogleIDPAddedEventType, GoogleIDPAddedEventMapper). RegisterFilterEventMapper(AggregateType, GoogleIDPChangedEventType, GoogleIDPChangedEventMapper). RegisterFilterEventMapper(AggregateType, LDAPIDPAddedEventType, LDAPIDPAddedEventMapper). diff --git a/internal/repository/instance/idp.go b/internal/repository/instance/idp.go index a7bec4ff6d..22aa4c41e3 100644 --- a/internal/repository/instance/idp.go +++ b/internal/repository/instance/idp.go @@ -20,6 +20,10 @@ const ( GitHubIDPChangedEventType eventstore.EventType = "instance.idp.github.changed" GitHubEnterpriseIDPAddedEventType eventstore.EventType = "instance.idp.github_enterprise.added" GitHubEnterpriseIDPChangedEventType eventstore.EventType = "instance.idp.github_enterprise.changed" + GitLabIDPAddedEventType eventstore.EventType = "instance.idp.gitlab.added" + GitLabIDPChangedEventType eventstore.EventType = "instance.idp.gitlab.changed" + GitLabSelfHostedIDPAddedEventType eventstore.EventType = "instance.idp.gitlab_self_hosted.added" + GitLabSelfHostedIDPChangedEventType eventstore.EventType = "instance.idp.gitlab_self_hosted.changed" GoogleIDPAddedEventType eventstore.EventType = "instance.idp.google.added" GoogleIDPChangedEventType eventstore.EventType = "instance.idp.google.changed" LDAPIDPAddedEventType eventstore.EventType = "instance.idp.ldap.added" @@ -425,6 +429,160 @@ func GitHubEnterpriseIDPChangedEventMapper(event *repository.Event) (eventstore. return &GitHubEnterpriseIDPChangedEvent{GitHubEnterpriseIDPChangedEvent: *e.(*idp.GitHubEnterpriseIDPChangedEvent)}, nil } +type GitLabIDPAddedEvent struct { + idp.GitLabIDPAddedEvent +} + +func NewGitLabIDPAddedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + clientID string, + clientSecret *crypto.CryptoValue, + scopes []string, + options idp.Options, +) *GitLabIDPAddedEvent { + + return &GitLabIDPAddedEvent{ + GitLabIDPAddedEvent: *idp.NewGitLabIDPAddedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + GitLabIDPAddedEventType, + ), + id, + name, + clientID, + clientSecret, + scopes, + options, + ), + } +} + +func GitLabIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.GitLabIDPAddedEventMapper(event) + if err != nil { + return nil, err + } + + return &GitLabIDPAddedEvent{GitLabIDPAddedEvent: *e.(*idp.GitLabIDPAddedEvent)}, nil +} + +type GitLabIDPChangedEvent struct { + idp.GitLabIDPChangedEvent +} + +func NewGitLabIDPChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id string, + changes []idp.GitLabIDPChanges, +) (*GitLabIDPChangedEvent, error) { + + changedEvent, err := idp.NewGitLabIDPChangedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + GitLabIDPChangedEventType, + ), + id, + changes, + ) + if err != nil { + return nil, err + } + return &GitLabIDPChangedEvent{GitLabIDPChangedEvent: *changedEvent}, nil +} + +func GitLabIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.GitLabIDPChangedEventMapper(event) + if err != nil { + return nil, err + } + + return &GitLabIDPChangedEvent{GitLabIDPChangedEvent: *e.(*idp.GitLabIDPChangedEvent)}, nil +} + +type GitLabSelfHostedIDPAddedEvent struct { + idp.GitLabSelfHostedIDPAddedEvent +} + +func NewGitLabSelfHostedIDPAddedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + issuer, + clientID string, + clientSecret *crypto.CryptoValue, + scopes []string, + options idp.Options, +) *GitLabSelfHostedIDPAddedEvent { + + return &GitLabSelfHostedIDPAddedEvent{ + GitLabSelfHostedIDPAddedEvent: *idp.NewGitLabSelfHostedIDPAddedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + GitLabSelfHostedIDPAddedEventType, + ), + id, + name, + issuer, + clientID, + clientSecret, + scopes, + options, + ), + } +} + +func GitLabSelfHostedIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.GitLabSelfHostedIDPAddedEventMapper(event) + if err != nil { + return nil, err + } + + return &GitLabSelfHostedIDPAddedEvent{GitLabSelfHostedIDPAddedEvent: *e.(*idp.GitLabSelfHostedIDPAddedEvent)}, nil +} + +type GitLabSelfHostedIDPChangedEvent struct { + idp.GitLabSelfHostedIDPChangedEvent +} + +func NewGitLabSelfHostedIDPChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id string, + changes []idp.GitLabSelfHostedIDPChanges, +) (*GitLabSelfHostedIDPChangedEvent, error) { + + changedEvent, err := idp.NewGitLabSelfHostedIDPChangedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + GitLabSelfHostedIDPChangedEventType, + ), + id, + changes, + ) + if err != nil { + return nil, err + } + return &GitLabSelfHostedIDPChangedEvent{GitLabSelfHostedIDPChangedEvent: *changedEvent}, nil +} + +func GitLabSelfHostedIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.GitLabSelfHostedIDPChangedEventMapper(event) + if err != nil { + return nil, err + } + + return &GitLabSelfHostedIDPChangedEvent{GitLabSelfHostedIDPChangedEvent: *e.(*idp.GitLabSelfHostedIDPChangedEvent)}, nil +} + type GoogleIDPAddedEvent struct { idp.GoogleIDPAddedEvent } diff --git a/internal/repository/org/eventstore.go b/internal/repository/org/eventstore.go index bb288603ab..fc6128f7b1 100644 --- a/internal/repository/org/eventstore.go +++ b/internal/repository/org/eventstore.go @@ -88,6 +88,10 @@ func RegisterEventMappers(es *eventstore.Eventstore) { RegisterFilterEventMapper(AggregateType, GitHubIDPChangedEventType, GitHubIDPChangedEventMapper). RegisterFilterEventMapper(AggregateType, GitHubEnterpriseIDPAddedEventType, GitHubEnterpriseIDPAddedEventMapper). RegisterFilterEventMapper(AggregateType, GitHubEnterpriseIDPChangedEventType, GitHubEnterpriseIDPChangedEventMapper). + RegisterFilterEventMapper(AggregateType, GitLabIDPAddedEventType, GitLabIDPAddedEventMapper). + RegisterFilterEventMapper(AggregateType, GitLabIDPChangedEventType, GitLabIDPChangedEventMapper). + RegisterFilterEventMapper(AggregateType, GitLabSelfHostedIDPAddedEventType, GitLabSelfHostedIDPAddedEventMapper). + RegisterFilterEventMapper(AggregateType, GitLabSelfHostedIDPChangedEventType, GitLabSelfHostedIDPChangedEventMapper). RegisterFilterEventMapper(AggregateType, GoogleIDPAddedEventType, GoogleIDPAddedEventMapper). RegisterFilterEventMapper(AggregateType, GoogleIDPChangedEventType, GoogleIDPChangedEventMapper). RegisterFilterEventMapper(AggregateType, LDAPIDPAddedEventType, LDAPIDPAddedEventMapper). diff --git a/internal/repository/org/idp.go b/internal/repository/org/idp.go index b86c57610a..db9ad882f9 100644 --- a/internal/repository/org/idp.go +++ b/internal/repository/org/idp.go @@ -20,6 +20,10 @@ const ( GitHubIDPChangedEventType eventstore.EventType = "org.idp.github.changed" GitHubEnterpriseIDPAddedEventType eventstore.EventType = "org.idp.github_enterprise.added" GitHubEnterpriseIDPChangedEventType eventstore.EventType = "org.idp.github_enterprise.changed" + GitLabIDPAddedEventType eventstore.EventType = "org.idp.gitlab.added" + GitLabIDPChangedEventType eventstore.EventType = "org.idp.gitlab.changed" + GitLabSelfHostedIDPAddedEventType eventstore.EventType = "org.idp.gitlab_self_hosted.added" + GitLabSelfHostedIDPChangedEventType eventstore.EventType = "org.idp.gitlab_self_hosted.changed" GoogleIDPAddedEventType eventstore.EventType = "org.idp.google.added" GoogleIDPChangedEventType eventstore.EventType = "org.idp.google.changed" LDAPIDPAddedEventType eventstore.EventType = "org.idp.ldap.added" @@ -425,6 +429,160 @@ func GitHubEnterpriseIDPChangedEventMapper(event *repository.Event) (eventstore. return &GitHubEnterpriseIDPChangedEvent{GitHubEnterpriseIDPChangedEvent: *e.(*idp.GitHubEnterpriseIDPChangedEvent)}, nil } +type GitLabIDPAddedEvent struct { + idp.GitLabIDPAddedEvent +} + +func NewGitLabIDPAddedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + clientID string, + clientSecret *crypto.CryptoValue, + scopes []string, + options idp.Options, +) *GitLabIDPAddedEvent { + + return &GitLabIDPAddedEvent{ + GitLabIDPAddedEvent: *idp.NewGitLabIDPAddedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + GitLabIDPAddedEventType, + ), + id, + name, + clientID, + clientSecret, + scopes, + options, + ), + } +} + +func GitLabIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.GitLabIDPAddedEventMapper(event) + if err != nil { + return nil, err + } + + return &GitLabIDPAddedEvent{GitLabIDPAddedEvent: *e.(*idp.GitLabIDPAddedEvent)}, nil +} + +type GitLabIDPChangedEvent struct { + idp.GitLabIDPChangedEvent +} + +func NewGitLabIDPChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id string, + changes []idp.GitLabIDPChanges, +) (*GitLabIDPChangedEvent, error) { + + changedEvent, err := idp.NewGitLabIDPChangedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + GitLabIDPChangedEventType, + ), + id, + changes, + ) + if err != nil { + return nil, err + } + return &GitLabIDPChangedEvent{GitLabIDPChangedEvent: *changedEvent}, nil +} + +func GitLabIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.GitLabIDPChangedEventMapper(event) + if err != nil { + return nil, err + } + + return &GitLabIDPChangedEvent{GitLabIDPChangedEvent: *e.(*idp.GitLabIDPChangedEvent)}, nil +} + +type GitLabSelfHostedIDPAddedEvent struct { + idp.GitLabSelfHostedIDPAddedEvent +} + +func NewGitLabSelfHostedIDPAddedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + issuer, + clientID string, + clientSecret *crypto.CryptoValue, + scopes []string, + options idp.Options, +) *GitLabSelfHostedIDPAddedEvent { + + return &GitLabSelfHostedIDPAddedEvent{ + GitLabSelfHostedIDPAddedEvent: *idp.NewGitLabSelfHostedIDPAddedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + GitLabSelfHostedIDPAddedEventType, + ), + id, + name, + issuer, + clientID, + clientSecret, + scopes, + options, + ), + } +} + +func GitLabSelfHostedIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.GitLabSelfHostedIDPAddedEventMapper(event) + if err != nil { + return nil, err + } + + return &GitLabSelfHostedIDPAddedEvent{GitLabSelfHostedIDPAddedEvent: *e.(*idp.GitLabSelfHostedIDPAddedEvent)}, nil +} + +type GitLabSelfHostedIDPChangedEvent struct { + idp.GitLabSelfHostedIDPChangedEvent +} + +func NewGitLabSelfHostedIDPChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id string, + changes []idp.GitLabSelfHostedIDPChanges, +) (*GitLabSelfHostedIDPChangedEvent, error) { + + changedEvent, err := idp.NewGitLabSelfHostedIDPChangedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + GitLabSelfHostedIDPChangedEventType, + ), + id, + changes, + ) + if err != nil { + return nil, err + } + return &GitLabSelfHostedIDPChangedEvent{GitLabSelfHostedIDPChangedEvent: *changedEvent}, nil +} + +func GitLabSelfHostedIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.GitLabSelfHostedIDPChangedEventMapper(event) + if err != nil { + return nil, err + } + + return &GitLabSelfHostedIDPChangedEvent{GitLabSelfHostedIDPChangedEvent: *e.(*idp.GitLabSelfHostedIDPChangedEvent)}, nil +} + type GoogleIDPAddedEvent struct { idp.GoogleIDPAddedEvent } diff --git a/proto/zitadel/admin.proto b/proto/zitadel/admin.proto index 3325cc5e7a..f852cbe18d 100644 --- a/proto/zitadel/admin.proto +++ b/proto/zitadel/admin.proto @@ -1368,6 +1368,54 @@ service AdminService { }; } + // Add a new GitLab identity provider on the instance + rpc AddGitLabProvider(AddGitLabProviderRequest) returns (AddGitLabProviderResponse) { + option (google.api.http) = { + post: "/idps/gitlab" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "iam.idp.write" + }; + } + + // Change an existing GitLab identity provider on the instance + rpc UpdateGitLabProvider(UpdateGitLabProviderRequest) returns (UpdateGitLabProviderResponse) { + option (google.api.http) = { + post: "/idps/gitlab/{id}" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "iam.idp.write" + }; + } + + // Add a new self hosted GitLab identity provider on the instance + rpc AddGitLabSelfHostedProvider(AddGitLabSelfHostedProviderRequest) returns (AddGitLabSelfHostedProviderResponse) { + option (google.api.http) = { + post: "/idps/gitlab_self_hosted" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "iam.idp.write" + }; + } + + // Change an existing self hosted GitLab identity provider on the instance + rpc UpdateGitLabSelfHostedProvider(UpdateGitLabSelfHostedProviderRequest) returns (UpdateGitLabSelfHostedProviderResponse) { + option (google.api.http) = { + post: "/idps/gitlab_self_hosted/{id}" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "iam.idp.write" + }; + } + // Add a new Google identity provider on the instance rpc AddGoogleProvider(AddGoogleProviderRequest) returns (AddGoogleProviderResponse) { option (google.api.http) = { @@ -4538,6 +4586,63 @@ message UpdateGitHubEnterpriseServerProviderResponse { zitadel.v1.ObjectDetails details = 1; } +message AddGitLabProviderRequest { + // GitLab will be used as default, if no name is provided + string name = 1 [(validate.rules).string = {max_len: 200}]; + string client_id = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string client_secret = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; + repeated string scopes = 4 [(validate.rules).repeated = {max_items: 20, items: {string: {min_len: 1, max_len: 100}}}]; + zitadel.idp.v1.Options provider_options = 5; +} + +message AddGitLabProviderResponse { + zitadel.v1.ObjectDetails details = 1; + string id = 2; +} + +message UpdateGitLabProviderRequest { + string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string name = 2 [(validate.rules).string = {max_len: 200}]; + string client_id = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; + // client_secret will only be updated if provided + string client_secret = 4 [(validate.rules).string = {max_len: 200}]; + repeated string scopes = 5 [(validate.rules).repeated = {max_items: 20, items: {string: {min_len: 1, max_len: 100}}}]; + zitadel.idp.v1.Options provider_options = 6; +} + +message UpdateGitLabProviderResponse { + zitadel.v1.ObjectDetails details = 1; +} + +message AddGitLabSelfHostedProviderRequest { + string issuer = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string client_id = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string client_secret = 4 [(validate.rules).string = {min_len: 1, max_len: 200}]; + repeated string scopes = 5 [(validate.rules).repeated = {max_items: 20, items: {string: {min_len: 1, max_len: 100}}}]; + zitadel.idp.v1.Options provider_options = 6; +} + +message AddGitLabSelfHostedProviderResponse { + zitadel.v1.ObjectDetails details = 1; + string id = 2; +} + +message UpdateGitLabSelfHostedProviderRequest { + string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string issuer = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string name = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string client_id = 4 [(validate.rules).string = {min_len: 1, max_len: 200}]; + // client_secret will only be updated if provided + string client_secret = 5 [(validate.rules).string = {max_len: 200}]; + repeated string scopes = 6 [(validate.rules).repeated = {max_items: 20, items: {string: {min_len: 1, max_len: 100}}}]; + zitadel.idp.v1.Options provider_options = 7; +} + +message UpdateGitLabSelfHostedProviderResponse { + zitadel.v1.ObjectDetails details = 1; +} + message AddGoogleProviderRequest { // Google will be used as default, if no name is provided string name = 1 [(validate.rules).string = {max_len: 200}]; diff --git a/proto/zitadel/idp.proto b/proto/zitadel/idp.proto index 1cda8d29ee..414d23347f 100644 --- a/proto/zitadel/idp.proto +++ b/proto/zitadel/idp.proto @@ -269,6 +269,8 @@ message ProviderConfig { JWTConfig jwt = 6; GitHubConfig github = 7; GitHubEnterpriseServerConfig github_es = 8; + GitLabConfig gitlab = 9; + GitLabSelfHostedConfig gitlab_self_hosted = 10; } } message OAuthConfig { @@ -304,6 +306,17 @@ message GoogleConfig { repeated string scopes = 2; } +message GitLabConfig { + string client_id = 1; + repeated string scopes = 2; +} + +message GitLabSelfHostedConfig { + string issuer = 1; + string client_id = 2; + repeated string scopes = 3; +} + message LDAPConfig { string host = 1; string port = 2; diff --git a/proto/zitadel/management.proto b/proto/zitadel/management.proto index 5724270ce4..208c3584b0 100644 --- a/proto/zitadel/management.proto +++ b/proto/zitadel/management.proto @@ -6584,6 +6584,54 @@ service ManagementService { }; } + // Add a new GitLab identity provider in the organization + rpc AddGitLabProvider(AddGitLabProviderRequest) returns (AddGitLabProviderResponse) { + option (google.api.http) = { + post: "/idps/gitlab" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "org.idp.write" + }; + } + + // Change an existing GitLab identity provider in the organization + rpc UpdateGitLabProvider(UpdateGitLabProviderRequest) returns (UpdateGitLabProviderResponse) { + option (google.api.http) = { + put: "/idps/gitlab/{id}" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "org.idp.write" + }; + } + + // Add a new self hosted GitLab identity provider in the organization + rpc AddGitLabSelfHostedProvider(AddGitLabSelfHostedProviderRequest) returns (AddGitLabSelfHostedProviderResponse) { + option (google.api.http) = { + post: "/idps/gitlab_self_hosted" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "org.idp.write" + }; + } + + // Change an existing self hosted GitLab identity provider in the organization + rpc UpdateGitLabSelfHostedProvider(UpdateGitLabSelfHostedProviderRequest) returns (UpdateGitLabSelfHostedProviderResponse) { + option (google.api.http) = { + put: "/idps/gitlab_self_hosted/{id}" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "org.idp.write" + }; + } + // Add a new Google identity provider in the organization rpc AddGoogleProvider(AddGoogleProviderRequest) returns (AddGoogleProviderResponse) { option (google.api.http) = { @@ -11212,6 +11260,63 @@ message UpdateGitHubEnterpriseServerProviderResponse { zitadel.v1.ObjectDetails details = 1; } +message AddGitLabProviderRequest { + // GitLab will be used as default, if no name is provided + string name = 1 [(validate.rules).string = {max_len: 200}]; + string client_id = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string client_secret = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; + repeated string scopes = 4 [(validate.rules).repeated = {max_items: 20, items: {string: {min_len: 1, max_len: 100}}}]; + zitadel.idp.v1.Options provider_options = 5; +} + +message AddGitLabProviderResponse { + zitadel.v1.ObjectDetails details = 1; + string id = 2; +} + +message UpdateGitLabProviderRequest { + string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string name = 2 [(validate.rules).string = {max_len: 200}]; + string client_id = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; + // client_secret will only be updated if provided + string client_secret = 4 [(validate.rules).string = {max_len: 200}]; + repeated string scopes = 5 [(validate.rules).repeated = {max_items: 20, items: {string: {min_len: 1, max_len: 100}}}]; + zitadel.idp.v1.Options provider_options = 6; +} + +message UpdateGitLabProviderResponse { + zitadel.v1.ObjectDetails details = 1; +} + +message AddGitLabSelfHostedProviderRequest { + string issuer = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string client_id = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string client_secret = 4 [(validate.rules).string = {min_len: 1, max_len: 200}]; + repeated string scopes = 5 [(validate.rules).repeated = {max_items: 20, items: {string: {min_len: 1, max_len: 100}}}]; + zitadel.idp.v1.Options provider_options = 6; +} + +message AddGitLabSelfHostedProviderResponse { + zitadel.v1.ObjectDetails details = 1; + string id = 2; +} + +message UpdateGitLabSelfHostedProviderRequest { + string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string issuer = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string name = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string client_id = 4 [(validate.rules).string = {min_len: 1, max_len: 200}]; + // client_secret will only be updated if provided + string client_secret = 5 [(validate.rules).string = {max_len: 200}]; + repeated string scopes = 6 [(validate.rules).repeated = {max_items: 20, items: {string: {min_len: 1, max_len: 100}}}]; + zitadel.idp.v1.Options provider_options = 7; +} + +message UpdateGitLabSelfHostedProviderResponse { + zitadel.v1.ObjectDetails details = 1; +} + message AddGoogleProviderRequest { // Google will be used as default, if no name is provided string name = 1 [(validate.rules).string = {max_len: 200}];