package projection import ( "context" "database/sql" "encoding/json" "slices" "github.com/muhlemmer/gu" "github.com/zitadel/zitadel/backend/v3/domain" "github.com/zitadel/zitadel/backend/v3/storage/database" v3_sql "github.com/zitadel/zitadel/backend/v3/storage/database/dialect/sql" "github.com/zitadel/zitadel/backend/v3/storage/database/repository" "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/idp" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/zerrors" ) func (p *relationalTablesProjection) reduceGitHubIDPAdded(event eventstore.Event) (*handler.Statement, error) { var orgId *string var idpEvent idp.GitHubIDPAddedEvent switch e := event.(type) { case *org.GitHubIDPAddedEvent: idpEvent = e.GitHubIDPAddedEvent orgId = &idpEvent.Aggregate().ResourceOwner case *instance.GitHubIDPAddedEvent: idpEvent = e.GitHubIDPAddedEvent default: return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-x9a022b", "reduce.wrong.event.type %v", []eventstore.EventType{org.GitHubIDPAddedEventType, instance.GitHubIDPAddedEventType}) } github := domain.Github{ ClientID: idpEvent.ClientID, ClientSecret: idpEvent.ClientSecret, Scopes: idpEvent.Scopes, } payloadJSON, err := json.Marshal(github) if err != nil { return nil, err } return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, _ string) error { tx, ok := ex.(*sql.Tx) if !ok { return zerrors.ThrowInvalidArgumentf(nil, "HANDL-HNpgd", "reduce.wrong.db.pool %T", ex) } repo := repository.IDProviderRepository() return repo.Create(ctx, v3_sql.SQLTx(tx), &domain.IdentityProvider{ InstanceID: idpEvent.Aggregate().InstanceID, OrgID: orgId, ID: idpEvent.ID, State: domain.IDPStateActive, Name: idpEvent.Name, Type: gu.Ptr(domain.IDPTypeGitHub), AllowCreation: idpEvent.IsCreationAllowed, AllowAutoCreation: idpEvent.IsAutoCreation, AllowAutoUpdate: idpEvent.IsAutoUpdate, AllowLinking: idpEvent.IsLinkingAllowed, AutoLinkingField: mapAutoLinkingField(idpEvent.AutoLinkingOption), Payload: payloadJSON, CreatedAt: event.CreatedAt(), UpdatedAt: event.CreatedAt(), }) }), nil } func (p *relationalTablesProjection) reduceGitHubIDPChanged(event eventstore.Event) (*handler.Statement, error) { var orgId *string var idpEvent idp.GitHubIDPChangedEvent switch e := event.(type) { case *org.GitHubIDPChangedEvent: idpEvent = e.GitHubIDPChangedEvent orgId = &idpEvent.Aggregate().ResourceOwner case *instance.GitHubIDPChangedEvent: idpEvent = e.GitHubIDPChangedEvent default: return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-L1U89ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.GitHubIDPChangedEventType, instance.GitHubIDPChangedEventType}) } return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, _ string) error { tx, ok := ex.(*sql.Tx) if !ok { return zerrors.ThrowInternal(nil, "HANDL-HX6ed", "unable to cast to tx executer") } idpRepo := repository.IDProviderRepository() github, err := idpRepo.GetGithub(ctx, v3_sql.SQLTx(tx), database.WithCondition(idpScopedCondition(idpRepo, idpEvent.Agg.InstanceID, idpEvent.ID, orgId))) if err != nil { return err } changes := p.reduceIDPChangedTemplateColumns(idpRepo, idpEvent.Name, idpEvent.OptionChanges) payload := &github.Github payloadChanged := p.reduceGitHubIDPChangedColumns(payload, &idpEvent) if payloadChanged { payloadJSON, err := json.Marshal(payload) if err != nil { return err } changes = append(changes, idpRepo.SetPayload(string(payloadJSON))) } changes = append(changes, idpRepo.SetUpdatedAt(gu.Ptr(event.CreatedAt()))) _, err = idpRepo.Update(ctx, v3_sql.SQLTx(tx), idpScopedCondition(idpRepo, idpEvent.Aggregate().InstanceID, idpEvent.ID, orgId), changes...) return err }), nil } func (p *relationalTablesProjection) reduceGitHubEnterpriseIDPAdded(event eventstore.Event) (*handler.Statement, error) { var orgId *string var idpEvent idp.GitHubEnterpriseIDPAddedEvent switch e := event.(type) { case *org.GitHubEnterpriseIDPAddedEvent: idpEvent = e.GitHubEnterpriseIDPAddedEvent orgId = &idpEvent.Aggregate().ResourceOwner case *instance.GitHubEnterpriseIDPAddedEvent: idpEvent = e.GitHubEnterpriseIDPAddedEvent default: return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Yf3g2a", "reduce.wrong.event.type %v", []eventstore.EventType{org.GitHubEnterpriseIDPAddedEventType, instance.GitHubEnterpriseIDPAddedEventType}) } githubEnterprise := domain.GithubEnterprise{ ClientID: idpEvent.ClientID, ClientSecret: idpEvent.ClientSecret, AuthorizationEndpoint: idpEvent.AuthorizationEndpoint, TokenEndpoint: idpEvent.TokenEndpoint, UserEndpoint: idpEvent.UserEndpoint, Scopes: idpEvent.Scopes, } payloadJSON, err := json.Marshal(githubEnterprise) if err != nil { return nil, err } return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, _ string) error { tx, ok := ex.(*sql.Tx) if !ok { return zerrors.ThrowInvalidArgumentf(nil, "HANDL-Kv4Fu", "reduce.wrong.db.pool %T", ex) } repo := repository.IDProviderRepository() return repo.Create(ctx, v3_sql.SQLTx(tx), &domain.IdentityProvider{ InstanceID: idpEvent.Aggregate().InstanceID, OrgID: orgId, ID: idpEvent.ID, State: domain.IDPStateActive, Name: idpEvent.Name, Type: gu.Ptr(domain.IDPTypeGitHubEnterprise), AllowCreation: idpEvent.IsCreationAllowed, AllowAutoCreation: idpEvent.IsAutoCreation, AllowAutoUpdate: idpEvent.IsAutoUpdate, AllowLinking: idpEvent.IsLinkingAllowed, AutoLinkingField: mapAutoLinkingField(idpEvent.AutoLinkingOption), Payload: payloadJSON, CreatedAt: event.CreatedAt(), UpdatedAt: event.CreatedAt(), }) }), nil } func (p *relationalTablesProjection) reduceGitHubEnterpriseIDPChanged(event eventstore.Event) (*handler.Statement, error) { var orgId *string var idpEvent idp.GitHubEnterpriseIDPChangedEvent switch e := event.(type) { case *org.GitHubEnterpriseIDPChangedEvent: idpEvent = e.GitHubEnterpriseIDPChangedEvent orgId = &idpEvent.Aggregate().ResourceOwner case *instance.GitHubEnterpriseIDPChangedEvent: idpEvent = e.GitHubEnterpriseIDPChangedEvent default: return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-YDg3g", "reduce.wrong.event.type %v", []eventstore.EventType{org.GitHubEnterpriseIDPChangedEventType, instance.GitHubEnterpriseIDPChangedEventType}) } return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, _ string) error { tx, ok := ex.(*sql.Tx) if !ok { return zerrors.ThrowInternal(nil, "HANDL-HX6ed", "unable to cast to tx executer") } idpRepo := repository.IDProviderRepository() githubEnterprise, err := idpRepo.GetGithubEnterprise(ctx, v3_sql.SQLTx(tx), database.WithCondition(idpScopedCondition(idpRepo, idpEvent.Agg.InstanceID, idpEvent.ID, orgId))) if err != nil { return err } changes := p.reduceIDPChangedTemplateColumns(idpRepo, idpEvent.Name, idpEvent.OptionChanges) payload := &githubEnterprise.GithubEnterprise payloadChanged := p.reduceGitHubEnterpriseIDPChangedColumns(payload, &idpEvent) if payloadChanged { payloadJSON, err := json.Marshal(payload) if err != nil { return err } changes = append(changes, idpRepo.SetPayload(string(payloadJSON))) } changes = append(changes, idpRepo.SetUpdatedAt(gu.Ptr(event.CreatedAt()))) _, err = idpRepo.Update(ctx, v3_sql.SQLTx(tx), idpScopedCondition(idpRepo, idpEvent.Aggregate().InstanceID, idpEvent.ID, orgId), changes...) return err }), nil } func (p *relationalTablesProjection) reduceGitHubIDPChangedColumns(payload *domain.Github, idpEvent *idp.GitHubIDPChangedEvent) bool { payloadChanged := false if payload == nil || idpEvent == nil { return payloadChanged } if idpEvent.ClientID != nil && *idpEvent.ClientID != payload.ClientID { payloadChanged = true payload.ClientID = *idpEvent.ClientID } if idpEvent.ClientSecret != nil { payloadChanged = true payload.ClientSecret = idpEvent.ClientSecret } if idpEvent.Scopes != nil && !slices.Equal(idpEvent.Scopes, payload.Scopes) { payloadChanged = true payload.Scopes = idpEvent.Scopes } return payloadChanged } func (p *relationalTablesProjection) reduceGitHubEnterpriseIDPChangedColumns(payload *domain.GithubEnterprise, idpEvent *idp.GitHubEnterpriseIDPChangedEvent) bool { payloadChanged := false if payload == nil || idpEvent == nil { return payloadChanged } if idpEvent.ClientID != nil && *idpEvent.ClientID != payload.ClientID { payloadChanged = true payload.ClientID = *idpEvent.ClientID } if idpEvent.ClientSecret != nil { payloadChanged = true payload.ClientSecret = idpEvent.ClientSecret } if idpEvent.AuthorizationEndpoint != nil && *idpEvent.AuthorizationEndpoint != payload.AuthorizationEndpoint { payloadChanged = true payload.AuthorizationEndpoint = *idpEvent.AuthorizationEndpoint } if idpEvent.TokenEndpoint != nil && *idpEvent.TokenEndpoint != payload.TokenEndpoint { payloadChanged = true payload.TokenEndpoint = *idpEvent.TokenEndpoint } if idpEvent.UserEndpoint != nil && *idpEvent.UserEndpoint != payload.UserEndpoint { payloadChanged = true payload.UserEndpoint = *idpEvent.UserEndpoint } if idpEvent.Scopes != nil && !slices.Equal(idpEvent.Scopes, payload.Scopes) { payloadChanged = true payload.Scopes = idpEvent.Scopes } return payloadChanged }