diff --git a/internal/actions/object/user.go b/internal/actions/object/user.go index 83fa873b85..0c8ed07e91 100644 --- a/internal/actions/object/user.go +++ b/internal/actions/object/user.go @@ -4,6 +4,7 @@ import ( "time" "github.com/dop251/goja" + "github.com/zitadel/zitadel/internal/actions" "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/domain" @@ -27,7 +28,7 @@ func externalUsersFromDomain(users []*domain.ExternalUser) []*externalUser { func externalUserFromDomain(user *domain.ExternalUser) *externalUser { return &externalUser{ ExternalId: user.ExternalUserID, - ExternalIdpId: user.ExternalUserID, + ExternalIdpId: user.IDPConfigID, Human: human{ FirstName: user.FirstName, LastName: user.LastName, diff --git a/internal/api/grpc/admin/idp.go b/internal/api/grpc/admin/idp.go index 5151999723..1e0e9ccdf5 100644 --- a/internal/api/grpc/admin/idp.go +++ b/internal/api/grpc/admin/idp.go @@ -241,6 +241,48 @@ func (s *Server) UpdateJWTProvider(ctx context.Context, req *admin_pb.UpdateJWTP }, nil } +func (s *Server) AddGitHubProvider(ctx context.Context, req *admin_pb.AddGitHubProviderRequest) (*admin_pb.AddGitHubProviderResponse, error) { + id, details, err := s.command.AddInstanceGitHubProvider(ctx, addGitHubProviderToCommand(req)) + if err != nil { + return nil, err + } + return &admin_pb.AddGitHubProviderResponse{ + Id: id, + Details: object_pb.DomainToAddDetailsPb(details), + }, nil +} + +func (s *Server) UpdateGitHubProvider(ctx context.Context, req *admin_pb.UpdateGitHubProviderRequest) (*admin_pb.UpdateGitHubProviderResponse, error) { + details, err := s.command.UpdateInstanceGitHubProvider(ctx, req.Id, updateGitHubProviderToCommand(req)) + if err != nil { + return nil, err + } + return &admin_pb.UpdateGitHubProviderResponse{ + Details: object_pb.DomainToChangeDetailsPb(details), + }, nil +} + +func (s *Server) AddGitHubEnterpriseServerProvider(ctx context.Context, req *admin_pb.AddGitHubEnterpriseServerProviderRequest) (*admin_pb.AddGitHubEnterpriseServerProviderResponse, error) { + id, details, err := s.command.AddInstanceGitHubEnterpriseProvider(ctx, addGitHubEnterpriseProviderToCommand(req)) + if err != nil { + return nil, err + } + return &admin_pb.AddGitHubEnterpriseServerProviderResponse{ + Id: id, + Details: object_pb.DomainToAddDetailsPb(details), + }, nil +} + +func (s *Server) UpdateGitHubEnterpriseServerProvider(ctx context.Context, req *admin_pb.UpdateGitHubEnterpriseServerProviderRequest) (*admin_pb.UpdateGitHubEnterpriseServerProviderResponse, error) { + details, err := s.command.UpdateInstanceGitHubEnterpriseProvider(ctx, req.Id, updateGitHubEnterpriseProviderToCommand(req)) + if err != nil { + return nil, err + } + return &admin_pb.UpdateGitHubEnterpriseServerProviderResponse{ + 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 fc1802f388..48cc17cfe8 100644 --- a/internal/api/grpc/admin/idp_converter.go +++ b/internal/api/grpc/admin/idp_converter.go @@ -273,6 +273,52 @@ func updateJWTProviderToCommand(req *admin_pb.UpdateJWTProviderRequest) command. } } +func addGitHubProviderToCommand(req *admin_pb.AddGitHubProviderRequest) command.GitHubProvider { + return command.GitHubProvider{ + Name: req.Name, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + Scopes: req.Scopes, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + } +} + +func updateGitHubProviderToCommand(req *admin_pb.UpdateGitHubProviderRequest) command.GitHubProvider { + return command.GitHubProvider{ + Name: req.Name, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + Scopes: req.Scopes, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + } +} + +func addGitHubEnterpriseProviderToCommand(req *admin_pb.AddGitHubEnterpriseServerProviderRequest) command.GitHubEnterpriseProvider { + return command.GitHubEnterpriseProvider{ + Name: req.Name, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + AuthorizationEndpoint: req.AuthorizationEndpoint, + TokenEndpoint: req.TokenEndpoint, + UserEndpoint: req.UserEndpoint, + Scopes: req.Scopes, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + } +} + +func updateGitHubEnterpriseProviderToCommand(req *admin_pb.UpdateGitHubEnterpriseServerProviderRequest) command.GitHubEnterpriseProvider { + return command.GitHubEnterpriseProvider{ + Name: req.Name, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + AuthorizationEndpoint: req.AuthorizationEndpoint, + TokenEndpoint: req.TokenEndpoint, + UserEndpoint: req.UserEndpoint, + 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 75a72c9b23..f693f1369f 100644 --- a/internal/api/grpc/idp/converter.go +++ b/internal/api/grpc/idp/converter.go @@ -376,8 +376,8 @@ func providerTypeToPb(idpType domain.IDPType) idp_pb.ProviderType { return idp_pb.ProviderType_PROVIDER_TYPE_AZURE_AD case domain.IDPTypeGitHub: return idp_pb.ProviderType_PROVIDER_TYPE_GITHUB - case domain.IDPTypeGitHubEE: - return idp_pb.ProviderType_PROVIDER_TYPE_GITHUB_EE + case domain.IDPTypeGitHubEnterprise: + return idp_pb.ProviderType_PROVIDER_TYPE_GITHUB_ES case domain.IDPTypeGitLab: return idp_pb.ProviderType_PROVIDER_TYPE_GITLAB case domain.IDPTypeGitLabSelfHosted: @@ -412,6 +412,14 @@ func configToPb(config *query.IDPTemplate) *idp_pb.ProviderConfig { jwtConfigToPb(providerConfig, config.JWTIDPTemplate) return providerConfig } + if config.GitHubIDPTemplate != nil { + githubConfigToPb(providerConfig, config.GitHubIDPTemplate) + return providerConfig + } + if config.GitHubEnterpriseIDPTemplate != nil { + githubEnterpriseConfigToPb(providerConfig, config.GitHubEnterpriseIDPTemplate) + return providerConfig + } if config.GoogleIDPTemplate != nil { googleConfigToPb(providerConfig, config.GoogleIDPTemplate) return providerConfig @@ -456,6 +464,27 @@ func jwtConfigToPb(providerConfig *idp_pb.ProviderConfig, template *query.JWTIDP } } +func githubConfigToPb(providerConfig *idp_pb.ProviderConfig, template *query.GitHubIDPTemplate) { + providerConfig.Config = &idp_pb.ProviderConfig_Github{ + Github: &idp_pb.GitHubConfig{ + ClientId: template.ClientID, + Scopes: template.Scopes, + }, + } +} + +func githubEnterpriseConfigToPb(providerConfig *idp_pb.ProviderConfig, template *query.GitHubEnterpriseIDPTemplate) { + providerConfig.Config = &idp_pb.ProviderConfig_GithubEs{ + GithubEs: &idp_pb.GitHubEnterpriseServerConfig{ + ClientId: template.ClientID, + AuthorizationEndpoint: template.AuthorizationEndpoint, + TokenEndpoint: template.TokenEndpoint, + UserEndpoint: template.UserEndpoint, + 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 571b06c6b5..7c787eb7e2 100644 --- a/internal/api/grpc/management/idp.go +++ b/internal/api/grpc/management/idp.go @@ -233,6 +233,48 @@ func (s *Server) UpdateJWTProvider(ctx context.Context, req *mgmt_pb.UpdateJWTPr }, nil } +func (s *Server) AddGitHubProvider(ctx context.Context, req *mgmt_pb.AddGitHubProviderRequest) (*mgmt_pb.AddGitHubProviderResponse, error) { + id, details, err := s.command.AddOrgGitHubProvider(ctx, authz.GetCtxData(ctx).OrgID, addGitHubProviderToCommand(req)) + if err != nil { + return nil, err + } + return &mgmt_pb.AddGitHubProviderResponse{ + Id: id, + Details: object_pb.DomainToAddDetailsPb(details), + }, nil +} + +func (s *Server) UpdateGitHubProvider(ctx context.Context, req *mgmt_pb.UpdateGitHubProviderRequest) (*mgmt_pb.UpdateGitHubProviderResponse, error) { + details, err := s.command.UpdateOrgGitHubProvider(ctx, authz.GetCtxData(ctx).OrgID, req.Id, updateGitHubProviderToCommand(req)) + if err != nil { + return nil, err + } + return &mgmt_pb.UpdateGitHubProviderResponse{ + Details: object_pb.DomainToChangeDetailsPb(details), + }, nil +} + +func (s *Server) AddGitHubEnterpriseServerProvider(ctx context.Context, req *mgmt_pb.AddGitHubEnterpriseServerProviderRequest) (*mgmt_pb.AddGitHubEnterpriseServerProviderResponse, error) { + id, details, err := s.command.AddOrgGitHubEnterpriseProvider(ctx, authz.GetCtxData(ctx).OrgID, addGitHubEnterpriseProviderToCommand(req)) + if err != nil { + return nil, err + } + return &mgmt_pb.AddGitHubEnterpriseServerProviderResponse{ + Id: id, + Details: object_pb.DomainToAddDetailsPb(details), + }, nil +} + +func (s *Server) UpdateGitHubEnterpriseServerProvider(ctx context.Context, req *mgmt_pb.UpdateGitHubEnterpriseServerProviderRequest) (*mgmt_pb.UpdateGitHubEnterpriseServerProviderResponse, error) { + details, err := s.command.UpdateOrgGitHubEnterpriseProvider(ctx, authz.GetCtxData(ctx).OrgID, req.Id, updateGitHubEnterpriseProviderToCommand(req)) + if err != nil { + return nil, err + } + return &mgmt_pb.UpdateGitHubEnterpriseServerProviderResponse{ + 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 75d8e2bab9..b402ee5e07 100644 --- a/internal/api/grpc/management/idp_converter.go +++ b/internal/api/grpc/management/idp_converter.go @@ -290,6 +290,52 @@ func updateJWTProviderToCommand(req *mgmt_pb.UpdateJWTProviderRequest) command.J } } +func addGitHubProviderToCommand(req *mgmt_pb.AddGitHubProviderRequest) command.GitHubProvider { + return command.GitHubProvider{ + Name: req.Name, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + Scopes: req.Scopes, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + } +} + +func updateGitHubProviderToCommand(req *mgmt_pb.UpdateGitHubProviderRequest) command.GitHubProvider { + return command.GitHubProvider{ + Name: req.Name, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + Scopes: req.Scopes, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + } +} + +func addGitHubEnterpriseProviderToCommand(req *mgmt_pb.AddGitHubEnterpriseServerProviderRequest) command.GitHubEnterpriseProvider { + return command.GitHubEnterpriseProvider{ + Name: req.Name, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + AuthorizationEndpoint: req.AuthorizationEndpoint, + TokenEndpoint: req.TokenEndpoint, + UserEndpoint: req.UserEndpoint, + Scopes: req.Scopes, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + } +} + +func updateGitHubEnterpriseProviderToCommand(req *mgmt_pb.UpdateGitHubEnterpriseServerProviderRequest) command.GitHubEnterpriseProvider { + return command.GitHubEnterpriseProvider{ + Name: req.Name, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + AuthorizationEndpoint: req.AuthorizationEndpoint, + TokenEndpoint: req.TokenEndpoint, + UserEndpoint: req.UserEndpoint, + 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 6bd0ccf0bc..42ee14396d 100644 --- a/internal/api/ui/login/external_provider_handler.go +++ b/internal/api/ui/login/external_provider_handler.go @@ -18,6 +18,7 @@ import ( "github.com/zitadel/zitadel/internal/errors" "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/google" "github.com/zitadel/zitadel/internal/idp/providers/jwt" "github.com/zitadel/zitadel/internal/idp/providers/oauth" @@ -141,12 +142,14 @@ func (l *Login) handleIDP(w http.ResponseWriter, r *http.Request, authReq *domai provider, err = l.oidcProvider(r.Context(), identityProvider) case domain.IDPTypeJWT: provider, err = l.jwtProvider(identityProvider) + case domain.IDPTypeGitHub: + provider, err = l.githubProvider(r.Context(), identityProvider) + case domain.IDPTypeGitHubEnterprise: + provider, err = l.githubEnterpriseProvider(r.Context(), identityProvider) case domain.IDPTypeGoogle: provider, err = l.googleProvider(r.Context(), identityProvider) case domain.IDPTypeLDAP, domain.IDPTypeAzureAD, - domain.IDPTypeGitHub, - domain.IDPTypeGitHubEE, domain.IDPTypeGitLab, domain.IDPTypeGitLabSelfHosted, domain.IDPTypeUnspecified: @@ -204,6 +207,20 @@ func (l *Login) handleExternalLoginCallback(w http.ResponseWriter, r *http.Reque return } session = &openid.Session{Provider: provider.(*openid.Provider), Code: data.Code} + case domain.IDPTypeGitHub: + provider, err = l.githubProvider(r.Context(), identityProvider) + if err != nil { + l.externalAuthFailed(w, r, authReq, nil, nil, err) + return + } + session = &oauth.Session{Provider: provider.(*github.Provider).Provider, Code: data.Code} + case domain.IDPTypeGitHubEnterprise: + provider, err = l.githubEnterpriseProvider(r.Context(), identityProvider) + if err != nil { + l.externalAuthFailed(w, r, authReq, nil, nil, err) + return + } + session = &oauth.Session{Provider: provider.(*github.Provider).Provider, Code: data.Code} case domain.IDPTypeGoogle: provider, err = l.googleProvider(r.Context(), identityProvider) if err != nil { @@ -214,8 +231,6 @@ func (l *Login) handleExternalLoginCallback(w http.ResponseWriter, r *http.Reque case domain.IDPTypeJWT, domain.IDPTypeLDAP, domain.IDPTypeAzureAD, - domain.IDPTypeGitHub, - domain.IDPTypeGitHubEE, domain.IDPTypeGitLab, domain.IDPTypeGitLabSelfHosted, domain.IDPTypeUnspecified: @@ -633,6 +648,36 @@ func (l *Login) oauthProvider(ctx context.Context, identityProvider *query.IDPTe ) } +func (l *Login) githubProvider(ctx context.Context, identityProvider *query.IDPTemplate) (*github.Provider, error) { + secret, err := crypto.DecryptString(identityProvider.GitHubIDPTemplate.ClientSecret, l.idpConfigAlg) + if err != nil { + return nil, err + } + return github.New( + identityProvider.GitHubIDPTemplate.ClientID, + secret, + l.baseURL(ctx)+EndpointExternalLoginCallback, + identityProvider.GitHubIDPTemplate.Scopes, + ) +} + +func (l *Login) githubEnterpriseProvider(ctx context.Context, identityProvider *query.IDPTemplate) (*github.Provider, error) { + secret, err := crypto.DecryptString(identityProvider.GitHubIDPTemplate.ClientSecret, l.idpConfigAlg) + if err != nil { + return nil, err + } + return github.NewCustomURL( + identityProvider.Name, + identityProvider.GitHubIDPTemplate.ClientID, + secret, + l.baseURL(ctx)+EndpointExternalLoginCallback, + identityProvider.GitHubEnterpriseIDPTemplate.AuthorizationEndpoint, + identityProvider.GitHubEnterpriseIDPTemplate.TokenEndpoint, + identityProvider.GitHubEnterpriseIDPTemplate.UserEndpoint, + identityProvider.GitHubIDPTemplate.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/github-white.png b/internal/api/ui/login/static/resources/images/idp/github-white.png new file mode 100644 index 0000000000..50b8175227 Binary files /dev/null and b/internal/api/ui/login/static/resources/images/idp/github-white.png differ diff --git a/internal/api/ui/login/static/resources/images/idp/github.png b/internal/api/ui/login/static/resources/images/idp/github.png new file mode 100644 index 0000000000..6cb3b705d0 Binary files /dev/null and b/internal/api/ui/login/static/resources/images/idp/github.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 fb1185e671..b459308b69 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 @@ -3,6 +3,7 @@ $lgn-idp-padding: 0 1px; $lgn-idp-provider-name-line-height: 36px; $lgn-idp-border-radius: .5rem; $googlelogosource: '../../../images/idp/google'; +$githublogosource: '../../../images/idp/github'; @mixin lgn-idp-base { display: block; @@ -39,4 +40,16 @@ $googlelogosource: '../../../images/idp/google'; border-radius: 5px; } } + + &.github { + span.logo { + height: 46px; + width: 46px; + background-image: url($githublogosource + '.png'); + background-size: 25px; + 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 bf340a0465..2c4c746228 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 @@ -21,6 +21,11 @@ color: var(--zitadel-color-google-text); background-color: var(--zitadel-color-google-background); } + + &.github { + color: var(--zitadel-color-github-text); + background-color: var(--zitadel-color-github-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 bb018f07ca..8f3582c38a 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 @@ -113,6 +113,8 @@ --zitadel-color-google-text: #8b8d8d; --zitadel-color-google-background: #ffffff; + --zitadel-color-github-text: #8b8d8d; + --zitadel-color-github-background: #ffffff; --zitadel-color-qr: var(--zitadel-color-black); --zitadel-color-qr-background: var(--zitadel-color-white); @@ -214,4 +216,6 @@ --zitadel-color-google-text: #8b8d8d; --zitadel-color-google-background: #ffffff; + --zitadel-color-github-text: #8b8d8d; + --zitadel-color-github-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 51524740f9..bce99cadb6 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 @@ -98,6 +98,8 @@ --zitadel-logo-powered-by: url("../logo-dark.svg"); --zitadel-color-google-text: #8b8d8d; --zitadel-color-google-background: #ffffff; + --zitadel-color-github-text: #8b8d8d; + --zitadel-color-github-background: #ffffff; --zitadel-color-qr: var(--zitadel-color-black); --zitadel-color-qr-background: var(--zitadel-color-white); } @@ -184,6 +186,8 @@ --zitadel-logo-powered-by: url("../logo-light.svg"); --zitadel-color-google-text: #8b8d8d; --zitadel-color-google-background: #ffffff; + --zitadel-color-github-text: #8b8d8d; + --zitadel-color-github-background: #ffffff; } body { @@ -210,7 +214,7 @@ body.waiting * { footer { width: 100%; box-sizing: border-box; - background: #00000020; + background: rgba(0, 0, 0, 0.1254901961); min-height: 50px; display: flex; align-items: center; @@ -559,6 +563,15 @@ a.sub-formfield-link { background-repeat: no-repeat; border-radius: 5px; } +.lgn-idp.github span.logo { + height: 46px; + width: 46px; + background-image: url("../../../images/idp/github.png"); + background-size: 25px; + background-position: center; + background-repeat: no-repeat; + border-radius: 5px; +} .lgn-error { display: flex; @@ -733,7 +746,7 @@ i { letter-spacing: 0.05em; font-size: 12px; white-space: nowrap; - box-shadow: 0 0 3px #0000001a; + box-shadow: 0 0 3px rgba(0, 0, 0, 0.1019607843); width: fit-content; line-height: 1rem; } @@ -1185,7 +1198,7 @@ i { footer { width: 100%; box-sizing: border-box; - background: #00000020; + background: rgba(0, 0, 0, 0.1254901961); min-height: 50px; display: flex; align-items: center; @@ -1534,6 +1547,15 @@ a.sub-formfield-link { background-repeat: no-repeat; border-radius: 5px; } +.lgn-idp.github span.logo { + height: 46px; + width: 46px; + background-image: url("../../../images/idp/github.png"); + background-size: 25px; + background-position: center; + background-repeat: no-repeat; + border-radius: 5px; +} .lgn-error { display: flex; @@ -1708,7 +1730,7 @@ i { letter-spacing: 0.05em; font-size: 12px; white-space: nowrap; - box-shadow: 0 0 3px #0000001a; + box-shadow: 0 0 3px rgba(0, 0, 0, 0.1019607843); width: fit-content; line-height: 1rem; } @@ -3047,6 +3069,10 @@ ul li i.lgn-valid { color: var(--zitadel-color-google-text); background-color: var(--zitadel-color-google-background); } +.lgn-idp.github { + color: var(--zitadel-color-github-text); + background-color: var(--zitadel-color-github-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 9ef33a4156..8619068f9c 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;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;;;ACvNF;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;ECKI;EACA,QARa;EASb;EACA;EACA;EACA;EACA,SAZc;EAad,eAXoB;EAYpB;EACA;;AAEA;EACI;EACA;;AAGJ;EACI,aAtB4B;EAuB5B;EACA;EACA;;AAIA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;ACtCZ;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;ECKI;EACA,QARa;EASb;EACA;EACA;EACA;EACA,SAZc;EAad,eAXoB;EAYpB;EACA;;AAEA;EACI;EACA;;AAGJ;EACI,aAtB4B;EAuB5B;EACA;EACA;;AAIA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;ACtCZ;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;;;AAKF;EACE;;;ACnBJ;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;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 diff --git a/internal/command/idp.go b/internal/command/idp.go index d0add970aa..360ca6a9af 100644 --- a/internal/command/idp.go +++ b/internal/command/idp.go @@ -38,6 +38,25 @@ type JWTProvider struct { IDPOptions idp.Options } +type GitHubProvider struct { + Name string + ClientID string + ClientSecret string + Scopes []string + IDPOptions idp.Options +} + +type GitHubEnterpriseProvider struct { + Name string + ClientID string + ClientSecret string + AuthorizationEndpoint string + TokenEndpoint string + UserEndpoint 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 86a7543241..4d253abb94 100644 --- a/internal/command/idp_model.go +++ b/internal/command/idp_model.go @@ -50,6 +50,7 @@ func (wm *OAuthIDPWriteModel) reduceAddedEvent(e *idp.OAuthIDPAddedEvent) { wm.UserEndpoint = e.UserEndpoint wm.Scopes = e.Scopes wm.IDAttribute = e.IDAttribute + wm.Options = e.Options wm.State = domain.IDPStateActive } @@ -412,6 +413,206 @@ func (wm *JWTIDPWriteModel) reduceJWTConfigChangedEvent(e *idpconfig.JWTConfigCh } } +type GitHubIDPWriteModel struct { + eventstore.WriteModel + + ID string + Name string + ClientID string + ClientSecret *crypto.CryptoValue + Scopes []string + idp.Options + + State domain.IDPState +} + +func (wm *GitHubIDPWriteModel) Reduce() error { + for _, event := range wm.Events { + switch e := event.(type) { + case *idp.GitHubIDPAddedEvent: + wm.reduceAddedEvent(e) + case *idp.GitHubIDPChangedEvent: + wm.reduceChangedEvent(e) + case *idp.RemovedEvent: + wm.State = domain.IDPStateRemoved + } + } + return wm.WriteModel.Reduce() +} + +func (wm *GitHubIDPWriteModel) reduceAddedEvent(e *idp.GitHubIDPAddedEvent) { + 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 *GitHubIDPWriteModel) reduceChangedEvent(e *idp.GitHubIDPChangedEvent) { + 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 *GitHubIDPWriteModel) NewChanges( + name, + clientID, + clientSecretString string, + secretCrypto crypto.Crypto, + scopes []string, + options idp.Options, +) ([]idp.GitHubIDPChanges, error) { + changes := make([]idp.GitHubIDPChanges, 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.ChangeGitHubClientSecret(clientSecret)) + } + if wm.Name != name { + changes = append(changes, idp.ChangeGitHubName(name)) + } + if wm.ClientID != clientID { + changes = append(changes, idp.ChangeGitHubClientID(clientID)) + } + if !reflect.DeepEqual(wm.Scopes, scopes) { + changes = append(changes, idp.ChangeGitHubScopes(scopes)) + } + + opts := wm.Options.Changes(options) + if !opts.IsZero() { + changes = append(changes, idp.ChangeGitHubOptions(opts)) + } + return changes, nil +} + +type GitHubEnterpriseIDPWriteModel struct { + eventstore.WriteModel + + ID string + Name string + ClientID string + ClientSecret *crypto.CryptoValue + AuthorizationEndpoint string + TokenEndpoint string + UserEndpoint string + Scopes []string + idp.Options + + State domain.IDPState +} + +func (wm *GitHubEnterpriseIDPWriteModel) Reduce() error { + for _, event := range wm.Events { + switch e := event.(type) { + case *idp.GitHubEnterpriseIDPAddedEvent: + wm.reduceAddedEvent(e) + case *idp.GitHubEnterpriseIDPChangedEvent: + wm.reduceChangedEvent(e) + case *idp.RemovedEvent: + wm.State = domain.IDPStateRemoved + } + } + return wm.WriteModel.Reduce() +} + +func (wm *GitHubEnterpriseIDPWriteModel) reduceAddedEvent(e *idp.GitHubEnterpriseIDPAddedEvent) { + wm.Name = e.Name + wm.ClientID = e.ClientID + wm.ClientSecret = e.ClientSecret + wm.AuthorizationEndpoint = e.AuthorizationEndpoint + wm.TokenEndpoint = e.TokenEndpoint + wm.UserEndpoint = e.UserEndpoint + wm.Scopes = e.Scopes + wm.Options = e.Options + wm.State = domain.IDPStateActive +} + +func (wm *GitHubEnterpriseIDPWriteModel) reduceChangedEvent(e *idp.GitHubEnterpriseIDPChangedEvent) { + 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.AuthorizationEndpoint != nil { + wm.AuthorizationEndpoint = *e.AuthorizationEndpoint + } + if e.TokenEndpoint != nil { + wm.TokenEndpoint = *e.TokenEndpoint + } + if e.UserEndpoint != nil { + wm.UserEndpoint = *e.UserEndpoint + } + if e.Scopes != nil { + wm.Scopes = e.Scopes + } + wm.Options.ReduceChanges(e.OptionChanges) +} + +func (wm *GitHubEnterpriseIDPWriteModel) NewChanges( + name, + clientID string, + clientSecretString string, + secretCrypto crypto.Crypto, + authorizationEndpoint, + tokenEndpoint, + userEndpoint string, + scopes []string, + options idp.Options, +) ([]idp.GitHubEnterpriseIDPChanges, error) { + changes := make([]idp.GitHubEnterpriseIDPChanges, 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.ChangeGitHubEnterpriseClientSecret(clientSecret)) + } + if wm.ClientID != clientID { + changes = append(changes, idp.ChangeGitHubEnterpriseClientID(clientID)) + } + if wm.Name != name { + changes = append(changes, idp.ChangeGitHubEnterpriseName(name)) + } + if wm.AuthorizationEndpoint != authorizationEndpoint { + changes = append(changes, idp.ChangeGitHubEnterpriseAuthorizationEndpoint(authorizationEndpoint)) + } + if wm.TokenEndpoint != tokenEndpoint { + changes = append(changes, idp.ChangeGitHubEnterpriseTokenEndpoint(tokenEndpoint)) + } + if wm.UserEndpoint != userEndpoint { + changes = append(changes, idp.ChangeGitHubEnterpriseUserEndpoint(userEndpoint)) + } + if !reflect.DeepEqual(wm.Scopes, scopes) { + changes = append(changes, idp.ChangeGitHubEnterpriseScopes(scopes)) + } + opts := wm.Options.Changes(options) + if !opts.IsZero() { + changes = append(changes, idp.ChangeGitHubEnterpriseOptions(opts)) + } + return changes, nil +} + type GoogleIDPWriteModel struct { eventstore.WriteModel @@ -666,6 +867,10 @@ func (wm *IDPRemoveWriteModel) Reduce() error { wm.reduceAdded(e.ID) case *idp.JWTIDPAddedEvent: wm.reduceAdded(e.ID) + case *idp.GitHubIDPAddedEvent: + wm.reduceAdded(e.ID) + case *idp.GitHubEnterpriseIDPAddedEvent: + 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 80f49711e4..15c38a8f25 100644 --- a/internal/command/instance_idp.go +++ b/internal/command/instance_idp.go @@ -139,6 +139,90 @@ func (c *Commands) UpdateInstanceJWTProvider(ctx context.Context, id string, pro return pushedEventsToObjectDetails(pushedEvents), nil } +func (c *Commands) AddInstanceGitHubProvider(ctx context.Context, provider GitHubProvider) (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 := NewGitHubInstanceIDPWriteModel(instanceID, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareAddInstanceGitHubProvider(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) UpdateInstanceGitHubProvider(ctx context.Context, id string, provider GitHubProvider) (*domain.ObjectDetails, error) { + instanceID := authz.GetInstance(ctx).InstanceID() + instanceAgg := instance.NewAggregate(instanceID) + writeModel := NewGitHubInstanceIDPWriteModel(instanceID, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareUpdateInstanceGitHubProvider(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) AddInstanceGitHubEnterpriseProvider(ctx context.Context, provider GitHubEnterpriseProvider) (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 := NewGitHubEnterpriseInstanceIDPWriteModel(instanceID, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareAddInstanceGitHubEnterpriseProvider(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) UpdateInstanceGitHubEnterpriseProvider(ctx context.Context, id string, provider GitHubEnterpriseProvider) (*domain.ObjectDetails, error) { + instanceID := authz.GetInstance(ctx).InstanceID() + instanceAgg := instance.NewAggregate(instanceID) + writeModel := NewGitHubEnterpriseInstanceIDPWriteModel(instanceID, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareUpdateInstanceGitHubEnterpriseProvider(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) @@ -548,6 +632,194 @@ func (c *Commands) prepareUpdateInstanceJWTProvider(a *instance.Aggregate, write } } +func (c *Commands) prepareAddInstanceGitHubProvider(a *instance.Aggregate, writeModel *InstanceGitHubIDPWriteModel, provider GitHubProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-Jdsgf", "Errors.Invalid.Argument") + } + if provider.ClientSecret = strings.TrimSpace(provider.ClientSecret); provider.ClientSecret == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-dsgz3", "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.NewGitHubIDPAddedEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.ClientID, + secret, + provider.Scopes, + provider.IDPOptions, + ), + }, nil + }, nil + } +} + +func (c *Commands) prepareUpdateInstanceGitHubProvider(a *instance.Aggregate, writeModel *InstanceGitHubIDPWriteModel, provider GitHubProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if writeModel.ID = strings.TrimSpace(writeModel.ID); writeModel.ID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-sdf4h", "Errors.Invalid.Argument") + } + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-fdh5z", "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-Dr1gs", "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 { + return nil, err + } + if event == nil { + return nil, nil + } + return []eventstore.Command{event}, nil + }, nil + } +} + +func (c *Commands) prepareAddInstanceGitHubEnterpriseProvider(a *instance.Aggregate, writeModel *InstanceGitHubEnterpriseIDPWriteModel, provider GitHubEnterpriseProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-Dg4td", "Errors.Invalid.Argument") + } + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-dgj53", "Errors.Invalid.Argument") + } + if provider.ClientSecret = strings.TrimSpace(provider.ClientSecret); provider.ClientSecret == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-Ghjjs", "Errors.Invalid.Argument") + } + if provider.AuthorizationEndpoint = strings.TrimSpace(provider.AuthorizationEndpoint); provider.AuthorizationEndpoint == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-sani2", "Errors.Invalid.Argument") + } + if provider.TokenEndpoint = strings.TrimSpace(provider.TokenEndpoint); provider.TokenEndpoint == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-agj42", "Errors.Invalid.Argument") + } + if provider.UserEndpoint = strings.TrimSpace(provider.UserEndpoint); provider.UserEndpoint == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-sd5hn", "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.NewGitHubEnterpriseIDPAddedEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.ClientID, + secret, + provider.AuthorizationEndpoint, + provider.TokenEndpoint, + provider.UserEndpoint, + provider.Scopes, + provider.IDPOptions, + ), + }, nil + }, nil + } +} + +func (c *Commands) prepareUpdateInstanceGitHubEnterpriseProvider(a *instance.Aggregate, writeModel *InstanceGitHubEnterpriseIDPWriteModel, provider GitHubEnterpriseProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if writeModel.ID = strings.TrimSpace(writeModel.ID); writeModel.ID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-sdfh3", "Errors.Invalid.Argument") + } + if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-shj42", "Errors.Invalid.Argument") + } + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-sdh73", "Errors.Invalid.Argument") + } + if provider.AuthorizationEndpoint = strings.TrimSpace(provider.AuthorizationEndpoint); provider.AuthorizationEndpoint == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-acx2w", "Errors.Invalid.Argument") + } + if provider.TokenEndpoint = strings.TrimSpace(provider.TokenEndpoint); provider.TokenEndpoint == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-dgj6q", "Errors.Invalid.Argument") + } + if provider.UserEndpoint = strings.TrimSpace(provider.UserEndpoint); provider.UserEndpoint == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-ybj62", "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-GBr42", "Errors.Instance.IDPConfig.NotExisting") + } + event, err := writeModel.NewChangedEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.ClientID, + provider.ClientSecret, + c.idpConfigEncryption, + provider.AuthorizationEndpoint, + provider.TokenEndpoint, + provider.UserEndpoint, + 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) prepareAddInstanceGoogleProvider(a *instance.Aggregate, writeModel *InstanceGoogleIDPWriteModel, provider GoogleProvider) preparation.Validation { return func() (preparation.CreateCommands, error) { if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { diff --git a/internal/command/instance_idp_model.go b/internal/command/instance_idp_model.go index 113d764fb0..e18d6ee9f1 100644 --- a/internal/command/instance_idp_model.go +++ b/internal/command/instance_idp_model.go @@ -291,6 +291,157 @@ func (wm *InstanceJWTIDPWriteModel) NewChangedEvent( return instance.NewJWTIDPChangedEvent(ctx, aggregate, id, changes) } +type InstanceGitHubIDPWriteModel struct { + GitHubIDPWriteModel +} + +func NewGitHubInstanceIDPWriteModel(instanceID, id string) *InstanceGitHubIDPWriteModel { + return &InstanceGitHubIDPWriteModel{ + GitHubIDPWriteModel{ + WriteModel: eventstore.WriteModel{ + AggregateID: instanceID, + ResourceOwner: instanceID, + }, + ID: id, + }, + } +} + +func (wm *InstanceGitHubIDPWriteModel) Reduce() error { + return wm.GitHubIDPWriteModel.Reduce() +} + +func (wm *InstanceGitHubIDPWriteModel) AppendEvents(events ...eventstore.Event) { + for _, event := range events { + switch e := event.(type) { + case *instance.GitHubIDPAddedEvent: + wm.GitHubIDPWriteModel.AppendEvents(&e.GitHubIDPAddedEvent) + case *instance.GitHubIDPChangedEvent: + wm.GitHubIDPWriteModel.AppendEvents(&e.GitHubIDPChangedEvent) + case *instance.IDPRemovedEvent: + wm.GitHubIDPWriteModel.AppendEvents(&e.RemovedEvent) + default: + wm.GitHubIDPWriteModel.AppendEvents(e) + } + } +} + +func (wm *InstanceGitHubIDPWriteModel) Query() *eventstore.SearchQueryBuilder { + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + ResourceOwner(wm.ResourceOwner). + AddQuery(). + AggregateTypes(instance.AggregateType). + AggregateIDs(wm.AggregateID). + EventTypes( + instance.GitHubIDPAddedEventType, + instance.GitHubIDPChangedEventType, + instance.IDPRemovedEventType, + ). + EventData(map[string]interface{}{"id": wm.ID}). + Builder() +} + +func (wm *InstanceGitHubIDPWriteModel) NewChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + clientID string, + clientSecretString string, + secretCrypto crypto.Crypto, + scopes []string, + options idp.Options, +) (*instance.GitHubIDPChangedEvent, error) { + + changes, err := wm.GitHubIDPWriteModel.NewChanges(name, clientID, clientSecretString, secretCrypto, scopes, options) + if err != nil || len(changes) == 0 { + return nil, err + } + return instance.NewGitHubIDPChangedEvent(ctx, aggregate, id, changes) +} + +type InstanceGitHubEnterpriseIDPWriteModel struct { + GitHubEnterpriseIDPWriteModel +} + +func NewGitHubEnterpriseInstanceIDPWriteModel(instanceID, id string) *InstanceGitHubEnterpriseIDPWriteModel { + return &InstanceGitHubEnterpriseIDPWriteModel{ + GitHubEnterpriseIDPWriteModel{ + WriteModel: eventstore.WriteModel{ + AggregateID: instanceID, + ResourceOwner: instanceID, + }, + ID: id, + }, + } +} + +func (wm *InstanceGitHubEnterpriseIDPWriteModel) Reduce() error { + return wm.GitHubEnterpriseIDPWriteModel.Reduce() +} + +func (wm *InstanceGitHubEnterpriseIDPWriteModel) AppendEvents(events ...eventstore.Event) { + for _, event := range events { + switch e := event.(type) { + case *instance.GitHubEnterpriseIDPAddedEvent: + wm.GitHubEnterpriseIDPWriteModel.AppendEvents(&e.GitHubEnterpriseIDPAddedEvent) + case *instance.GitHubEnterpriseIDPChangedEvent: + wm.GitHubEnterpriseIDPWriteModel.AppendEvents(&e.GitHubEnterpriseIDPChangedEvent) + case *instance.IDPRemovedEvent: + wm.GitHubEnterpriseIDPWriteModel.AppendEvents(&e.RemovedEvent) + default: + wm.GitHubEnterpriseIDPWriteModel.AppendEvents(e) + } + } +} + +func (wm *InstanceGitHubEnterpriseIDPWriteModel) Query() *eventstore.SearchQueryBuilder { + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + ResourceOwner(wm.ResourceOwner). + AddQuery(). + AggregateTypes(instance.AggregateType). + AggregateIDs(wm.AggregateID). + EventTypes( + instance.GitHubEnterpriseIDPAddedEventType, + instance.GitHubEnterpriseIDPChangedEventType, + instance.IDPRemovedEventType, + ). + EventData(map[string]interface{}{"id": wm.ID}). + Builder() +} + +func (wm *InstanceGitHubEnterpriseIDPWriteModel) NewChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + clientID string, + clientSecretString string, + secretCrypto crypto.Crypto, + authorizationEndpoint, + tokenEndpoint, + userEndpoint string, + scopes []string, + options idp.Options, +) (*instance.GitHubEnterpriseIDPChangedEvent, error) { + + changes, err := wm.GitHubEnterpriseIDPWriteModel.NewChanges( + name, + clientID, + clientSecretString, + secretCrypto, + authorizationEndpoint, + tokenEndpoint, + userEndpoint, + scopes, + options, + ) + if err != nil || len(changes) == 0 { + return nil, err + } + return instance.NewGitHubEnterpriseIDPChangedEvent(ctx, aggregate, id, changes) +} + type InstanceGoogleIDPWriteModel struct { GoogleIDPWriteModel } @@ -476,6 +627,10 @@ func (wm *InstanceIDPRemoveWriteModel) AppendEvents(events ...eventstore.Event) wm.IDPRemoveWriteModel.AppendEvents(&e.OIDCIDPAddedEvent) case *instance.JWTIDPAddedEvent: wm.IDPRemoveWriteModel.AppendEvents(&e.JWTIDPAddedEvent) + case *instance.GitHubIDPAddedEvent: + wm.IDPRemoveWriteModel.AppendEvents(&e.GitHubIDPAddedEvent) + case *instance.GitHubEnterpriseIDPAddedEvent: + wm.IDPRemoveWriteModel.AppendEvents(&e.GitHubEnterpriseIDPAddedEvent) case *instance.GoogleIDPAddedEvent: wm.IDPRemoveWriteModel.AppendEvents(&e.GoogleIDPAddedEvent) case *instance.LDAPIDPAddedEvent: @@ -502,6 +657,8 @@ func (wm *InstanceIDPRemoveWriteModel) Query() *eventstore.SearchQueryBuilder { instance.OAuthIDPAddedEventType, instance.OIDCIDPAddedEventType, instance.JWTIDPAddedEventType, + instance.GitHubIDPAddedEventType, + instance.GitHubEnterpriseIDPAddedEventType, instance.GoogleIDPAddedEventType, instance.LDAPIDPAddedEventType, instance.IDPRemovedEventType, diff --git a/internal/command/instance_idp_test.go b/internal/command/instance_idp_test.go index 6105641a8c..6ade24c2ad 100644 --- a/internal/command/instance_idp_test.go +++ b/internal/command/instance_idp_test.go @@ -607,6 +607,902 @@ func TestCommandSide_UpdateInstanceGenericOAuthIDP(t *testing.T) { } } +func TestCommandSide_AddInstanceGitHubIDP(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + idGenerator id.Generator + secretCrypto crypto.EncryptionAlgorithm + } + type args struct { + ctx context.Context + provider GitHubProvider + } + type res struct { + id string + want *domain.ObjectDetails + err func(error) bool + } + tests := []struct { + name string + fields fields + args args + res res + }{ + { + "invalid client id", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitHubProvider{}, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid client secret", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitHubProvider{ + ClientID: "clientID", + }, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + name: "ok", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter(), + expectPush( + []*repository.Event{ + eventFromEventPusherWithInstanceID( + "instance1", + instance.NewGitHubIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + idp.Options{}, + )), + }, + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitHubProvider{ + 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.NewGitHubIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid"}, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + )), + }, + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitHubProvider{ + Name: "name", + 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.AddInstanceGitHubProvider(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_UpdateInstanceGitHubIDP(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + secretCrypto crypto.EncryptionAlgorithm + } + type args struct { + ctx context.Context + id string + provider GitHubProvider + } + 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: GitHubProvider{}, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid client id", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GitHubProvider{}, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + name: "not found", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter(), + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GitHubProvider{ + ClientID: "clientID", + }, + }, + res: res{ + err: caos_errors.IsNotFound, + }, + }, + { + name: "no changes", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + instance.NewGitHubIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + idp.Options{}, + )), + ), + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GitHubProvider{ + ClientID: "clientID", + }, + }, + res: res{ + want: &domain.ObjectDetails{ResourceOwner: "instance1"}, + }, + }, + { + name: "change ok", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + instance.NewGitHubIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "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.NewGitHubIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + []idp.GitHubIDPChanges{ + idp.ChangeGitHubName("new name"), + idp.ChangeGitHubClientID("new clientID"), + idp.ChangeGitHubClientSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("new clientSecret"), + }), + idp.ChangeGitHubScopes([]string{"openid", "profile"}), + idp.ChangeGitHubOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), + ), + }, + ), + ), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GitHubProvider{ + Name: "new name", + ClientID: "new clientID", + ClientSecret: "new clientSecret", + 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.UpdateInstanceGitHubProvider(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_AddInstanceGitHubEnterpriseIDP(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + idGenerator id.Generator + secretCrypto crypto.EncryptionAlgorithm + } + type args struct { + ctx context.Context + provider GitHubEnterpriseProvider + } + 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: GitHubEnterpriseProvider{}, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid clientID", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitHubEnterpriseProvider{ + Name: "name", + }, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid clientSecret", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitHubEnterpriseProvider{ + Name: "name", + ClientID: "clientID", + }, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid auth endpoint", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitHubEnterpriseProvider{ + Name: "name", + ClientID: "clientID", + ClientSecret: "clientSecret", + }, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid token endpoint", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitHubEnterpriseProvider{ + Name: "name", + ClientID: "clientID", + ClientSecret: "clientSecret", + AuthorizationEndpoint: "auth", + }, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid user endpoint", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitHubEnterpriseProvider{ + Name: "name", + ClientID: "clientID", + ClientSecret: "clientSecret", + AuthorizationEndpoint: "auth", + TokenEndpoint: "token", + }, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + name: "ok", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter(), + expectPush( + []*repository.Event{ + eventFromEventPusherWithInstanceID( + "instance1", + instance.NewGitHubEnterpriseIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + "auth", + "token", + "user", + nil, + idp.Options{}, + )), + }, + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitHubEnterpriseProvider{ + Name: "name", + ClientID: "clientID", + ClientSecret: "clientSecret", + AuthorizationEndpoint: "auth", + TokenEndpoint: "token", + UserEndpoint: "user", + }, + }, + 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.NewGitHubEnterpriseIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + "auth", + "token", + "user", + []string{"user"}, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + )), + }, + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GitHubEnterpriseProvider{ + Name: "name", + ClientID: "clientID", + ClientSecret: "clientSecret", + AuthorizationEndpoint: "auth", + TokenEndpoint: "token", + UserEndpoint: "user", + Scopes: []string{"user"}, + 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.AddInstanceGitHubEnterpriseProvider(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_UpdateInstanceGitHubEnterpriseIDP(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + secretCrypto crypto.EncryptionAlgorithm + } + type args struct { + ctx context.Context + id string + provider GitHubEnterpriseProvider + } + 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: GitHubEnterpriseProvider{}, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid name", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GitHubEnterpriseProvider{}, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid clientID", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GitHubEnterpriseProvider{ + Name: "name", + }, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid auth endpoint", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GitHubEnterpriseProvider{ + Name: "name", + }, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid token endpoint", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GitHubEnterpriseProvider{ + Name: "name", + ClientID: "clientID", + AuthorizationEndpoint: "auth", + }, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid user endpoint", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GitHubEnterpriseProvider{ + Name: "name", + ClientID: "clientID", + AuthorizationEndpoint: "auth", + TokenEndpoint: "token", + }, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + name: "not found", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter(), + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GitHubEnterpriseProvider{ + Name: "name", + ClientID: "clientID", + AuthorizationEndpoint: "auth", + TokenEndpoint: "token", + UserEndpoint: "user", + }, + }, + res: res{ + err: caos_errors.IsNotFound, + }, + }, + { + name: "no changes", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + instance.NewGitHubEnterpriseIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + "auth", + "token", + "user", + nil, + idp.Options{}, + )), + ), + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GitHubEnterpriseProvider{ + Name: "name", + ClientID: "clientID", + AuthorizationEndpoint: "auth", + TokenEndpoint: "token", + UserEndpoint: "user", + }, + }, + res: res{ + want: &domain.ObjectDetails{ResourceOwner: "instance1"}, + }, + }, + { + name: "change ok", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + instance.NewGitHubEnterpriseIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + "auth", + "token", + "user", + nil, + idp.Options{}, + )), + ), + expectPush( + []*repository.Event{ + eventFromEventPusherWithInstanceID( + "instance1", + func() eventstore.Command { + t := true + event, _ := instance.NewGitHubEnterpriseIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + []idp.GitHubEnterpriseIDPChanges{ + idp.ChangeGitHubEnterpriseName("new name"), + idp.ChangeGitHubEnterpriseClientID("clientID2"), + idp.ChangeGitHubEnterpriseClientSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("newSecret"), + }), + idp.ChangeGitHubEnterpriseAuthorizationEndpoint("new auth"), + idp.ChangeGitHubEnterpriseTokenEndpoint("new token"), + idp.ChangeGitHubEnterpriseUserEndpoint("new user"), + idp.ChangeGitHubEnterpriseScopes([]string{"openid", "profile"}), + idp.ChangeGitHubEnterpriseOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), + ), + }, + ), + ), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GitHubEnterpriseProvider{ + Name: "new name", + ClientID: "clientID2", + ClientSecret: "newSecret", + AuthorizationEndpoint: "new auth", + TokenEndpoint: "new token", + UserEndpoint: "new user", + 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.UpdateInstanceGitHubEnterpriseProvider(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 diff --git a/internal/command/org_idp.go b/internal/command/org_idp.go index 7717736763..b244d7d33a 100644 --- a/internal/command/org_idp.go +++ b/internal/command/org_idp.go @@ -132,6 +132,86 @@ func (c *Commands) UpdateOrgJWTProvider(ctx context.Context, resourceOwner, id s return pushedEventsToObjectDetails(pushedEvents), nil } +func (c *Commands) AddOrgGitHubProvider(ctx context.Context, resourceOwner string, provider GitHubProvider) (string, *domain.ObjectDetails, error) { + orgAgg := org.NewAggregate(resourceOwner) + id, err := c.idGenerator.Next() + if err != nil { + return "", nil, err + } + writeModel := NewGitHubOrgIDPWriteModel(resourceOwner, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareAddOrgGitHubProvider(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) UpdateOrgGitHubProvider(ctx context.Context, resourceOwner, id string, provider GitHubProvider) (*domain.ObjectDetails, error) { + orgAgg := org.NewAggregate(resourceOwner) + writeModel := NewGitHubOrgIDPWriteModel(resourceOwner, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareUpdateOrgGitHubProvider(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) AddOrgGitHubEnterpriseProvider(ctx context.Context, resourceOwner string, provider GitHubEnterpriseProvider) (string, *domain.ObjectDetails, error) { + orgAgg := org.NewAggregate(resourceOwner) + id, err := c.idGenerator.Next() + if err != nil { + return "", nil, err + } + writeModel := NewGitHubEnterpriseOrgIDPWriteModel(resourceOwner, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareAddOrgGitHubEnterpriseProvider(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) UpdateOrgGitHubEnterpriseProvider(ctx context.Context, resourceOwner, id string, provider GitHubEnterpriseProvider) (*domain.ObjectDetails, error) { + orgAgg := org.NewAggregate(resourceOwner) + writeModel := NewGitHubEnterpriseOrgIDPWriteModel(resourceOwner, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareUpdateOrgGitHubEnterpriseProvider(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() @@ -540,6 +620,194 @@ func (c *Commands) prepareUpdateOrgJWTProvider(a *org.Aggregate, writeModel *Org } } +func (c *Commands) prepareAddOrgGitHubProvider(a *org.Aggregate, writeModel *OrgGitHubIDPWriteModel, provider GitHubProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-Jdsgf", "Errors.Invalid.Argument") + } + if provider.ClientSecret = strings.TrimSpace(provider.ClientSecret); provider.ClientSecret == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-dsgz3", "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.NewGitHubIDPAddedEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.ClientID, + secret, + provider.Scopes, + provider.IDPOptions, + ), + }, nil + }, nil + } +} + +func (c *Commands) prepareUpdateOrgGitHubProvider(a *org.Aggregate, writeModel *OrgGitHubIDPWriteModel, provider GitHubProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if writeModel.ID = strings.TrimSpace(writeModel.ID); writeModel.ID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-sdf4h", "Errors.Invalid.Argument") + } + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-fdh5z", "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-Dr1gs", "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) prepareAddOrgGitHubEnterpriseProvider(a *org.Aggregate, writeModel *OrgGitHubEnterpriseIDPWriteModel, provider GitHubEnterpriseProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-Dg4td", "Errors.Invalid.Argument") + } + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-dgj53", "Errors.Invalid.Argument") + } + if provider.ClientSecret = strings.TrimSpace(provider.ClientSecret); provider.ClientSecret == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-Ghjjs", "Errors.Invalid.Argument") + } + if provider.AuthorizationEndpoint = strings.TrimSpace(provider.AuthorizationEndpoint); provider.AuthorizationEndpoint == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-sani2", "Errors.Invalid.Argument") + } + if provider.TokenEndpoint = strings.TrimSpace(provider.TokenEndpoint); provider.TokenEndpoint == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-agj42", "Errors.Invalid.Argument") + } + if provider.UserEndpoint = strings.TrimSpace(provider.UserEndpoint); provider.UserEndpoint == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-sd5hn", "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.NewGitHubEnterpriseIDPAddedEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.ClientID, + secret, + provider.AuthorizationEndpoint, + provider.TokenEndpoint, + provider.UserEndpoint, + provider.Scopes, + provider.IDPOptions, + ), + }, nil + }, nil + } +} + +func (c *Commands) prepareUpdateOrgGitHubEnterpriseProvider(a *org.Aggregate, writeModel *OrgGitHubEnterpriseIDPWriteModel, provider GitHubEnterpriseProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if writeModel.ID = strings.TrimSpace(writeModel.ID); writeModel.ID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-sdfh3", "Errors.Invalid.Argument") + } + if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-shj42", "Errors.Invalid.Argument") + } + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-sdh73", "Errors.Invalid.Argument") + } + if provider.AuthorizationEndpoint = strings.TrimSpace(provider.AuthorizationEndpoint); provider.AuthorizationEndpoint == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-acx2w", "Errors.Invalid.Argument") + } + if provider.TokenEndpoint = strings.TrimSpace(provider.TokenEndpoint); provider.TokenEndpoint == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-dgj6q", "Errors.Invalid.Argument") + } + if provider.UserEndpoint = strings.TrimSpace(provider.UserEndpoint); provider.UserEndpoint == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-ybj62", "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-GBr42", "Errors.Org.IDPConfig.NotExisting") + } + event, err := writeModel.NewChangedEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.ClientID, + provider.ClientSecret, + c.idpConfigEncryption, + provider.AuthorizationEndpoint, + provider.TokenEndpoint, + provider.UserEndpoint, + 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 ee3afd16d2..c972656e0d 100644 --- a/internal/command/org_idp_model.go +++ b/internal/command/org_idp_model.go @@ -293,6 +293,159 @@ func (wm *OrgJWTIDPWriteModel) NewChangedEvent( return org.NewJWTIDPChangedEvent(ctx, aggregate, id, changes) } +type OrgGitHubIDPWriteModel struct { + GitHubIDPWriteModel +} + +func NewGitHubOrgIDPWriteModel(orgID, id string) *OrgGitHubIDPWriteModel { + return &OrgGitHubIDPWriteModel{ + GitHubIDPWriteModel{ + WriteModel: eventstore.WriteModel{ + AggregateID: orgID, + ResourceOwner: orgID, + }, + ID: id, + }, + } +} + +func (wm *OrgGitHubIDPWriteModel) Reduce() error { + return wm.GitHubIDPWriteModel.Reduce() +} + +func (wm *OrgGitHubIDPWriteModel) AppendEvents(events ...eventstore.Event) { + for _, event := range events { + switch e := event.(type) { + case *org.GitHubIDPAddedEvent: + wm.GitHubIDPWriteModel.AppendEvents(&e.GitHubIDPAddedEvent) + case *org.GitHubIDPChangedEvent: + wm.GitHubIDPWriteModel.AppendEvents(&e.GitHubIDPChangedEvent) + case *org.IDPRemovedEvent: + wm.GitHubIDPWriteModel.AppendEvents(&e.RemovedEvent) + default: + wm.GitHubIDPWriteModel.AppendEvents(e) + } + } +} + +func (wm *OrgGitHubIDPWriteModel) Query() *eventstore.SearchQueryBuilder { + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + ResourceOwner(wm.ResourceOwner). + AddQuery(). + AggregateTypes(org.AggregateType). + AggregateIDs(wm.AggregateID). + EventTypes( + org.GitHubIDPAddedEventType, + org.GitHubIDPChangedEventType, + org.IDPRemovedEventType, + ). + EventData(map[string]interface{}{"id": wm.ID}). + Builder() +} + +func (wm *OrgGitHubIDPWriteModel) NewChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + clientID, + clientSecretString string, + secretCrypto crypto.Crypto, + scopes []string, + options idp.Options, +) (*org.GitHubIDPChangedEvent, error) { + + changes, err := wm.GitHubIDPWriteModel.NewChanges(name, clientID, clientSecretString, secretCrypto, scopes, options) + + if err != nil || len(changes) == 0 { + return nil, err + } + return org.NewGitHubIDPChangedEvent(ctx, aggregate, id, changes) +} + +type OrgGitHubEnterpriseIDPWriteModel struct { + GitHubEnterpriseIDPWriteModel +} + +func NewGitHubEnterpriseOrgIDPWriteModel(orgID, id string) *OrgGitHubEnterpriseIDPWriteModel { + return &OrgGitHubEnterpriseIDPWriteModel{ + GitHubEnterpriseIDPWriteModel{ + WriteModel: eventstore.WriteModel{ + AggregateID: orgID, + ResourceOwner: orgID, + }, + ID: id, + }, + } +} + +func (wm *OrgGitHubEnterpriseIDPWriteModel) Reduce() error { + return wm.GitHubEnterpriseIDPWriteModel.Reduce() +} + +func (wm *OrgGitHubEnterpriseIDPWriteModel) AppendEvents(events ...eventstore.Event) { + for _, event := range events { + switch e := event.(type) { + case *org.GitHubEnterpriseIDPAddedEvent: + wm.GitHubEnterpriseIDPWriteModel.AppendEvents(&e.GitHubEnterpriseIDPAddedEvent) + case *org.GitHubEnterpriseIDPChangedEvent: + wm.GitHubEnterpriseIDPWriteModel.AppendEvents(&e.GitHubEnterpriseIDPChangedEvent) + case *org.IDPRemovedEvent: + wm.GitHubEnterpriseIDPWriteModel.AppendEvents(&e.RemovedEvent) + default: + wm.GitHubEnterpriseIDPWriteModel.AppendEvents(e) + } + } +} + +func (wm *OrgGitHubEnterpriseIDPWriteModel) Query() *eventstore.SearchQueryBuilder { + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + ResourceOwner(wm.ResourceOwner). + AddQuery(). + AggregateTypes(org.AggregateType). + AggregateIDs(wm.AggregateID). + EventTypes( + org.GitHubEnterpriseIDPAddedEventType, + org.GitHubEnterpriseIDPChangedEventType, + org.IDPRemovedEventType, + ). + EventData(map[string]interface{}{"id": wm.ID}). + Builder() +} + +func (wm *OrgGitHubEnterpriseIDPWriteModel) NewChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + clientID string, + clientSecretString string, + secretCrypto crypto.Crypto, + authorizationEndpoint, + tokenEndpoint, + userEndpoint string, + scopes []string, + options idp.Options, +) (*org.GitHubEnterpriseIDPChangedEvent, error) { + + changes, err := wm.GitHubEnterpriseIDPWriteModel.NewChanges( + name, + clientID, + clientSecretString, + secretCrypto, + authorizationEndpoint, + tokenEndpoint, + userEndpoint, + scopes, + options, + ) + + if err != nil || len(changes) == 0 { + return nil, err + } + return org.NewGitHubEnterpriseIDPChangedEvent(ctx, aggregate, id, changes) +} + type OrgGoogleIDPWriteModel struct { GoogleIDPWriteModel } @@ -480,6 +633,10 @@ func (wm *OrgIDPRemoveWriteModel) AppendEvents(events ...eventstore.Event) { wm.IDPRemoveWriteModel.AppendEvents(&e.OIDCIDPAddedEvent) case *org.JWTIDPAddedEvent: wm.IDPRemoveWriteModel.AppendEvents(&e.JWTIDPAddedEvent) + case *org.GitHubIDPAddedEvent: + wm.IDPRemoveWriteModel.AppendEvents(&e.GitHubIDPAddedEvent) + case *org.GitHubEnterpriseIDPAddedEvent: + wm.IDPRemoveWriteModel.AppendEvents(&e.GitHubEnterpriseIDPAddedEvent) case *org.GoogleIDPAddedEvent: wm.IDPRemoveWriteModel.AppendEvents(&e.GoogleIDPAddedEvent) case *org.LDAPIDPAddedEvent: @@ -506,6 +663,8 @@ func (wm *OrgIDPRemoveWriteModel) Query() *eventstore.SearchQueryBuilder { org.OAuthIDPAddedEventType, org.OIDCIDPAddedEventType, org.JWTIDPAddedEventType, + org.GitHubIDPAddedEventType, + org.GitHubEnterpriseIDPAddedEventType, org.GoogleIDPAddedEventType, org.LDAPIDPAddedEventType, org.IDPRemovedEventType, diff --git a/internal/command/org_idp_test.go b/internal/command/org_idp_test.go index 1d35659713..9337fc61e6 100644 --- a/internal/command/org_idp_test.go +++ b/internal/command/org_idp_test.go @@ -617,6 +617,914 @@ func TestCommandSide_UpdateOrgGenericOAuthIDP(t *testing.T) { } } +func TestCommandSide_AddOrgGitHubIDP(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + idGenerator id.Generator + secretCrypto crypto.EncryptionAlgorithm + } + type args struct { + ctx context.Context + resourceOwner string + provider GitHubProvider + } + type res struct { + id string + want *domain.ObjectDetails + err func(error) bool + } + tests := []struct { + name string + fields fields + args args + res res + }{ + { + "invalid client id", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitHubProvider{}, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid client secret", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitHubProvider{ + ClientID: "clientID", + }, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + name: "ok", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter(), + expectPush( + eventPusherToEvents( + org.NewGitHubIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + idp.Options{}, + )), + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitHubProvider{ + ClientID: "clientID", + ClientSecret: "clientSecret", + }, + }, + res: res{ + id: "id1", + want: &domain.ObjectDetails{ResourceOwner: "org1"}, + }, + }, + { + name: "ok all set", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter(), + expectPush( + eventPusherToEvents( + org.NewGitHubIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid"}, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + )), + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitHubProvider{ + Name: "name", + 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.AddOrgGitHubProvider(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_UpdateOrgGitHubIDP(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + secretCrypto crypto.EncryptionAlgorithm + } + type args struct { + ctx context.Context + resourceOwner string + id string + provider GitHubProvider + } + 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: GitHubProvider{}, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid client id", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GitHubProvider{}, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + name: "not found", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter(), + ), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GitHubProvider{ + ClientID: "clientID", + }, + }, + res: res{ + err: caos_errors.IsNotFound, + }, + }, + { + name: "no changes", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + org.NewGitHubIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + idp.Options{}, + )), + ), + ), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GitHubProvider{ + ClientID: "clientID", + }, + }, + res: res{ + want: &domain.ObjectDetails{ResourceOwner: "org1"}, + }, + }, + { + name: "change ok", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + org.NewGitHubIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + idp.Options{}, + )), + ), + expectPush( + eventPusherToEvents( + func() eventstore.Command { + t := true + event, _ := org.NewGitHubIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + []idp.GitHubIDPChanges{ + idp.ChangeGitHubName("new name"), + idp.ChangeGitHubClientID("new clientID"), + idp.ChangeGitHubClientSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("new clientSecret"), + }), + idp.ChangeGitHubScopes([]string{"openid", "profile"}), + idp.ChangeGitHubOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), + ), + ), + ), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GitHubProvider{ + Name: "new name", + ClientID: "new clientID", + ClientSecret: "new clientSecret", + 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.UpdateOrgGitHubProvider(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_AddOrgGitHubEnterpriseIDP(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + idGenerator id.Generator + secretCrypto crypto.EncryptionAlgorithm + } + type args struct { + ctx context.Context + resourceOwner string + provider GitHubEnterpriseProvider + } + 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: GitHubEnterpriseProvider{}, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid clientID", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitHubEnterpriseProvider{ + Name: "name", + }, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid clientSecret", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitHubEnterpriseProvider{ + Name: "name", + ClientID: "clientID", + }, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid auth endpoint", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitHubEnterpriseProvider{ + Name: "name", + ClientID: "clientID", + ClientSecret: "clientSecret", + }, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid token endpoint", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitHubEnterpriseProvider{ + Name: "name", + ClientID: "clientID", + ClientSecret: "clientSecret", + AuthorizationEndpoint: "auth", + }, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid user endpoint", + fields{ + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitHubEnterpriseProvider{ + Name: "name", + ClientID: "clientID", + ClientSecret: "clientSecret", + AuthorizationEndpoint: "auth", + TokenEndpoint: "token", + }, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + name: "ok", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter(), + expectPush( + eventPusherToEvents( + org.NewGitHubEnterpriseIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + "auth", + "token", + "user", + nil, + idp.Options{}, + )), + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitHubEnterpriseProvider{ + Name: "name", + ClientID: "clientID", + ClientSecret: "clientSecret", + AuthorizationEndpoint: "auth", + TokenEndpoint: "token", + UserEndpoint: "user", + }, + }, + res: res{ + id: "id1", + want: &domain.ObjectDetails{ResourceOwner: "org1"}, + }, + }, + { + name: "ok all set", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter(), + expectPush( + eventPusherToEvents( + org.NewGitHubEnterpriseIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + "auth", + "token", + "user", + []string{"user"}, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + )), + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: GitHubEnterpriseProvider{ + Name: "name", + ClientID: "clientID", + ClientSecret: "clientSecret", + AuthorizationEndpoint: "auth", + TokenEndpoint: "token", + UserEndpoint: "user", + Scopes: []string{"user"}, + 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.AddOrgGitHubEnterpriseProvider(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_UpdateOrgGitHubEnterpriseIDP(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + secretCrypto crypto.EncryptionAlgorithm + } + type args struct { + ctx context.Context + resourceOwner string + id string + provider GitHubEnterpriseProvider + } + 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: GitHubEnterpriseProvider{}, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid name", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GitHubEnterpriseProvider{}, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid clientID", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GitHubEnterpriseProvider{ + Name: "name", + }, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid auth endpoint", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GitHubEnterpriseProvider{ + Name: "name", + }, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid token endpoint", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GitHubEnterpriseProvider{ + Name: "name", + ClientID: "clientID", + AuthorizationEndpoint: "auth", + }, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + "invalid user endpoint", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GitHubEnterpriseProvider{ + Name: "name", + ClientID: "clientID", + AuthorizationEndpoint: "auth", + TokenEndpoint: "token", + }, + }, + res{ + err: caos_errors.IsErrorInvalidArgument, + }, + }, + { + name: "not found", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter(), + ), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GitHubEnterpriseProvider{ + Name: "name", + ClientID: "clientID", + AuthorizationEndpoint: "auth", + TokenEndpoint: "token", + UserEndpoint: "user", + }, + }, + res: res{ + err: caos_errors.IsNotFound, + }, + }, + { + name: "no changes", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + org.NewGitHubEnterpriseIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + "auth", + "token", + "user", + nil, + idp.Options{}, + )), + ), + ), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GitHubEnterpriseProvider{ + Name: "name", + ClientID: "clientID", + AuthorizationEndpoint: "auth", + TokenEndpoint: "token", + UserEndpoint: "user", + }, + }, + res: res{ + want: &domain.ObjectDetails{ResourceOwner: "org1"}, + }, + }, + { + name: "change ok", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + org.NewGitHubEnterpriseIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + "auth", + "token", + "user", + nil, + idp.Options{}, + )), + ), + expectPush( + eventPusherToEvents( + func() eventstore.Command { + t := true + event, _ := org.NewGitHubEnterpriseIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + []idp.GitHubEnterpriseIDPChanges{ + idp.ChangeGitHubEnterpriseName("new name"), + idp.ChangeGitHubEnterpriseClientID("clientID2"), + idp.ChangeGitHubEnterpriseClientSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("newSecret"), + }), + idp.ChangeGitHubEnterpriseAuthorizationEndpoint("new auth"), + idp.ChangeGitHubEnterpriseTokenEndpoint("new token"), + idp.ChangeGitHubEnterpriseUserEndpoint("new user"), + idp.ChangeGitHubEnterpriseScopes([]string{"openid", "profile"}), + idp.ChangeGitHubEnterpriseOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), + ), + ), + ), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GitHubEnterpriseProvider{ + Name: "new name", + ClientID: "clientID2", + ClientSecret: "newSecret", + AuthorizationEndpoint: "new auth", + TokenEndpoint: "new token", + UserEndpoint: "new user", + 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.UpdateOrgGitHubEnterpriseProvider(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 diff --git a/internal/domain/idp.go b/internal/domain/idp.go index af34731c1e..3c1d33e411 100644 --- a/internal/domain/idp.go +++ b/internal/domain/idp.go @@ -29,7 +29,7 @@ const ( IDPTypeLDAP IDPTypeAzureAD IDPTypeGitHub - IDPTypeGitHubEE + IDPTypeGitHubEnterprise IDPTypeGitLab IDPTypeGitLabSelfHosted IDPTypeGoogle @@ -39,6 +39,9 @@ func (t IDPType) GetCSSClass() string { switch t { //nolint:exhaustive case IDPTypeGoogle: return "google" + case IDPTypeGitHub, + IDPTypeGitHubEnterprise: + return "github" default: return "" } diff --git a/internal/query/idp_template.go b/internal/query/idp_template.go index 8d4a909f6d..e954e1e483 100644 --- a/internal/query/idp_template.go +++ b/internal/query/idp_template.go @@ -37,6 +37,8 @@ type IDPTemplate struct { *OAuthIDPTemplate *OIDCIDPTemplate *JWTIDPTemplate + *GitHubIDPTemplate + *GitHubEnterpriseIDPTemplate *GoogleIDPTemplate *LDAPIDPTemplate } @@ -73,6 +75,23 @@ type JWTIDPTemplate struct { Endpoint string } +type GitHubIDPTemplate struct { + IDPID string + ClientID string + ClientSecret *crypto.CryptoValue + Scopes database.StringArray +} + +type GitHubEnterpriseIDPTemplate struct { + IDPID string + ClientID string + ClientSecret *crypto.CryptoValue + AuthorizationEndpoint string + TokenEndpoint string + UserEndpoint string + Scopes database.StringArray +} + type GoogleIDPTemplate struct { IDPID string ClientID string @@ -265,6 +284,72 @@ var ( } ) +var ( + githubIdpTemplateTable = table{ + name: projection.IDPTemplateGitHubTable, + instanceIDCol: projection.GitHubInstanceIDCol, + } + GitHubIDCol = Column{ + name: projection.GitHubIDCol, + table: githubIdpTemplateTable, + } + GitHubInstanceIDCol = Column{ + name: projection.GitHubInstanceIDCol, + table: githubIdpTemplateTable, + } + GitHubClientIDCol = Column{ + name: projection.GitHubClientIDCol, + table: githubIdpTemplateTable, + } + GitHubClientSecretCol = Column{ + name: projection.GitHubClientSecretCol, + table: githubIdpTemplateTable, + } + GitHubScopesCol = Column{ + name: projection.GitHubScopesCol, + table: githubIdpTemplateTable, + } +) + +var ( + githubEnterpriseIdpTemplateTable = table{ + name: projection.IDPTemplateGitHubEnterpriseTable, + instanceIDCol: projection.GitHubEnterpriseInstanceIDCol, + } + GitHubEnterpriseIDCol = Column{ + name: projection.GitHubEnterpriseIDCol, + table: githubEnterpriseIdpTemplateTable, + } + GitHubEnterpriseInstanceIDCol = Column{ + name: projection.GitHubEnterpriseInstanceIDCol, + table: githubEnterpriseIdpTemplateTable, + } + GitHubEnterpriseClientIDCol = Column{ + name: projection.GitHubEnterpriseClientIDCol, + table: githubEnterpriseIdpTemplateTable, + } + GitHubEnterpriseClientSecretCol = Column{ + name: projection.GitHubEnterpriseClientSecretCol, + table: githubEnterpriseIdpTemplateTable, + } + GitHubEnterpriseAuthorizationEndpointCol = Column{ + name: projection.GitHubEnterpriseAuthorizationEndpointCol, + table: githubEnterpriseIdpTemplateTable, + } + GitHubEnterpriseTokenEndpointCol = Column{ + name: projection.GitHubEnterpriseTokenEndpointCol, + table: githubEnterpriseIdpTemplateTable, + } + GitHubEnterpriseUserEndpointCol = Column{ + name: projection.GitHubEnterpriseUserEndpointCol, + table: githubEnterpriseIdpTemplateTable, + } + GitHubEnterpriseScopesCol = Column{ + name: projection.GitHubEnterpriseScopesCol, + table: githubEnterpriseIdpTemplateTable, + } +) + var ( googleIdpTemplateTable = table{ name: projection.IDPTemplateGoogleTable, @@ -523,6 +608,19 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se JWTEndpointCol.identifier(), JWTKeysEndpointCol.identifier(), JWTHeaderNameCol.identifier(), + // github + GitHubIDCol.identifier(), + GitHubClientIDCol.identifier(), + GitHubClientSecretCol.identifier(), + GitHubScopesCol.identifier(), + // github enterprise + GitHubEnterpriseIDCol.identifier(), + GitHubEnterpriseClientIDCol.identifier(), + GitHubEnterpriseClientSecretCol.identifier(), + GitHubEnterpriseAuthorizationEndpointCol.identifier(), + GitHubEnterpriseTokenEndpointCol.identifier(), + GitHubEnterpriseUserEndpointCol.identifier(), + GitHubEnterpriseScopesCol.identifier(), // google GoogleIDCol.identifier(), GoogleClientIDCol.identifier(), @@ -555,6 +653,8 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se LeftJoin(join(OAuthIDCol, IDPTemplateIDCol)). LeftJoin(join(OIDCIDCol, IDPTemplateIDCol)). LeftJoin(join(JWTIDCol, IDPTemplateIDCol)). + LeftJoin(join(GitHubIDCol, IDPTemplateIDCol)). + LeftJoin(join(GitHubEnterpriseIDCol, IDPTemplateIDCol)). LeftJoin(join(GoogleIDCol, IDPTemplateIDCol)). LeftJoin(join(LDAPIDCol, IDPTemplateIDCol) + db.Timetravel(call.Took(ctx))). PlaceholderFormat(sq.Dollar), @@ -584,6 +684,19 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se jwtKeysEndpoint := sql.NullString{} jwtHeaderName := sql.NullString{} + githubID := sql.NullString{} + githubClientID := sql.NullString{} + githubClientSecret := new(crypto.CryptoValue) + githubScopes := database.StringArray{} + + githubEnterpriseID := sql.NullString{} + githubEnterpriseClientID := sql.NullString{} + githubEnterpriseClientSecret := new(crypto.CryptoValue) + githubEnterpriseAuthorizationEndpoint := sql.NullString{} + githubEnterpriseTokenEndpoint := sql.NullString{} + githubEnterpriseUserEndpoint := sql.NullString{} + githubEnterpriseScopes := database.StringArray{} + googleID := sql.NullString{} googleClientID := sql.NullString{} googleClientSecret := new(crypto.CryptoValue) @@ -647,6 +760,19 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se &jwtEndpoint, &jwtKeysEndpoint, &jwtHeaderName, + // github + &githubID, + &githubClientID, + &githubClientSecret, + &githubScopes, + // github enterprise + &githubEnterpriseID, + &githubEnterpriseClientID, + &githubEnterpriseClientSecret, + &githubEnterpriseAuthorizationEndpoint, + &githubEnterpriseTokenEndpoint, + &githubEnterpriseUserEndpoint, + &githubEnterpriseScopes, // google &googleID, &googleClientID, @@ -715,6 +841,25 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se Endpoint: jwtEndpoint.String, } } + if githubID.Valid { + idpTemplate.GitHubIDPTemplate = &GitHubIDPTemplate{ + IDPID: githubID.String, + ClientID: githubClientID.String, + ClientSecret: githubClientSecret, + Scopes: githubScopes, + } + } + if githubEnterpriseID.Valid { + idpTemplate.GitHubEnterpriseIDPTemplate = &GitHubEnterpriseIDPTemplate{ + IDPID: githubEnterpriseID.String, + ClientID: githubEnterpriseClientID.String, + ClientSecret: githubEnterpriseClientSecret, + AuthorizationEndpoint: githubEnterpriseAuthorizationEndpoint.String, + TokenEndpoint: githubEnterpriseTokenEndpoint.String, + UserEndpoint: githubEnterpriseUserEndpoint.String, + Scopes: githubEnterpriseScopes, + } + } if googleID.Valid { idpTemplate.GoogleIDPTemplate = &GoogleIDPTemplate{ IDPID: googleID.String, @@ -792,6 +937,19 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec JWTEndpointCol.identifier(), JWTKeysEndpointCol.identifier(), JWTHeaderNameCol.identifier(), + // github + GitHubIDCol.identifier(), + GitHubClientIDCol.identifier(), + GitHubClientSecretCol.identifier(), + GitHubScopesCol.identifier(), + // github enterprise + GitHubEnterpriseIDCol.identifier(), + GitHubEnterpriseClientIDCol.identifier(), + GitHubEnterpriseClientSecretCol.identifier(), + GitHubEnterpriseAuthorizationEndpointCol.identifier(), + GitHubEnterpriseTokenEndpointCol.identifier(), + GitHubEnterpriseUserEndpointCol.identifier(), + GitHubEnterpriseScopesCol.identifier(), // google GoogleIDCol.identifier(), GoogleClientIDCol.identifier(), @@ -825,6 +983,8 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec LeftJoin(join(OAuthIDCol, IDPTemplateIDCol)). LeftJoin(join(OIDCIDCol, IDPTemplateIDCol)). LeftJoin(join(JWTIDCol, IDPTemplateIDCol)). + LeftJoin(join(GitHubIDCol, IDPTemplateIDCol)). + LeftJoin(join(GitHubEnterpriseIDCol, IDPTemplateIDCol)). LeftJoin(join(GoogleIDCol, IDPTemplateIDCol)). LeftJoin(join(LDAPIDCol, IDPTemplateIDCol) + db.Timetravel(call.Took(ctx))). PlaceholderFormat(sq.Dollar), @@ -857,6 +1017,19 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec jwtKeysEndpoint := sql.NullString{} jwtHeaderName := sql.NullString{} + githubID := sql.NullString{} + githubClientID := sql.NullString{} + githubClientSecret := new(crypto.CryptoValue) + githubScopes := database.StringArray{} + + githubEnterpriseID := sql.NullString{} + githubEnterpriseClientID := sql.NullString{} + githubEnterpriseClientSecret := new(crypto.CryptoValue) + githubEnterpriseAuthorizationEndpoint := sql.NullString{} + githubEnterpriseTokenEndpoint := sql.NullString{} + githubEnterpriseUserEndpoint := sql.NullString{} + githubEnterpriseScopes := database.StringArray{} + googleID := sql.NullString{} googleClientID := sql.NullString{} googleClientSecret := new(crypto.CryptoValue) @@ -920,6 +1093,19 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec &jwtEndpoint, &jwtKeysEndpoint, &jwtHeaderName, + // github + &githubID, + &githubClientID, + &githubClientSecret, + &githubScopes, + // github enterprise + &githubEnterpriseID, + &githubEnterpriseClientID, + &githubEnterpriseClientSecret, + &githubEnterpriseAuthorizationEndpoint, + &githubEnterpriseTokenEndpoint, + &githubEnterpriseUserEndpoint, + &githubEnterpriseScopes, // google &googleID, &googleClientID, @@ -987,6 +1173,25 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec Endpoint: jwtEndpoint.String, } } + if githubID.Valid { + idpTemplate.GitHubIDPTemplate = &GitHubIDPTemplate{ + IDPID: githubID.String, + ClientID: githubClientID.String, + ClientSecret: githubClientSecret, + Scopes: githubScopes, + } + } + if githubEnterpriseID.Valid { + idpTemplate.GitHubEnterpriseIDPTemplate = &GitHubEnterpriseIDPTemplate{ + IDPID: githubEnterpriseID.String, + ClientID: githubEnterpriseClientID.String, + ClientSecret: githubEnterpriseClientSecret, + AuthorizationEndpoint: githubEnterpriseAuthorizationEndpoint.String, + TokenEndpoint: githubEnterpriseTokenEndpoint.String, + UserEndpoint: githubEnterpriseUserEndpoint.String, + Scopes: githubEnterpriseScopes, + } + } 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 d0470eb0da..cf59d153ce 100644 --- a/internal/query/idp_template_test.go +++ b/internal/query/idp_template_test.go @@ -49,6 +49,19 @@ var ( ` projections.idp_templates3_jwt.jwt_endpoint,` + ` projections.idp_templates3_jwt.keys_endpoint,` + ` projections.idp_templates3_jwt.header_name,` + + // github + ` projections.idp_templates3_github.idp_id,` + + ` projections.idp_templates3_github.client_id,` + + ` projections.idp_templates3_github.client_secret,` + + ` projections.idp_templates3_github.scopes,` + + // github enterprise + ` projections.idp_templates3_github_enterprise.idp_id,` + + ` projections.idp_templates3_github_enterprise.client_id,` + + ` projections.idp_templates3_github_enterprise.client_secret,` + + ` projections.idp_templates3_github_enterprise.authorization_endpoint,` + + ` projections.idp_templates3_github_enterprise.token_endpoint,` + + ` projections.idp_templates3_github_enterprise.user_endpoint,` + + ` projections.idp_templates3_github_enterprise.scopes,` + // google ` projections.idp_templates3_google.idp_id,` + ` projections.idp_templates3_google.client_id,` + @@ -81,6 +94,8 @@ var ( ` LEFT JOIN projections.idp_templates3_oauth2 ON projections.idp_templates3.id = projections.idp_templates3_oauth2.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_oauth2.instance_id` + ` LEFT JOIN projections.idp_templates3_oidc ON projections.idp_templates3.id = projections.idp_templates3_oidc.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_oidc.instance_id` + ` 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_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'` @@ -119,6 +134,19 @@ var ( "jwt_endpoint", "keys_endpoint", "header_name", + // github config + "idp_id", + "client_id", + "client_secret", + "scopes", + // github enterprise config + "idp_id", + "client_id", + "client_secret", + "authorization_endpoint", + "token_endpoint", + "user_endpoint", + "scopes", // google config "idp_id", "client_id", @@ -182,6 +210,19 @@ var ( ` projections.idp_templates3_jwt.jwt_endpoint,` + ` projections.idp_templates3_jwt.keys_endpoint,` + ` projections.idp_templates3_jwt.header_name,` + + // github + ` projections.idp_templates3_github.idp_id,` + + ` projections.idp_templates3_github.client_id,` + + ` projections.idp_templates3_github.client_secret,` + + ` projections.idp_templates3_github.scopes,` + + // github enterprise + ` projections.idp_templates3_github_enterprise.idp_id,` + + ` projections.idp_templates3_github_enterprise.client_id,` + + ` projections.idp_templates3_github_enterprise.client_secret,` + + ` projections.idp_templates3_github_enterprise.authorization_endpoint,` + + ` projections.idp_templates3_github_enterprise.token_endpoint,` + + ` projections.idp_templates3_github_enterprise.user_endpoint,` + + ` projections.idp_templates3_github_enterprise.scopes,` + // google ` projections.idp_templates3_google.idp_id,` + ` projections.idp_templates3_google.client_id,` + @@ -215,6 +256,8 @@ var ( ` LEFT JOIN projections.idp_templates3_oauth2 ON projections.idp_templates3.id = projections.idp_templates3_oauth2.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_oauth2.instance_id` + ` LEFT JOIN projections.idp_templates3_oidc ON projections.idp_templates3.id = projections.idp_templates3_oidc.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_oidc.instance_id` + ` 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_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'` @@ -253,6 +296,19 @@ var ( "jwt_endpoint", "keys_endpoint", "header_name", + // github config + "idp_id", + "client_id", + "client_secret", + "scopes", + // github enterprise config + "idp_id", + "client_id", + "client_secret", + "authorization_endpoint", + "token_endpoint", + "user_endpoint", + "scopes", // google config "idp_id", "client_id", @@ -356,6 +412,19 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // github + nil, + nil, + nil, + nil, + // github enterprise + nil, + nil, + nil, + nil, + nil, + nil, + nil, // google nil, nil, @@ -455,6 +524,19 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // github + nil, + nil, + nil, + nil, + // github enterprise + nil, + nil, + nil, + nil, + nil, + nil, + nil, // google nil, nil, @@ -510,7 +592,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { }, }, { - name: "prepareIDPTemplateByIDQuery oidc idp", + name: "prepareIDPTemplateByIDQuery jwt idp", prepare: prepareIDPTemplateByIDQuery, want: want{ sqlExpectations: mockQuery( @@ -551,6 +633,19 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { "jwt", "keys", "header", + // github + nil, + nil, + nil, + nil, + // github enterprise + nil, + nil, + nil, + nil, + nil, + nil, + nil, // google nil, nil, @@ -605,6 +700,114 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { }, }, }, + { + name: "prepareIDPTemplateByIDQuery github 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.IDPTypeGitHub, + 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 + "idp-id", + "client_id", + nil, + database.StringArray{"profile"}, + // github enterprise + nil, + nil, + 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.IDPTypeGitHub, + OwnerType: domain.IdentityProviderTypeOrg, + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + GitHubIDPTemplate: &GitHubIDPTemplate{ + IDPID: "idp-id", + ClientID: "client_id", + ClientSecret: nil, + Scopes: []string{"profile"}, + }, + }, + }, { name: "prepareIDPTemplateByIDQuery google idp", prepare: prepareIDPTemplateByIDQuery, @@ -647,6 +850,19 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // github + nil, + nil, + nil, + nil, + // github enterprise + nil, + nil, + nil, + nil, + nil, + nil, + nil, // google "idp-id", "client_id", @@ -742,6 +958,19 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // github + nil, + nil, + nil, + nil, + // github enterprise + nil, + nil, + nil, + nil, + nil, + nil, + nil, // google nil, nil, @@ -856,6 +1085,19 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // github + nil, + nil, + nil, + nil, + // github enterprise + nil, + nil, + nil, + nil, + nil, + nil, + nil, // google config nil, nil, @@ -981,6 +1223,19 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // github + nil, + nil, + nil, + nil, + // github enterprise + nil, + nil, + nil, + nil, + nil, + nil, + nil, // google config nil, nil, @@ -1104,6 +1359,19 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // github + nil, + nil, + nil, + nil, + // github enterprise + nil, + nil, + nil, + nil, + nil, + nil, + nil, // google config nil, nil, @@ -1202,6 +1470,19 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // github + nil, + nil, + nil, + nil, + // github enterprise + nil, + nil, + nil, + nil, + nil, + nil, + nil, // google config nil, nil, @@ -1266,6 +1547,19 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // github + nil, + nil, + nil, + nil, + // github enterprise + nil, + nil, + nil, + nil, + nil, + nil, + nil, // google "idp-id-google", "client_id", @@ -1330,6 +1624,19 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // github + nil, + nil, + nil, + nil, + // github enterprise + nil, + nil, + nil, + nil, + nil, + nil, + nil, // google nil, nil, @@ -1394,6 +1701,19 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // github + nil, + nil, + nil, + nil, + // github enterprise + nil, + nil, + nil, + nil, + nil, + nil, + nil, // google nil, nil, @@ -1458,6 +1778,19 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { "jwt", "keys", "header", + // github + nil, + nil, + nil, + nil, + // github enterprise + nil, + nil, + 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 7a7cd13af7..b90ee12076 100644 --- a/internal/query/projection/idp_template.go +++ b/internal/query/projection/idp_template.go @@ -21,12 +21,16 @@ const ( IDPTemplateOAuthTable = IDPTemplateTable + "_" + IDPTemplateOAuthSuffix IDPTemplateOIDCTable = IDPTemplateTable + "_" + IDPTemplateOIDCSuffix IDPTemplateJWTTable = IDPTemplateTable + "_" + IDPTemplateJWTSuffix + IDPTemplateGitHubTable = IDPTemplateTable + "_" + IDPTemplateGitHubSuffix + IDPTemplateGitHubEnterpriseTable = IDPTemplateTable + "_" + IDPTemplateGitHubEnterpriseSuffix IDPTemplateGoogleTable = IDPTemplateTable + "_" + IDPTemplateGoogleSuffix IDPTemplateLDAPTable = IDPTemplateTable + "_" + IDPTemplateLDAPSuffix IDPTemplateOAuthSuffix = "oauth2" IDPTemplateOIDCSuffix = "oidc" IDPTemplateJWTSuffix = "jwt" + IDPTemplateGitHubSuffix = "github" + IDPTemplateGitHubEnterpriseSuffix = "github_enterprise" IDPTemplateGoogleSuffix = "google" IDPTemplateLDAPSuffix = "ldap" @@ -70,6 +74,21 @@ const ( JWTKeysEndpointCol = "keys_endpoint" JWTHeaderNameCol = "header_name" + GitHubIDCol = "idp_id" + GitHubInstanceIDCol = "instance_id" + GitHubClientIDCol = "client_id" + GitHubClientSecretCol = "client_secret" + GitHubScopesCol = "scopes" + + GitHubEnterpriseIDCol = "idp_id" + GitHubEnterpriseInstanceIDCol = "instance_id" + GitHubEnterpriseClientIDCol = "client_id" + GitHubEnterpriseClientSecretCol = "client_secret" + GitHubEnterpriseAuthorizationEndpointCol = "authorization_endpoint" + GitHubEnterpriseTokenEndpointCol = "token_endpoint" + GitHubEnterpriseUserEndpointCol = "user_endpoint" + GitHubEnterpriseScopesCol = "scopes" + GoogleIDCol = "idp_id" GoogleInstanceIDCol = "instance_id" GoogleClientIDCol = "client_id" @@ -170,6 +189,31 @@ func newIDPTemplateProjection(ctx context.Context, config crdb.StatementHandlerC IDPTemplateJWTSuffix, crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), ), + crdb.NewSuffixedTable([]*crdb.Column{ + crdb.NewColumn(GitHubIDCol, crdb.ColumnTypeText), + crdb.NewColumn(GitHubInstanceIDCol, crdb.ColumnTypeText), + crdb.NewColumn(GitHubClientIDCol, crdb.ColumnTypeText), + crdb.NewColumn(GitHubClientSecretCol, crdb.ColumnTypeJSONB), + crdb.NewColumn(GitHubScopesCol, crdb.ColumnTypeTextArray, crdb.Nullable()), + }, + crdb.NewPrimaryKey(GitHubInstanceIDCol, GitHubIDCol), + IDPTemplateGitHubSuffix, + crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), + ), + crdb.NewSuffixedTable([]*crdb.Column{ + crdb.NewColumn(GitHubEnterpriseIDCol, crdb.ColumnTypeText), + crdb.NewColumn(GitHubEnterpriseInstanceIDCol, crdb.ColumnTypeText), + crdb.NewColumn(GitHubEnterpriseClientIDCol, crdb.ColumnTypeText), + crdb.NewColumn(GitHubEnterpriseClientSecretCol, crdb.ColumnTypeJSONB), + crdb.NewColumn(GitHubEnterpriseAuthorizationEndpointCol, crdb.ColumnTypeText), + crdb.NewColumn(GitHubEnterpriseTokenEndpointCol, crdb.ColumnTypeText), + crdb.NewColumn(GitHubEnterpriseUserEndpointCol, crdb.ColumnTypeText), + crdb.NewColumn(GitHubEnterpriseScopesCol, crdb.ColumnTypeTextArray, crdb.Nullable()), + }, + crdb.NewPrimaryKey(GitHubEnterpriseInstanceIDCol, GitHubEnterpriseIDCol), + IDPTemplateGitHubEnterpriseSuffix, + crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), + ), crdb.NewSuffixedTable([]*crdb.Column{ crdb.NewColumn(GoogleIDCol, crdb.ColumnTypeText), crdb.NewColumn(GoogleInstanceIDCol, crdb.ColumnTypeText), @@ -268,6 +312,22 @@ func (p *idpTemplateProjection) reducers() []handler.AggregateReducer { Event: instance.IDPJWTConfigChangedEventType, Reduce: p.reduceOldJWTConfigChanged, }, + { + Event: instance.GitHubIDPAddedEventType, + Reduce: p.reduceGitHubIDPAdded, + }, + { + Event: instance.GitHubIDPChangedEventType, + Reduce: p.reduceGitHubIDPChanged, + }, + { + Event: instance.GitHubEnterpriseIDPAddedEventType, + Reduce: p.reduceGitHubEnterpriseIDPAdded, + }, + { + Event: instance.GitHubEnterpriseIDPChangedEventType, + Reduce: p.reduceGitHubEnterpriseIDPChanged, + }, { Event: instance.GoogleIDPAddedEventType, Reduce: p.reduceGoogleIDPAdded, @@ -346,6 +406,22 @@ func (p *idpTemplateProjection) reducers() []handler.AggregateReducer { Event: org.IDPJWTConfigChangedEventType, Reduce: p.reduceOldJWTConfigChanged, }, + { + Event: org.GitHubIDPAddedEventType, + Reduce: p.reduceGitHubIDPAdded, + }, + { + Event: org.GitHubIDPChangedEventType, + Reduce: p.reduceGitHubIDPChanged, + }, + { + Event: org.GitHubEnterpriseIDPAddedEventType, + Reduce: p.reduceGitHubEnterpriseIDPAdded, + }, + { + Event: org.GitHubEnterpriseIDPChangedEventType, + Reduce: p.reduceGitHubEnterpriseIDPChanged, + }, { Event: org.GoogleIDPAddedEventType, Reduce: p.reduceGoogleIDPAdded, @@ -901,6 +977,185 @@ func (p *idpTemplateProjection) reduceOldJWTConfigChanged(event eventstore.Event ), nil } +func (p *idpTemplateProjection) reduceGitHubIDPAdded(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idp.GitHubIDPAddedEvent + var idpOwnerType domain.IdentityProviderType + switch e := event.(type) { + case *org.GitHubIDPAddedEvent: + idpEvent = e.GitHubIDPAddedEvent + idpOwnerType = domain.IdentityProviderTypeOrg + case *instance.GitHubIDPAddedEvent: + idpEvent = e.GitHubIDPAddedEvent + idpOwnerType = domain.IdentityProviderTypeSystem + default: + return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-x9a022b", "reduce.wrong.event.type %v", []eventstore.EventType{org.GitHubIDPAddedEventType, instance.GitHubIDPAddedEventType}) + } + + 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.IDPTypeGitHub), + 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(GitHubIDCol, idpEvent.ID), + handler.NewCol(GitHubInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCol(GitHubClientIDCol, idpEvent.ClientID), + handler.NewCol(GitHubClientSecretCol, idpEvent.ClientSecret), + handler.NewCol(GitHubScopesCol, database.StringArray(idpEvent.Scopes)), + }, + crdb.WithTableSuffix(IDPTemplateGitHubSuffix), + ), + ), nil +} + +func (p *idpTemplateProjection) reduceGitHubEnterpriseIDPAdded(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idp.GitHubEnterpriseIDPAddedEvent + var idpOwnerType domain.IdentityProviderType + switch e := event.(type) { + case *org.GitHubEnterpriseIDPAddedEvent: + idpEvent = e.GitHubEnterpriseIDPAddedEvent + idpOwnerType = domain.IdentityProviderTypeOrg + case *instance.GitHubEnterpriseIDPAddedEvent: + idpEvent = e.GitHubEnterpriseIDPAddedEvent + idpOwnerType = domain.IdentityProviderTypeSystem + default: + return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Sf3g2a", "reduce.wrong.event.type %v", []eventstore.EventType{org.GitHubEnterpriseIDPAddedEventType, instance.GitHubEnterpriseIDPAddedEventType}) + } + + 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.IDPTypeGitHubEnterprise), + 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(GitHubEnterpriseIDCol, idpEvent.ID), + handler.NewCol(GitHubEnterpriseInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCol(GitHubEnterpriseClientIDCol, idpEvent.ClientID), + handler.NewCol(GitHubEnterpriseClientSecretCol, idpEvent.ClientSecret), + handler.NewCol(GitHubEnterpriseAuthorizationEndpointCol, idpEvent.AuthorizationEndpoint), + handler.NewCol(GitHubEnterpriseTokenEndpointCol, idpEvent.TokenEndpoint), + handler.NewCol(GitHubEnterpriseUserEndpointCol, idpEvent.UserEndpoint), + handler.NewCol(GitHubEnterpriseScopesCol, database.StringArray(idpEvent.Scopes)), + }, + crdb.WithTableSuffix(IDPTemplateGitHubEnterpriseSuffix), + ), + ), nil +} + +func (p *idpTemplateProjection) reduceGitHubIDPChanged(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idp.GitHubIDPChangedEvent + switch e := event.(type) { + case *org.GitHubIDPChangedEvent: + idpEvent = e.GitHubIDPChangedEvent + case *instance.GitHubIDPChangedEvent: + idpEvent = e.GitHubIDPChangedEvent + default: + return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.GitHubIDPChangedEventType, instance.GitHubIDPChangedEventType}) + } + + ops := make([]func(eventstore.Event) crdb.Exec, 0, 2) + ops = 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), + }, + ), + ) + githubCols := reduceGitHubIDPChangedColumns(idpEvent) + if len(githubCols) > 0 { + ops = append(ops, + crdb.AddUpdateStatement( + githubCols, + []handler.Condition{ + handler.NewCond(GitHubIDCol, idpEvent.ID), + handler.NewCond(GitHubInstanceIDCol, idpEvent.Aggregate().InstanceID), + }, + crdb.WithTableSuffix(IDPTemplateGitHubSuffix), + ), + ) + } + + return crdb.NewMultiStatement( + &idpEvent, + ops..., + ), nil +} + +func (p *idpTemplateProjection) reduceGitHubEnterpriseIDPChanged(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idp.GitHubEnterpriseIDPChangedEvent + switch e := event.(type) { + case *org.GitHubEnterpriseIDPChangedEvent: + idpEvent = e.GitHubEnterpriseIDPChangedEvent + case *instance.GitHubEnterpriseIDPChangedEvent: + idpEvent = e.GitHubEnterpriseIDPChangedEvent + default: + return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SDg3g", "reduce.wrong.event.type %v", []eventstore.EventType{org.GitHubEnterpriseIDPChangedEventType, instance.GitHubEnterpriseIDPChangedEventType}) + } + + ops := make([]func(eventstore.Event) crdb.Exec, 0, 2) + ops = 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), + }, + ), + ) + githubCols := reduceGitHubEnterpriseIDPChangedColumns(idpEvent) + if len(githubCols) > 0 { + ops = append(ops, + crdb.AddUpdateStatement( + githubCols, + []handler.Condition{ + handler.NewCond(GitHubEnterpriseIDCol, idpEvent.ID), + handler.NewCond(GitHubEnterpriseInstanceIDCol, idpEvent.Aggregate().InstanceID), + }, + crdb.WithTableSuffix(IDPTemplateGitHubEnterpriseSuffix), + ), + ) + } + + return crdb.NewMultiStatement( + &idpEvent, + ops..., + ), nil +} + func (p *idpTemplateProjection) reduceGoogleIDPAdded(event eventstore.Event) (*handler.Statement, error) { var idpEvent idp.GoogleIDPAddedEvent var idpOwnerType domain.IdentityProviderType @@ -1219,6 +1474,43 @@ func reduceJWTIDPChangedColumns(idpEvent idp.JWTIDPChangedEvent) []handler.Colum return jwtCols } +func reduceGitHubIDPChangedColumns(idpEvent idp.GitHubIDPChangedEvent) []handler.Column { + oauthCols := make([]handler.Column, 0, 3) + if idpEvent.ClientID != nil { + oauthCols = append(oauthCols, handler.NewCol(GitHubClientIDCol, *idpEvent.ClientID)) + } + if idpEvent.ClientSecret != nil { + oauthCols = append(oauthCols, handler.NewCol(GitHubClientSecretCol, *idpEvent.ClientSecret)) + } + if idpEvent.Scopes != nil { + oauthCols = append(oauthCols, handler.NewCol(GitHubScopesCol, database.StringArray(idpEvent.Scopes))) + } + return oauthCols +} + +func reduceGitHubEnterpriseIDPChangedColumns(idpEvent idp.GitHubEnterpriseIDPChangedEvent) []handler.Column { + oauthCols := make([]handler.Column, 0, 6) + if idpEvent.ClientID != nil { + oauthCols = append(oauthCols, handler.NewCol(GitHubEnterpriseClientIDCol, *idpEvent.ClientID)) + } + if idpEvent.ClientSecret != nil { + oauthCols = append(oauthCols, handler.NewCol(GitHubEnterpriseClientSecretCol, *idpEvent.ClientSecret)) + } + if idpEvent.AuthorizationEndpoint != nil { + oauthCols = append(oauthCols, handler.NewCol(GitHubEnterpriseAuthorizationEndpointCol, *idpEvent.AuthorizationEndpoint)) + } + if idpEvent.TokenEndpoint != nil { + oauthCols = append(oauthCols, handler.NewCol(GitHubEnterpriseTokenEndpointCol, *idpEvent.TokenEndpoint)) + } + if idpEvent.UserEndpoint != nil { + oauthCols = append(oauthCols, handler.NewCol(GitHubEnterpriseUserEndpointCol, *idpEvent.UserEndpoint)) + } + if idpEvent.Scopes != nil { + oauthCols = append(oauthCols, handler.NewCol(GitHubEnterpriseScopesCol, database.StringArray(idpEvent.Scopes))) + } + return oauthCols +} + 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 9950922e8a..70f49932fe 100644 --- a/internal/query/projection/idp_template_test.go +++ b/internal/query/projection/idp_template_test.go @@ -401,6 +401,528 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { } } +func TestIDPTemplateProjection_reducesGitHub(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 reduceGitHubIDPAdded", + args: args{ + event: getEvent(testEvent( + repository.EventType(instance.GitHubIDPAddedEventType), + instance.AggregateType, + []byte(`{ + "id": "idp-id", + "name": "name", + "clientId": "client_id", + "clientSecret": { + "cryptoType": 0, + "algorithm": "RSA-265", + "keyId": "key-id" + }, + "scopes": ["profile"], + "isCreationAllowed": true, + "isLinkingAllowed": true, + "isAutoCreation": true, + "isAutoUpdate": true +}`), + ), instance.GitHubIDPAddedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceGitHubIDPAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, + previousSequence: 10, + 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)", + expectedArgs: []interface{}{ + "idp-id", + anyArg{}, + anyArg{}, + uint64(15), + "ro-id", + "instance-id", + domain.IDPStateActive, + "name", + domain.IdentityProviderTypeSystem, + domain.IDPTypeGitHub, + true, + true, + true, + true, + }, + }, + { + expectedStmt: "INSERT INTO projections.idp_templates3_github (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 reduceGitHubIDPAdded", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.GitHubIDPAddedEventType), + org.AggregateType, + []byte(`{ + "id": "idp-id", + "name": "name", + "clientId": "client_id", + "clientSecret": { + "cryptoType": 0, + "algorithm": "RSA-265", + "keyId": "key-id" + }, + "scopes": ["profile"], + "isCreationAllowed": true, + "isLinkingAllowed": true, + "isAutoCreation": true, + "isAutoUpdate": true +}`), + ), org.GitHubIDPAddedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceGitHubIDPAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + 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)", + expectedArgs: []interface{}{ + "idp-id", + anyArg{}, + anyArg{}, + uint64(15), + "ro-id", + "instance-id", + domain.IDPStateActive, + "name", + domain.IdentityProviderTypeOrg, + domain.IDPTypeGitHub, + true, + true, + true, + true, + }, + }, + { + expectedStmt: "INSERT INTO projections.idp_templates3_github (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 reduceGitHubIDPChanged minimal", + args: args{ + event: getEvent(testEvent( + repository.EventType(instance.GitHubIDPChangedEventType), + instance.AggregateType, + []byte(`{ + "id": "idp-id", + "isCreationAllowed": true, + "clientId": "id" +}`), + ), instance.GitHubIDPChangedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceGitHubIDPChanged, + want: wantReduce{ + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, + previousSequence: 10, + 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)", + expectedArgs: []interface{}{ + true, + anyArg{}, + uint64(15), + "idp-id", + "instance-id", + }, + }, + { + expectedStmt: "UPDATE projections.idp_templates3_github SET client_id = $1 WHERE (idp_id = $2) AND (instance_id = $3)", + expectedArgs: []interface{}{ + "id", + "idp-id", + "instance-id", + }, + }, + }, + }, + }, + }, + { + name: "instance reduceGitHubIDPChanged", + args: args{ + event: getEvent(testEvent( + repository.EventType(instance.GitHubIDPChangedEventType), + instance.AggregateType, + []byte(`{ + "id": "idp-id", + "name": "name", + "clientId": "client_id", + "clientSecret": { + "cryptoType": 0, + "algorithm": "RSA-265", + "keyId": "key-id" + }, + "scopes": ["profile"], + "isCreationAllowed": true, + "isLinkingAllowed": true, + "isAutoCreation": true, + "isAutoUpdate": true +}`), + ), instance.GitHubIDPChangedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceGitHubIDPChanged, + want: wantReduce{ + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, + previousSequence: 10, + 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)", + expectedArgs: []interface{}{ + "name", + true, + true, + true, + true, + anyArg{}, + uint64(15), + "idp-id", + "instance-id", + }, + }, + { + expectedStmt: "UPDATE projections.idp_templates3_github 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_reducesGitHubEnterprise(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 reduceGitHubEnterpriseIDPAdded", + args: args{ + event: getEvent(testEvent( + repository.EventType(instance.GitHubEnterpriseIDPAddedEventType), + instance.AggregateType, + []byte(`{ + "id": "idp-id", + "name": "name", + "clientId": "client_id", + "clientSecret": { + "cryptoType": 0, + "algorithm": "RSA-265", + "keyId": "key-id" + }, + "authorizationEndpoint": "auth", + "tokenEndpoint": "token", + "userEndpoint": "user", + "scopes": ["profile"], + "isCreationAllowed": true, + "isLinkingAllowed": true, + "isAutoCreation": true, + "isAutoUpdate": true +}`), + ), instance.GitHubEnterpriseIDPAddedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceGitHubEnterpriseIDPAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, + previousSequence: 10, + 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)", + expectedArgs: []interface{}{ + "idp-id", + anyArg{}, + anyArg{}, + uint64(15), + "ro-id", + "instance-id", + domain.IDPStateActive, + "name", + domain.IdentityProviderTypeSystem, + domain.IDPTypeGitHubEnterprise, + true, + true, + true, + true, + }, + }, + { + expectedStmt: "INSERT INTO projections.idp_templates3_github_enterprise (idp_id, instance_id, client_id, client_secret, authorization_endpoint, token_endpoint, user_endpoint, scopes) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", + expectedArgs: []interface{}{ + "idp-id", + "instance-id", + "client_id", + anyArg{}, + "auth", + "token", + "user", + database.StringArray{"profile"}, + }, + }, + }, + }, + }, + }, + { + name: "org reduceGitHubEnterpriseIDPAdded", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.GitHubEnterpriseIDPAddedEventType), + org.AggregateType, + []byte(`{ + "id": "idp-id", + "name": "name", + "clientId": "client_id", + "clientSecret": { + "cryptoType": 0, + "algorithm": "RSA-265", + "keyId": "key-id" + }, + "authorizationEndpoint": "auth", + "tokenEndpoint": "token", + "userEndpoint": "user", + "scopes": ["profile"], + "isCreationAllowed": true, + "isLinkingAllowed": true, + "isAutoCreation": true, + "isAutoUpdate": true +}`), + ), org.GitHubEnterpriseIDPAddedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceGitHubEnterpriseIDPAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + 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)", + expectedArgs: []interface{}{ + "idp-id", + anyArg{}, + anyArg{}, + uint64(15), + "ro-id", + "instance-id", + domain.IDPStateActive, + "name", + domain.IdentityProviderTypeOrg, + domain.IDPTypeGitHubEnterprise, + true, + true, + true, + true, + }, + }, + { + expectedStmt: "INSERT INTO projections.idp_templates3_github_enterprise (idp_id, instance_id, client_id, client_secret, authorization_endpoint, token_endpoint, user_endpoint, scopes) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", + expectedArgs: []interface{}{ + "idp-id", + "instance-id", + "client_id", + anyArg{}, + "auth", + "token", + "user", + database.StringArray{"profile"}, + }, + }, + }, + }, + }, + }, + { + name: "instance reduceGitHubEnterpriseIDPChanged minimal", + args: args{ + event: getEvent(testEvent( + repository.EventType(instance.GitHubEnterpriseIDPChangedEventType), + instance.AggregateType, + []byte(`{ + "id": "idp-id", + "isCreationAllowed": true, + "clientId": "id" +}`), + ), instance.GitHubEnterpriseIDPChangedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceGitHubEnterpriseIDPChanged, + want: wantReduce{ + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, + previousSequence: 10, + 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)", + expectedArgs: []interface{}{ + true, + anyArg{}, + uint64(15), + "idp-id", + "instance-id", + }, + }, + { + expectedStmt: "UPDATE projections.idp_templates3_github_enterprise SET client_id = $1 WHERE (idp_id = $2) AND (instance_id = $3)", + expectedArgs: []interface{}{ + "id", + "idp-id", + "instance-id", + }, + }, + }, + }, + }, + }, + { + name: "instance reduceGitHubEnterpriseIDPChanged", + args: args{ + event: getEvent(testEvent( + repository.EventType(instance.GitHubEnterpriseIDPChangedEventType), + instance.AggregateType, + []byte(`{ + "id": "idp-id", + "name": "name", + "clientId": "client_id", + "clientSecret": { + "cryptoType": 0, + "algorithm": "RSA-265", + "keyId": "key-id" + }, + "authorizationEndpoint": "auth", + "tokenEndpoint": "token", + "userEndpoint": "user", + "scopes": ["profile"], + "isCreationAllowed": true, + "isLinkingAllowed": true, + "isAutoCreation": true, + "isAutoUpdate": true +}`), + ), instance.GitHubEnterpriseIDPChangedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceGitHubEnterpriseIDPChanged, + want: wantReduce{ + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, + previousSequence: 10, + 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)", + expectedArgs: []interface{}{ + "name", + true, + true, + true, + true, + anyArg{}, + uint64(15), + "idp-id", + "instance-id", + }, + }, + { + expectedStmt: "UPDATE projections.idp_templates3_github_enterprise SET (client_id, client_secret, authorization_endpoint, token_endpoint, user_endpoint, scopes) = ($1, $2, $3, $4, $5, $6) WHERE (idp_id = $7) AND (instance_id = $8)", + expectedArgs: []interface{}{ + "client_id", + anyArg{}, + "auth", + "token", + "user", + 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 diff --git a/internal/repository/idp/github.go b/internal/repository/idp/github.go new file mode 100644 index 0000000000..e3f30f20f3 --- /dev/null +++ b/internal/repository/idp/github.go @@ -0,0 +1,306 @@ +package idp + +import ( + "encoding/json" + + "github.com/zitadel/zitadel/internal/crypto" + "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/eventstore/repository" +) + +type GitHubIDPAddedEvent struct { + eventstore.BaseEvent `json:"-"` + + ID string `json:"id"` + Name string `json:"name,omitempty"` + ClientID string `json:"clientId,omitempty"` + ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"` + Scopes []string `json:"scopes,omitempty"` + Options +} + +func NewGitHubIDPAddedEvent( + base *eventstore.BaseEvent, + id, + name, + clientID string, + clientSecret *crypto.CryptoValue, + scopes []string, + options Options, +) *GitHubIDPAddedEvent { + return &GitHubIDPAddedEvent{ + BaseEvent: *base, + ID: id, + Name: name, + ClientID: clientID, + ClientSecret: clientSecret, + Scopes: scopes, + Options: options, + } +} + +func (e *GitHubIDPAddedEvent) Data() interface{} { + return e +} + +func (e *GitHubIDPAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func GitHubIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { + e := &GitHubIDPAddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "IDP-sdfs3", "unable to unmarshal event") + } + + return e, nil +} + +type GitHubIDPChangedEvent struct { + eventstore.BaseEvent `json:"-"` + + ID string `json:"id"` + Name *string `json:"name,omitempty"` + ClientID *string `json:"clientId,omitempty"` + ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"` + Scopes []string `json:"scopes,omitempty"` + OptionChanges +} + +func NewGitHubIDPChangedEvent( + base *eventstore.BaseEvent, + id string, + changes []GitHubIDPChanges, +) (*GitHubIDPChangedEvent, error) { + if len(changes) == 0 { + return nil, errors.ThrowPreconditionFailed(nil, "IDP-BH3dl", "Errors.NoChangesFound") + } + changedEvent := &GitHubIDPChangedEvent{ + BaseEvent: *base, + ID: id, + } + for _, change := range changes { + change(changedEvent) + } + return changedEvent, nil +} + +type GitHubIDPChanges func(*GitHubIDPChangedEvent) + +func ChangeGitHubName(name string) func(*GitHubIDPChangedEvent) { + return func(e *GitHubIDPChangedEvent) { + e.Name = &name + } +} +func ChangeGitHubClientID(clientID string) func(*GitHubIDPChangedEvent) { + return func(e *GitHubIDPChangedEvent) { + e.ClientID = &clientID + } +} + +func ChangeGitHubClientSecret(clientSecret *crypto.CryptoValue) func(*GitHubIDPChangedEvent) { + return func(e *GitHubIDPChangedEvent) { + e.ClientSecret = clientSecret + } +} + +func ChangeGitHubOptions(options OptionChanges) func(*GitHubIDPChangedEvent) { + return func(e *GitHubIDPChangedEvent) { + e.OptionChanges = options + } +} + +func ChangeGitHubScopes(scopes []string) func(*GitHubIDPChangedEvent) { + return func(e *GitHubIDPChangedEvent) { + e.Scopes = scopes + } +} + +func (e *GitHubIDPChangedEvent) Data() interface{} { + return e +} + +func (e *GitHubIDPChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func GitHubIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { + e := &GitHubIDPChangedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "IDP-Sfrth", "unable to unmarshal event") + } + + return e, nil +} + +type GitHubEnterpriseIDPAddedEvent struct { + eventstore.BaseEvent `json:"-"` + + ID string `json:"id"` + Name string `json:"name,omitempty"` + ClientID string `json:"clientId,omitempty"` + ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"` + AuthorizationEndpoint string `json:"authorizationEndpoint,omitempty"` + TokenEndpoint string `json:"tokenEndpoint,omitempty"` + UserEndpoint string `json:"userEndpoint,omitempty"` + Scopes []string `json:"scopes,omitempty"` + Options +} + +func NewGitHubEnterpriseIDPAddedEvent( + base *eventstore.BaseEvent, + id, + name, + clientID string, + clientSecret *crypto.CryptoValue, + authorizationEndpoint, + tokenEndpoint, + userEndpoint string, + scopes []string, + options Options, +) *GitHubEnterpriseIDPAddedEvent { + return &GitHubEnterpriseIDPAddedEvent{ + *base, + id, + name, + clientID, + clientSecret, + authorizationEndpoint, + tokenEndpoint, + userEndpoint, + scopes, + options, + } +} + +func (e *GitHubEnterpriseIDPAddedEvent) Data() interface{} { + return e +} + +func (e *GitHubEnterpriseIDPAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func GitHubEnterpriseIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { + e := &GitHubEnterpriseIDPAddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "IDP-sdfs3", "unable to unmarshal event") + } + + return e, nil +} + +type GitHubEnterpriseIDPChangedEvent struct { + eventstore.BaseEvent `json:"-"` + + ID string `json:"id"` + Name *string `json:"name,omitempty"` + ClientID *string `json:"clientId,omitempty"` + ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"` + AuthorizationEndpoint *string `json:"authorizationEndpoint,omitempty"` + TokenEndpoint *string `json:"tokenEndpoint,omitempty"` + UserEndpoint *string `json:"userEndpoint,omitempty"` + Scopes []string `json:"scopes,omitempty"` + OptionChanges +} + +func NewGitHubEnterpriseIDPChangedEvent( + base *eventstore.BaseEvent, + id string, + changes []GitHubEnterpriseIDPChanges, +) (*GitHubEnterpriseIDPChangedEvent, error) { + if len(changes) == 0 { + return nil, errors.ThrowPreconditionFailed(nil, "IDP-JHKs9", "Errors.NoChangesFound") + } + changedEvent := &GitHubEnterpriseIDPChangedEvent{ + BaseEvent: *base, + ID: id, + } + for _, change := range changes { + change(changedEvent) + } + return changedEvent, nil +} + +type GitHubEnterpriseIDPChanges func(*GitHubEnterpriseIDPChangedEvent) + +func ChangeGitHubEnterpriseName(name string) func(*GitHubEnterpriseIDPChangedEvent) { + return func(e *GitHubEnterpriseIDPChangedEvent) { + e.Name = &name + } +} +func ChangeGitHubEnterpriseClientID(clientID string) func(*GitHubEnterpriseIDPChangedEvent) { + return func(e *GitHubEnterpriseIDPChangedEvent) { + e.ClientID = &clientID + } +} + +func ChangeGitHubEnterpriseClientSecret(clientSecret *crypto.CryptoValue) func(*GitHubEnterpriseIDPChangedEvent) { + return func(e *GitHubEnterpriseIDPChangedEvent) { + e.ClientSecret = clientSecret + } +} + +func ChangeGitHubEnterpriseOptions(options OptionChanges) func(*GitHubEnterpriseIDPChangedEvent) { + return func(e *GitHubEnterpriseIDPChangedEvent) { + e.OptionChanges = options + } +} + +func ChangeGitHubEnterpriseAuthorizationEndpoint(authorizationEndpoint string) func(*GitHubEnterpriseIDPChangedEvent) { + return func(e *GitHubEnterpriseIDPChangedEvent) { + e.AuthorizationEndpoint = &authorizationEndpoint + } +} + +func ChangeGitHubEnterpriseTokenEndpoint(tokenEndpoint string) func(*GitHubEnterpriseIDPChangedEvent) { + return func(e *GitHubEnterpriseIDPChangedEvent) { + e.TokenEndpoint = &tokenEndpoint + } +} + +func ChangeGitHubEnterpriseUserEndpoint(userEndpoint string) func(*GitHubEnterpriseIDPChangedEvent) { + return func(e *GitHubEnterpriseIDPChangedEvent) { + e.UserEndpoint = &userEndpoint + } +} + +func ChangeGitHubEnterpriseScopes(scopes []string) func(*GitHubEnterpriseIDPChangedEvent) { + return func(e *GitHubEnterpriseIDPChangedEvent) { + e.Scopes = scopes + } +} + +func (e *GitHubEnterpriseIDPChangedEvent) Data() interface{} { + return e +} + +func (e *GitHubEnterpriseIDPChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func GitHubEnterpriseIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { + e := &GitHubEnterpriseIDPChangedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "IDP-ASf3r", "unable to unmarshal event") + } + + return e, nil +} diff --git a/internal/repository/instance/eventstore.go b/internal/repository/instance/eventstore.go index 6d552f52ec..50c430ac75 100644 --- a/internal/repository/instance/eventstore.go +++ b/internal/repository/instance/eventstore.go @@ -76,6 +76,10 @@ func RegisterEventMappers(es *eventstore.Eventstore) { RegisterFilterEventMapper(AggregateType, OIDCIDPChangedEventType, OIDCIDPChangedEventMapper). RegisterFilterEventMapper(AggregateType, JWTIDPAddedEventType, JWTIDPAddedEventMapper). RegisterFilterEventMapper(AggregateType, JWTIDPChangedEventType, JWTIDPChangedEventMapper). + RegisterFilterEventMapper(AggregateType, GitHubIDPAddedEventType, GitHubIDPAddedEventMapper). + RegisterFilterEventMapper(AggregateType, GitHubIDPChangedEventType, GitHubIDPChangedEventMapper). + RegisterFilterEventMapper(AggregateType, GitHubEnterpriseIDPAddedEventType, GitHubEnterpriseIDPAddedEventMapper). + RegisterFilterEventMapper(AggregateType, GitHubEnterpriseIDPChangedEventType, GitHubEnterpriseIDPChangedEventMapper). 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 e8250b9fc0..a7bec4ff6d 100644 --- a/internal/repository/instance/idp.go +++ b/internal/repository/instance/idp.go @@ -10,17 +10,21 @@ import ( ) const ( - OAuthIDPAddedEventType eventstore.EventType = "instance.idp.oauth.added" - OAuthIDPChangedEventType eventstore.EventType = "instance.idp.oauth.changed" - OIDCIDPAddedEventType eventstore.EventType = "instance.idp.oidc.added" - OIDCIDPChangedEventType eventstore.EventType = "instance.idp.oidc.changed" - JWTIDPAddedEventType eventstore.EventType = "instance.idp.jwt.added" - JWTIDPChangedEventType eventstore.EventType = "instance.idp.jwt.changed" - GoogleIDPAddedEventType eventstore.EventType = "instance.idp.google.added" - GoogleIDPChangedEventType eventstore.EventType = "instance.idp.google.changed" - LDAPIDPAddedEventType eventstore.EventType = "instance.idp.ldap.added" - LDAPIDPChangedEventType eventstore.EventType = "instance.idp.ldap.changed" - IDPRemovedEventType eventstore.EventType = "instance.idp.removed" + OAuthIDPAddedEventType eventstore.EventType = "instance.idp.oauth.added" + OAuthIDPChangedEventType eventstore.EventType = "instance.idp.oauth.changed" + OIDCIDPAddedEventType eventstore.EventType = "instance.idp.oidc.added" + OIDCIDPChangedEventType eventstore.EventType = "instance.idp.oidc.changed" + JWTIDPAddedEventType eventstore.EventType = "instance.idp.jwt.added" + JWTIDPChangedEventType eventstore.EventType = "instance.idp.jwt.changed" + GitHubIDPAddedEventType eventstore.EventType = "instance.idp.github.added" + GitHubIDPChangedEventType eventstore.EventType = "instance.idp.github.changed" + GitHubEnterpriseIDPAddedEventType eventstore.EventType = "instance.idp.github_enterprise.added" + GitHubEnterpriseIDPChangedEventType eventstore.EventType = "instance.idp.github_enterprise.changed" + GoogleIDPAddedEventType eventstore.EventType = "instance.idp.google.added" + GoogleIDPChangedEventType eventstore.EventType = "instance.idp.google.changed" + LDAPIDPAddedEventType eventstore.EventType = "instance.idp.ldap.added" + LDAPIDPChangedEventType eventstore.EventType = "instance.idp.ldap.changed" + IDPRemovedEventType eventstore.EventType = "instance.idp.removed" ) type OAuthIDPAddedEvent struct { @@ -263,6 +267,164 @@ func JWTIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) return &JWTIDPChangedEvent{JWTIDPChangedEvent: *e.(*idp.JWTIDPChangedEvent)}, nil } +type GitHubIDPAddedEvent struct { + idp.GitHubIDPAddedEvent +} + +func NewGitHubIDPAddedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + clientID string, + clientSecret *crypto.CryptoValue, + scopes []string, + options idp.Options, +) *GitHubIDPAddedEvent { + + return &GitHubIDPAddedEvent{ + GitHubIDPAddedEvent: *idp.NewGitHubIDPAddedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + GitHubIDPAddedEventType, + ), + id, + name, + clientID, + clientSecret, + scopes, + options, + ), + } +} + +func GitHubIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.GitHubIDPAddedEventMapper(event) + if err != nil { + return nil, err + } + + return &GitHubIDPAddedEvent{GitHubIDPAddedEvent: *e.(*idp.GitHubIDPAddedEvent)}, nil +} + +type GitHubIDPChangedEvent struct { + idp.GitHubIDPChangedEvent +} + +func NewGitHubIDPChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id string, + changes []idp.GitHubIDPChanges, +) (*GitHubIDPChangedEvent, error) { + + changedEvent, err := idp.NewGitHubIDPChangedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + GitHubIDPChangedEventType, + ), + id, + changes, + ) + if err != nil { + return nil, err + } + return &GitHubIDPChangedEvent{GitHubIDPChangedEvent: *changedEvent}, nil +} + +func GitHubIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.GitHubIDPChangedEventMapper(event) + if err != nil { + return nil, err + } + + return &GitHubIDPChangedEvent{GitHubIDPChangedEvent: *e.(*idp.GitHubIDPChangedEvent)}, nil +} + +type GitHubEnterpriseIDPAddedEvent struct { + idp.GitHubEnterpriseIDPAddedEvent +} + +func NewGitHubEnterpriseIDPAddedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + clientID string, + clientSecret *crypto.CryptoValue, + authorizationEndpoint, + tokenEndpoint, + userEndpoint string, + scopes []string, + options idp.Options, +) *GitHubEnterpriseIDPAddedEvent { + + return &GitHubEnterpriseIDPAddedEvent{ + GitHubEnterpriseIDPAddedEvent: *idp.NewGitHubEnterpriseIDPAddedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + GitHubEnterpriseIDPAddedEventType, + ), + id, + name, + clientID, + clientSecret, + authorizationEndpoint, + tokenEndpoint, + userEndpoint, + scopes, + options, + ), + } +} + +func GitHubEnterpriseIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.GitHubEnterpriseIDPAddedEventMapper(event) + if err != nil { + return nil, err + } + + return &GitHubEnterpriseIDPAddedEvent{GitHubEnterpriseIDPAddedEvent: *e.(*idp.GitHubEnterpriseIDPAddedEvent)}, nil +} + +type GitHubEnterpriseIDPChangedEvent struct { + idp.GitHubEnterpriseIDPChangedEvent +} + +func NewGitHubEnterpriseIDPChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id string, + changes []idp.GitHubEnterpriseIDPChanges, +) (*GitHubEnterpriseIDPChangedEvent, error) { + + changedEvent, err := idp.NewGitHubEnterpriseIDPChangedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + GitHubEnterpriseIDPChangedEventType, + ), + id, + changes, + ) + if err != nil { + return nil, err + } + return &GitHubEnterpriseIDPChangedEvent{GitHubEnterpriseIDPChangedEvent: *changedEvent}, nil +} + +func GitHubEnterpriseIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.GitHubEnterpriseIDPChangedEventMapper(event) + if err != nil { + return nil, err + } + + return &GitHubEnterpriseIDPChangedEvent{GitHubEnterpriseIDPChangedEvent: *e.(*idp.GitHubEnterpriseIDPChangedEvent)}, nil +} + type GoogleIDPAddedEvent struct { idp.GoogleIDPAddedEvent } diff --git a/internal/repository/org/eventstore.go b/internal/repository/org/eventstore.go index f9ac6e76e7..bb288603ab 100644 --- a/internal/repository/org/eventstore.go +++ b/internal/repository/org/eventstore.go @@ -84,6 +84,10 @@ func RegisterEventMappers(es *eventstore.Eventstore) { RegisterFilterEventMapper(AggregateType, OIDCIDPChangedEventType, OIDCIDPChangedEventMapper). RegisterFilterEventMapper(AggregateType, JWTIDPAddedEventType, JWTIDPAddedEventMapper). RegisterFilterEventMapper(AggregateType, JWTIDPChangedEventType, JWTIDPChangedEventMapper). + RegisterFilterEventMapper(AggregateType, GitHubIDPAddedEventType, GitHubIDPAddedEventMapper). + RegisterFilterEventMapper(AggregateType, GitHubIDPChangedEventType, GitHubIDPChangedEventMapper). + RegisterFilterEventMapper(AggregateType, GitHubEnterpriseIDPAddedEventType, GitHubEnterpriseIDPAddedEventMapper). + RegisterFilterEventMapper(AggregateType, GitHubEnterpriseIDPChangedEventType, GitHubEnterpriseIDPChangedEventMapper). 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 6cbb188e91..b86c57610a 100644 --- a/internal/repository/org/idp.go +++ b/internal/repository/org/idp.go @@ -10,17 +10,21 @@ import ( ) const ( - OAuthIDPAddedEventType eventstore.EventType = "org.idp.oauth.added" - OAuthIDPChangedEventType eventstore.EventType = "org.idp.oauth.changed" - OIDCIDPAddedEventType eventstore.EventType = "org.idp.oidc.added" - OIDCIDPChangedEventType eventstore.EventType = "org.idp.oidc.changed" - JWTIDPAddedEventType eventstore.EventType = "org.idp.jwt.added" - JWTIDPChangedEventType eventstore.EventType = "org.idp.jwt.changed" - GoogleIDPAddedEventType eventstore.EventType = "org.idp.google.added" - GoogleIDPChangedEventType eventstore.EventType = "org.idp.google.changed" - LDAPIDPAddedEventType eventstore.EventType = "org.idp.ldap.added" - LDAPIDPChangedEventType eventstore.EventType = "org.idp.ldap.changed" - IDPRemovedEventType eventstore.EventType = "org.idp.removed" + OAuthIDPAddedEventType eventstore.EventType = "org.idp.oauth.added" + OAuthIDPChangedEventType eventstore.EventType = "org.idp.oauth.changed" + OIDCIDPAddedEventType eventstore.EventType = "org.idp.oidc.added" + OIDCIDPChangedEventType eventstore.EventType = "org.idp.oidc.changed" + JWTIDPAddedEventType eventstore.EventType = "org.idp.jwt.added" + JWTIDPChangedEventType eventstore.EventType = "org.idp.jwt.changed" + GitHubIDPAddedEventType eventstore.EventType = "org.idp.github.added" + GitHubIDPChangedEventType eventstore.EventType = "org.idp.github.changed" + GitHubEnterpriseIDPAddedEventType eventstore.EventType = "org.idp.github_enterprise.added" + GitHubEnterpriseIDPChangedEventType eventstore.EventType = "org.idp.github_enterprise.changed" + GoogleIDPAddedEventType eventstore.EventType = "org.idp.google.added" + GoogleIDPChangedEventType eventstore.EventType = "org.idp.google.changed" + LDAPIDPAddedEventType eventstore.EventType = "org.idp.ldap.added" + LDAPIDPChangedEventType eventstore.EventType = "org.idp.ldap.changed" + IDPRemovedEventType eventstore.EventType = "org.idp.removed" ) type OAuthIDPAddedEvent struct { @@ -263,6 +267,164 @@ func JWTIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) return &JWTIDPChangedEvent{JWTIDPChangedEvent: *e.(*idp.JWTIDPChangedEvent)}, nil } +type GitHubIDPAddedEvent struct { + idp.GitHubIDPAddedEvent +} + +func NewGitHubIDPAddedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + clientID string, + clientSecret *crypto.CryptoValue, + scopes []string, + options idp.Options, +) *GitHubIDPAddedEvent { + + return &GitHubIDPAddedEvent{ + GitHubIDPAddedEvent: *idp.NewGitHubIDPAddedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + GitHubIDPAddedEventType, + ), + id, + name, + clientID, + clientSecret, + scopes, + options, + ), + } +} + +func GitHubIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.GitHubIDPAddedEventMapper(event) + if err != nil { + return nil, err + } + + return &GitHubIDPAddedEvent{GitHubIDPAddedEvent: *e.(*idp.GitHubIDPAddedEvent)}, nil +} + +type GitHubIDPChangedEvent struct { + idp.GitHubIDPChangedEvent +} + +func NewGitHubIDPChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id string, + changes []idp.GitHubIDPChanges, +) (*GitHubIDPChangedEvent, error) { + + changedEvent, err := idp.NewGitHubIDPChangedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + GitHubIDPChangedEventType, + ), + id, + changes, + ) + if err != nil { + return nil, err + } + return &GitHubIDPChangedEvent{GitHubIDPChangedEvent: *changedEvent}, nil +} + +func GitHubIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.GitHubIDPChangedEventMapper(event) + if err != nil { + return nil, err + } + + return &GitHubIDPChangedEvent{GitHubIDPChangedEvent: *e.(*idp.GitHubIDPChangedEvent)}, nil +} + +type GitHubEnterpriseIDPAddedEvent struct { + idp.GitHubEnterpriseIDPAddedEvent +} + +func NewGitHubEnterpriseIDPAddedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + clientID string, + clientSecret *crypto.CryptoValue, + authorizationEndpoint, + tokenEndpoint, + userEndpoint string, + scopes []string, + options idp.Options, +) *GitHubEnterpriseIDPAddedEvent { + + return &GitHubEnterpriseIDPAddedEvent{ + GitHubEnterpriseIDPAddedEvent: *idp.NewGitHubEnterpriseIDPAddedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + GitHubEnterpriseIDPAddedEventType, + ), + id, + name, + clientID, + clientSecret, + authorizationEndpoint, + tokenEndpoint, + userEndpoint, + scopes, + options, + ), + } +} + +func GitHubEnterpriseIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.GitHubEnterpriseIDPAddedEventMapper(event) + if err != nil { + return nil, err + } + + return &GitHubEnterpriseIDPAddedEvent{GitHubEnterpriseIDPAddedEvent: *e.(*idp.GitHubEnterpriseIDPAddedEvent)}, nil +} + +type GitHubEnterpriseIDPChangedEvent struct { + idp.GitHubEnterpriseIDPChangedEvent +} + +func NewGitHubEnterpriseIDPChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id string, + changes []idp.GitHubEnterpriseIDPChanges, +) (*GitHubEnterpriseIDPChangedEvent, error) { + + changedEvent, err := idp.NewGitHubEnterpriseIDPChangedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + GitHubEnterpriseIDPChangedEventType, + ), + id, + changes, + ) + if err != nil { + return nil, err + } + return &GitHubEnterpriseIDPChangedEvent{GitHubEnterpriseIDPChangedEvent: *changedEvent}, nil +} + +func GitHubEnterpriseIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.GitHubEnterpriseIDPChangedEventMapper(event) + if err != nil { + return nil, err + } + + return &GitHubEnterpriseIDPChangedEvent{GitHubEnterpriseIDPChangedEvent: *e.(*idp.GitHubEnterpriseIDPChangedEvent)}, nil +} + type GoogleIDPAddedEvent struct { idp.GoogleIDPAddedEvent } diff --git a/proto/zitadel/admin.proto b/proto/zitadel/admin.proto index 4b0ae485ae..3325cc5e7a 100644 --- a/proto/zitadel/admin.proto +++ b/proto/zitadel/admin.proto @@ -1320,6 +1320,54 @@ service AdminService { }; } + // Add a new GitHub identity provider on the instance + rpc AddGitHubProvider(AddGitHubProviderRequest) returns (AddGitHubProviderResponse) { + option (google.api.http) = { + post: "/idps/github" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "iam.idp.write" + }; + } + + // Change an existing GitHub identity provider on the instance + rpc UpdateGitHubProvider(UpdateGitHubProviderRequest) returns (UpdateGitHubProviderResponse) { + option (google.api.http) = { + put: "/idps/github/{id}" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "iam.idp.write" + }; + } + + // Add a new GitHub Enterprise Server identity provider on the instance + rpc AddGitHubEnterpriseServerProvider(AddGitHubEnterpriseServerProviderRequest) returns (AddGitHubEnterpriseServerProviderResponse) { + option (google.api.http) = { + post: "/idps/github_es" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "iam.idp.write" + }; + } + + // Change an existing GitHub Enterprise Server identity provider on the instance + rpc UpdateGitHubEnterpriseServerProvider(UpdateGitHubEnterpriseServerProviderRequest) returns (UpdateGitHubEnterpriseServerProviderResponse) { + option (google.api.http) = { + put: "/idps/github_es/{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) = { @@ -4429,6 +4477,67 @@ message UpdateJWTProviderResponse { zitadel.v1.ObjectDetails details = 1; } +message AddGitHubProviderRequest { + // GitHub 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 AddGitHubProviderResponse { + zitadel.v1.ObjectDetails details = 1; + string id = 2; +} + +message UpdateGitHubProviderRequest { + 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 UpdateGitHubProviderResponse { + zitadel.v1.ObjectDetails details = 1; +} + +message AddGitHubEnterpriseServerProviderRequest { + string client_id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string client_secret = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string authorization_endpoint = 4 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string token_endpoint = 5 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string user_endpoint = 6 [(validate.rules).string = {min_len: 1, max_len: 200}]; + repeated string scopes = 7 [(validate.rules).repeated = {max_items: 20, items: {string: {min_len: 1, max_len: 100}}}]; + zitadel.idp.v1.Options provider_options = 8; +} + +message AddGitHubEnterpriseServerProviderResponse { + zitadel.v1.ObjectDetails details = 1; + string id = 2; +} + +message UpdateGitHubEnterpriseServerProviderRequest { + string id = 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}]; + // client_secret will only be updated if provided + string client_secret = 4 [(validate.rules).string = {max_len: 200}]; + string authorization_endpoint = 5 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string token_endpoint = 6 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string user_endpoint = 7 [(validate.rules).string = {min_len: 1, max_len: 200}]; + repeated string scopes = 8 [(validate.rules).repeated = {max_items: 20, items: {string: {min_len: 1, max_len: 100}}}]; + zitadel.idp.v1.Options provider_options = 9; +} + +message UpdateGitHubEnterpriseServerProviderResponse { + 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 71afeec27e..1cda8d29ee 100644 --- a/proto/zitadel/idp.proto +++ b/proto/zitadel/idp.proto @@ -253,7 +253,7 @@ enum ProviderType { PROVIDER_TYPE_OAUTH = 4; PROVIDER_TYPE_AZURE_AD = 5; PROVIDER_TYPE_GITHUB = 6; - PROVIDER_TYPE_GITHUB_EE = 7; + PROVIDER_TYPE_GITHUB_ES = 7; PROVIDER_TYPE_GITLAB = 8; PROVIDER_TYPE_GITLAB_SELF_HOSTED = 9; PROVIDER_TYPE_GOOGLE = 10; @@ -267,6 +267,8 @@ message ProviderConfig { OAuthConfig oauth = 4; GenericOIDCConfig oidc = 5; JWTConfig jwt = 6; + GitHubConfig github = 7; + GitHubEnterpriseServerConfig github_es = 8; } } message OAuthConfig { @@ -284,6 +286,19 @@ message GenericOIDCConfig { repeated string scopes = 3; } +message GitHubConfig { + string client_id = 1; + repeated string scopes = 2; +} + +message GitHubEnterpriseServerConfig { + string client_id = 1; + string authorization_endpoint = 2; + string token_endpoint = 3; + string user_endpoint = 4; + repeated string scopes = 5; +} + message GoogleConfig { string client_id = 1; repeated string scopes = 2; diff --git a/proto/zitadel/management.proto b/proto/zitadel/management.proto index 529a58d196..5724270ce4 100644 --- a/proto/zitadel/management.proto +++ b/proto/zitadel/management.proto @@ -6488,7 +6488,7 @@ service ManagementService { }; } - // Add a new OIDC identity provider in the organisation + // Add a new OIDC identity provider in the organization rpc AddGenericOIDCProvider(AddGenericOIDCProviderRequest) returns (AddGenericOIDCProviderResponse) { option (google.api.http) = { post: "/idps/generic_oidc" @@ -6500,7 +6500,7 @@ service ManagementService { }; } - // Change an existing OIDC identity provider in the organisation + // Change an existing OIDC identity provider in the organization rpc UpdateGenericOIDCProvider(UpdateGenericOIDCProviderRequest) returns (UpdateGenericOIDCProviderResponse) { option (google.api.http) = { put: "/idps/generic_oidc/{id}" @@ -6512,7 +6512,7 @@ service ManagementService { }; } - // Add a new JWT identity provider in the organisation + // Add a new JWT identity provider in the organization rpc AddJWTProvider(AddJWTProviderRequest) returns (AddJWTProviderResponse) { option (google.api.http) = { post: "/idps/generic_jwt" @@ -6524,7 +6524,7 @@ service ManagementService { }; } - // Change an existing JWT identity provider in the organisation + // Change an existing JWT identity provider in the organization rpc UpdateJWTProvider(UpdateJWTProviderRequest) returns (UpdateJWTProviderResponse) { option (google.api.http) = { put: "/idps/generic_jwt/{id}" @@ -6536,7 +6536,55 @@ service ManagementService { }; } - // Add a new Google identity provider in the organisation + // Add a new GitHub identity provider in the organization + rpc AddGitHubProvider(AddGitHubProviderRequest) returns (AddGitHubProviderResponse) { + option (google.api.http) = { + post: "/idps/github" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "org.idp.write" + }; + } + + // Change an existing GitHub identity provider in the organization + rpc UpdateGitHubProvider(UpdateGitHubProviderRequest) returns (UpdateGitHubProviderResponse) { + option (google.api.http) = { + put: "/idps/github/{id}" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "org.idp.write" + }; + } + + // Add a new GitHub Enterprise Server identity provider in the organization + rpc AddGitHubEnterpriseServerProvider(AddGitHubEnterpriseServerProviderRequest) returns (AddGitHubEnterpriseServerProviderResponse) { + option (google.api.http) = { + post: "/idps/github_es" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "org.idp.write" + }; + } + + // Change an existing GitHub Enterprise Server identity provider in the organization + rpc UpdateGitHubEnterpriseServerProvider(UpdateGitHubEnterpriseServerProviderRequest) returns (UpdateGitHubEnterpriseServerProviderResponse) { + option (google.api.http) = { + put: "/idps/github_es/{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) = { post: "/idps/google" @@ -11103,6 +11151,67 @@ message UpdateJWTProviderResponse { zitadel.v1.ObjectDetails details = 1; } +message AddGitHubProviderRequest { + // GitHub 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 AddGitHubProviderResponse { + zitadel.v1.ObjectDetails details = 1; + string id = 2; +} + +message UpdateGitHubProviderRequest { + 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 UpdateGitHubProviderResponse { + zitadel.v1.ObjectDetails details = 1; +} + +message AddGitHubEnterpriseServerProviderRequest { + string client_id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string client_secret = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string authorization_endpoint = 4 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string token_endpoint = 5 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string user_endpoint = 6 [(validate.rules).string = {min_len: 1, max_len: 200}]; + repeated string scopes = 7 [(validate.rules).repeated = {max_items: 20, items: {string: {min_len: 1, max_len: 100}}}]; + zitadel.idp.v1.Options provider_options = 8; +} + +message AddGitHubEnterpriseServerProviderResponse { + zitadel.v1.ObjectDetails details = 1; + string id = 2; +} + +message UpdateGitHubEnterpriseServerProviderRequest { + string id = 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}]; + // client_secret will only be updated if provided + string client_secret = 4 [(validate.rules).string = {max_len: 200}]; + string authorization_endpoint = 5 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string token_endpoint = 6 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string user_endpoint = 7 [(validate.rules).string = {min_len: 1, max_len: 200}]; + repeated string scopes = 8 [(validate.rules).repeated = {max_items: 20, items: {string: {min_len: 1, max_len: 100}}}]; + zitadel.idp.v1.Options provider_options = 9; +} + +message UpdateGitHubEnterpriseServerProviderResponse { + 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}];