From 3042d7ef5cd55086c2f5ae4503af189dfd1e1bdb Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Wed, 8 Mar 2023 11:17:28 +0100 Subject: [PATCH] feat: add github provider template (#5334) Adds possibility to manage and use GitHub (incl. Enterprise Server) template based providers --- internal/actions/object/user.go | 3 +- internal/api/grpc/admin/idp.go | 42 + internal/api/grpc/admin/idp_converter.go | 46 + internal/api/grpc/idp/converter.go | 33 +- internal/api/grpc/management/idp.go | 42 + internal/api/grpc/management/idp_converter.go | 46 + .../api/ui/login/external_provider_handler.go | 53 +- .../resources/images/idp/github-white.png | Bin 0 -> 4837 bytes .../static/resources/images/idp/github.png | Bin 0 -> 6393 bytes .../identity_provider_base.scss | 13 + .../identity_provider_theme.scss | 5 + .../resources/themes/scss/styles/vars.scss | 4 + .../resources/themes/zitadel/css/zitadel.css | 34 +- .../themes/zitadel/css/zitadel.css.map | 2 +- internal/command/idp.go | 19 + internal/command/idp_model.go | 205 ++++ internal/command/instance_idp.go | 272 ++++++ internal/command/instance_idp_model.go | 157 +++ internal/command/instance_idp_test.go | 896 +++++++++++++++++ internal/command/org_idp.go | 268 ++++++ internal/command/org_idp_model.go | 159 +++ internal/command/org_idp_test.go | 908 ++++++++++++++++++ internal/domain/idp.go | 5 +- internal/query/idp_template.go | 205 ++++ internal/query/idp_template_test.go | 335 ++++++- internal/query/projection/idp_template.go | 292 ++++++ .../query/projection/idp_template_test.go | 522 ++++++++++ internal/repository/idp/github.go | 306 ++++++ internal/repository/instance/eventstore.go | 4 + internal/repository/instance/idp.go | 184 +++- internal/repository/org/eventstore.go | 4 + internal/repository/org/idp.go | 184 +++- proto/zitadel/admin.proto | 109 +++ proto/zitadel/idp.proto | 17 +- proto/zitadel/management.proto | 119 ++- 35 files changed, 5451 insertions(+), 42 deletions(-) create mode 100644 internal/api/ui/login/static/resources/images/idp/github-white.png create mode 100644 internal/api/ui/login/static/resources/images/idp/github.png create mode 100644 internal/repository/idp/github.go 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 0000000000000000000000000000000000000000..50b81752278d084ba9d449fff25f4051df162b0f GIT binary patch literal 4837 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..6cb3b705d018006a2bd4200ea94c9d5fb98b6f76 GIT binary patch literal 6393 zcmVt<80drDELIAGL9O(c600d`2O+f$vv5yP-FqK~#7F?VZ1K z8%LJM-y1+@%G#>M+FpAVnW`o4Nbi;iWtR!eHnW`VMWV9HBxRS0%r2Ak7l_I(6B%A4 zD7(xpP8tI` zdHy`?5l{yN>>KPGsz|ZXCE-ZDiK)^X8v1-3TH^jQySG$v&`|AtmZg`gi-nX%J z7Zy5SAmAKW`E$ENgXn!GzMm+=lnn~af|8xilo%}x&loDj(xH!snajcMPvf9w#*g3!jy z56`}%yzuW&oq*jr?(5NQGQ3ToIb=y8%A^_qcYvnI*yz@@$>%af^f0AO< zy3oTc^Ar29O#q}Pv{~v8w7S$P1? zQff=eP!$79vdX^NQdNa`7i7(nwZwn5$*pfSCAZWFcxCPCJ!1ZM0w7=h^2XcmkWFqq zBL%1s@KC(l1VABhM~jHP7qB}fV*WP*pip#(*lPi=zPItnzL5V)0F(lE-hBHH%T~nu zQF|k(yMz$IFjem(P zZv+hS0v-4zVlMcs(-OzD>y&c}9|4+#KWoN&OKN1ueH zw&^MLGK1VIk}etqfIeEXcHJ5-kS9h#vP(DU5qmv$DP+ z0`5?m6ci8VE?}R|d;2f>cWKV+&d0XU9qVqt4|lr=xXS@OKKqXL(!5_Q>+L%>IJ!?I zQq=iy?gAd(?e$>T81GxRW}&vBZZle<8`hNHgH_HLYi*6;$82ct`1xX%Yq@Phq94pR zR5pQmaQw+fcPU456|hf7MoHY~IIOO_+9$|;|JegjZSAj?77T6xSY?;WP*jM0y zua$A}T83rWbL9K6LkWostx)Zo5?V1G*yr`86)Y5i%er5pWqTgJ%}&CX^#u1QL$Vj}`o52uyou~H@imYvSm zIYusH3u=jEqRB^$xt&!ryi5cv)|UYA5KoJ1T3KmkVFCMWeF5+l(M%Rrcwqs<`T~%S zGhRFvUP!>Oz5t|$$=qD@qQgQ0hV=ztAr{U^rxvjD-;D?NE$3ixsi4+)e_z{Xq!+Qm zsRcY}P)EaM_JHZP1Zs)gNFx7P$O@--p(7pcv!VEf_n=x__)bT+6gKH^t)&vM+_KTq zN`~P=*OsWMV~vWIT>GgMq!KV^c+WL&5$zDD1#*#J8ts!#T1njK*aFt-K0EOm-Yly% zD<}uogW9mlO*@Gj9p8mk>OMyUz63nWo0UQw2OPc=m<{g#1#B8h&VTjwIs%^I zTF@$3M`u$)+KB?@hMKvmJpy1sG_0c_NMeDFlHuJA!uc;)7$*LbJZG9FrwLev3*GF) z0)xeg$bUmHO_RZtFRBpm=_xEQSR7{m*HOUq+lgPF^hJAc{4OZ~C6pi&j0y|9Jn8F+ z2YdriH8@b<$+3y=LbK8-gaA|(P7(tH0CX@p24)>eECA|)p(GYq$uSZDS)ioup?WTK zoY^q|R2kI*o>t%uKwUr*3)CJhm4}m1E#Q6=$6a7?v{W8WLbZU+04_9G94(cHlTa<- zX;-WONQB~J)5!u>P~0tOx%LRWXPNwGq9!MoQYt9!7MMt_>jOMOK@y9T2v`f&0{@Nx zSO6{k-=;CGlv0TWR?@o~c#D?)Z-%%x>Fd)$0j(KwXsEGpB&?9IJ)jKFC7cD0lk)dxVeSNY8RuTgXQ3L^lh3Jq1rfG7T zfP16_>jGUT08+5B*6xrJlDW{4A{W|F8;LBC3PlMllSIH5jINQL&ELR{25Hday-h2w znkeAYC0+fN&46wY07+pT@vm_7NjTA{P86_~flnh42ZN-z_*c(8;Hd_6YAL0bYAgrh zV2}{Iz7=_GJT;`9DquFOYW8mPB5e@>F$u`LPfD0I2RoSYBvpwlQuKy^auN60C>mZc zE1aDr;2!Csv-&69H%mY{T~dZI$VP)07(Ll%q5pp=1T2|oEuA@j z!kF7gW`S8)FKtVk`#ft3=j;ppMx7OIHD9MY1i&;RbB`2ZXm&Drj(~M#q6Id};u}yH z+N`gGXD5^Awbbd7GUN@CH;Mpw6=l}f5zN-$Oab?ov>hd#Vua?)D}g1FUjP%-CdznD(Sy{V!PowpXqrEt7WxJ%4 zR-ery0=33%;>_EmlkU84m@8n71s!8_R@U2arEAQ9%~Mj!;AI8^c5$#?D{L|MP-0n6 zR@SfH*XTN*!`*rDuMlrCgVs3soR&>sJV92vUaYQPy=_IH+56g$^G$I_t8_^*vI{pa znkNKmfp}a-Z`|wPAfD!!VzTny#y5&O7)&NG4~{?i=q`cEB1tQWd-b}`=k?D=hX+^U zd~fXGW;Uh$n6wk|ot5{l>N^hvv8aN09n9Uh-x^!MY-o?FfZ=V3xO!AZycQEsY-1VQ zg%&E|Mvs6yT^ZadgH2RcLA*)aXCcvi;7YjBBgCCv-}n&KTDtk;di#bk)v&yd1n#qt zNWhhGqkpC?ZWlzX6Dg5ovZo7G@d_!K`z$1Kp@r4;jV~&*+l|9!`}ot3b_jTnY`DWR z*$!2Rr0%nj$N~$Ma-+wQoAEXkW|GTa17UrH{hM4Pr_XSrQwc;0&~xpsyFWE z{o}(haaYyE7TA%()N4cHd=r^R67!=)Pw|LwSKr%sBpy-q#YEdjxVpTxA-#?in4b32Bm7Bbt7iYYK571jz0~zlRRa0&APV*3V9r7m6^IG;K#=whg|}( zaYsQ7x?wj(nQ7Ibnj&lH>?L1|bN6@3^V74k*51z83U`kW4>lzrGn_V%xvn@X`x|Q0AhLqxj{OpvERfhN-aYy>yhSNlNWjht|6snMELotS zLaea~%zYn@8DwX56CMM8Cfx<4J!slpRwFLVX;8;R(FO!Nou=U{i{w-m60oqk-rhBo z@ic@5MC|#k6tT)y#3tk*I512-&B7L|y0k>CGp05NHo<7jhRqna?W$U?>RD};ENXq- z-$4s9ENlCMvL-MO`ridRX%@HAt7UurmwZcunB@WiODQ8nx)6(6U!g$@^3_)_PTu_e zWl4c&>mnKc=f(y4>+ddK{_>mudGS2SQ{{Jh`>o6S*22lbxc7@p+->`2{>$-k_<|Jh z%~vm;zwzefi}n}q5J-hs-_H)ih0Br`w!lJeR(J?A?KUFbNxECP-bltg_1aR{E>|93nl#jp2ooFm=NfD@Bx< zQOQiet^s_MuTVxJPTJ#n@S22YNyU_q>K-a<*! zfQ4a!f0yz`n$pS5l?3>cbm8jVXo3}<1MeL@&;D+C<^mR)1-Yv{FprYN!@juE zY?3uD)48@C))tT#b{PfD3h32g$EAT1&iLhKQxp2vrp2!{GBF z;14KAaucv1?rK3r6rD7Et4b1amnw>E+NjL>8Cm;z-wV%Gz(P?)6ecqF(+u$*ig>fA zg%<=>U*M{T!Doi7r@>3wrku%Lzy-R}t>){LY9hOM3JoXXypu58t$L>px#LWLWIYve zH8ght3x#EVjk%r13Ja20Iywxu953aIRVBU;QX5kYXCb z^W7{i2#h*kT8nZsX&YO+0rVoGeHjMVKdo0Q9e3HEl9jqv3+@)VQKxS!o92gESK7_B z$@PA&>vFiTfQLKiu6($LY)h_HjC{20uJ`UQej?GAL(3DMeMh}I3HDWjKJ`qYtI8kF z+agn;g+hf|U}0sgE&ZIIQl2!dyNWiirI2@X2cIzm{^0Y^itQC%NDMrVi-+?*x*25K za2|lU*toZ7@d||tSa3%-`Q8lbB(2T@AT`W;c~)D^q7(rOx!(+e6$S+$Yq zr3qNhha348P;^$-+o{fl0f@tBmRFfc%hCiaxJ<9qisp6=&D@784RXV--LfyHlqz6B zDw8e~m+i|$VI#Ao#7Q*^!~ zn&_v$=amOQ4RTcEVa)p~-X*anQC0^@P*Xh2Hcvx^fCVSwk{hyvI>2|eh*wY}U}4yh zeG?-*K;}sAGQ+pD&1+UAU_lxJG$X!-{=*JlY`0nS2;T`QAMAZve zkmMHPVh{%x?*@ELTe4~zl@PEXZqV6le665iYN?RwECS`hym$7JuT^QhO{H3JOP?+K z>CWm}JCw?;VMP@vkiL(vxrA576=zh!>W)(x3p|b-2NW}`4EPVbW5=qv%&$_}AsEBV z;+D0>U0CB9GP1fA74C>iTHtYDjq6CYt?oFr7()eXToYC| z4_B1&JzuGlc!gRCc!U&xWIo6nlmyGLyv-^UWu&2&0v5!rmTn8&=WD2`)`u(FvBH&M z+HT@yO{uMbM;sl6q105%RWej^DPVZ*PeP$O3wK2A1w3LDA4ABVGE7iOoU8HLUtZKA z3!Q}F;@Gtr>n+1{)22r{1WMz)!Js6lXt$0r?mQsiDU5`?vexb})0QE#aC=*hs&Co* zOB6PLpbU`Y6v+&tE`h0d-&WQaq+RNOY1>-l>uJxCCG%Z}2J$QG8&B=04khK>O%~xk zM0^_$2sj0)+-pUh4i`nd7Gm=>{xdkVqTTPG(gV23$$)?tK& zNi|~SpW1gQF!!f^gSEEC@MAW#2Wy)i2sk6e>R78Rjo{Bazq=nlQEO zPIhAR2|W|hV{2_gSX%%900000000000000000000;FtVA#ht2v8mJ-W00000NkvXX Hu0mjfZ$b4` literal 0 HcmV?d00001 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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","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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","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}];