From 53811a1fd8ede108c78674c6216aca4c516adee3 Mon Sep 17 00:00:00 2001 From: Iraq Jaber Date: Thu, 7 Aug 2025 09:19:23 +0100 Subject: [PATCH] fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! added first event --- backend/v3/domain/id_provider.go | 32 + .../events_testing/id_provider_org_test.go | 294 +++++++++ .../events_testing/id_provider_test.go | 407 ++++++++++++ .../database/repository/id_provider.go | 44 ++ .../projection/idp_template_relational.go | 579 +++++++++++------- 5 files changed, 1120 insertions(+), 236 deletions(-) diff --git a/backend/v3/domain/id_provider.go b/backend/v3/domain/id_provider.go index 4e64b84230..d5f096a80b 100644 --- a/backend/v3/domain/id_provider.go +++ b/backend/v3/domain/id_provider.go @@ -6,6 +6,7 @@ import ( "github.com/zitadel/zitadel/backend/v3/storage/database" "github.com/zitadel/zitadel/internal/crypto" + "github.com/zitadel/zitadel/internal/domain" ) //go:generate enumer -type IDPType -transform lower -trimprefix IDPType @@ -236,6 +237,35 @@ type IDPLDAP struct { LDAP } +type Apple struct { + ClientID string `json:"clientId"` + TeamID string `json:"teamId"` + KeyID string `json:"keyId"` + PrivateKey *crypto.CryptoValue `json:"privateKey"` + Scopes []string `json:"scopes,omitempty"` +} + +type IDPApple struct { + *IdentityProvider + Apple +} + +type SAML struct { + Metadata []byte `json:"metadata,omitempty"` + Key *crypto.CryptoValue `json:"key,omitempty"` + Certificate []byte `json:"certificate,omitempty"` + Binding string `json:"binding,omitempty"` + WithSignedRequest bool `json:"withSignedRequest,omitempty"` + NameIDFormat *domain.SAMLNameIDFormat `json:"nameIDFormat,omitempty"` + TransientMappingAttributeName string `json:"transientMappingAttributeName,omitempty"` + FederatedLogoutEnabled bool `json:"federatedLogoutEnabled,omitempty"` +} + +type IDPSAML struct { + *IdentityProvider + SAML +} + // IDPIdentifierCondition is used to help specify a single identity_provider, // it will either be used as the identity_provider ID or identity_provider name, // as identity_provider can be identified either using (instanceID + OrgID + ID) OR (instanceID + OrgID + name) @@ -316,4 +346,6 @@ type IDProviderRepository interface { GetGitlab(ctx context.Context, id IDPIdentifierCondition, instanceID string, orgID *string) (*IDPGitlab, error) GetGitlabSelfHosting(ctx context.Context, id IDPIdentifierCondition, instanceID string, orgID *string) (*IDPGitlabSelfHosting, error) GetLDAP(ctx context.Context, id IDPIdentifierCondition, instanceID string, orgID *string) (*IDPLDAP, error) + GetApple(ctx context.Context, id IDPIdentifierCondition, instanceID string, orgID *string) (*IDPApple, error) + GetSAML(ctx context.Context, id IDPIdentifierCondition, instanceID string, orgID *string) (*IDPSAML, error) } diff --git a/backend/v3/storage/database/events_testing/id_provider_org_test.go b/backend/v3/storage/database/events_testing/id_provider_org_test.go index 8c9951be73..384141cbc8 100644 --- a/backend/v3/storage/database/events_testing/id_provider_org_test.go +++ b/backend/v3/storage/database/events_testing/id_provider_org_test.go @@ -14,6 +14,7 @@ import ( "github.com/zitadel/zitadel/backend/v3/domain" "github.com/zitadel/zitadel/backend/v3/storage/database" "github.com/zitadel/zitadel/backend/v3/storage/database/repository" + zitadel_internal_domain "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/integration" "github.com/zitadel/zitadel/pkg/grpc/idp" idp_grpc "github.com/zitadel/zitadel/pkg/grpc/idp" @@ -2140,4 +2141,297 @@ func TestServer_TestIDProviderOrgReduces(t *testing.T) { assert.Equal(t, "new_profileAttribute", updateLdap.ProfileAttribute) }, retryDuration, tick) }) + + t.Run("test instance apple added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add apple + beforeCreate := time.Now() + // addApple, err := AdminClient.AddAppleProvider(CTX, &admin.AddAppleProviderRequest{ + addApple, err := MgmtClient.AddAppleProvider(CTX, &management.AddAppleProviderRequest{ + Name: name, + ClientId: "clientID", + TeamId: "teamIDteam", + KeyId: "keyIDKeyId", + PrivateKey: []byte("privateKey"), + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + afterCreate := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository(pool) + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, time.Second*5) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + apple, err := idpRepo.GetApple(CTX, idpRepo.IDCondition(addApple.Id), instanceID, &orgID) + require.NoError(t, err) + + // event instance.idp.apple.added + // idp + assert.Equal(t, instanceID, apple.InstanceID) + assert.Equal(t, orgID, *apple.OrgID) + assert.Equal(t, addApple.Id, apple.ID) + assert.Equal(t, name, apple.Name) + assert.Equal(t, domain.IDPTypeApple.String(), apple.Type) + assert.Equal(t, false, apple.AllowLinking) + assert.Equal(t, false, apple.AllowCreation) + assert.Equal(t, false, apple.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingOptionEmail.String(), apple.AllowAutoLinking) + assert.WithinRange(t, apple.CreatedAt, beforeCreate, afterCreate) + assert.WithinRange(t, apple.UpdatedAt, beforeCreate, afterCreate) + + // apple + assert.Equal(t, "clientID", apple.ClientID) + assert.Equal(t, "teamIDteam", apple.TeamID) + assert.Equal(t, "keyIDKeyId", apple.KeyID) + assert.NotNil(t, apple.PrivateKey) + assert.Equal(t, []string{"scope"}, apple.Scopes) + }, retryDuration, tick) + }) + + t.Run("test instance apple changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add apple + addApple, err := MgmtClient.AddAppleProvider(CTX, &management.AddAppleProviderRequest{ + Name: name, + ClientId: "clientID", + TeamId: "teamIDteam", + KeyId: "keyIDKeyId", + PrivateKey: []byte("privateKey"), + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository(pool) + + var apple *domain.IDPApple + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, time.Second*5) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + apple, err = idpRepo.GetApple(CTX, idpRepo.IDCondition(addApple.Id), instanceID, &orgID) + require.NoError(t, err) + assert.Equal(t, addApple.Id, apple.ID) + }, retryDuration, tick) + + name = "new_" + name + // change apple + beforeCreate := time.Now() + // _, err = AdminClient.UpdateAppleProvider(CTX, &admin.UpdateAppleProviderRequest{ + _, err = MgmtClient.UpdateAppleProvider(CTX, &management.UpdateAppleProviderRequest{ + Id: addApple.Id, + Name: name, + ClientId: "new_clientID", + TeamId: "new_teamID", + KeyId: "new_kKeyId", + PrivateKey: []byte("new_privateKey"), + Scopes: []string{"new_scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + afterCreate := time.Now() + require.NoError(t, err) + + // check values for apple + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(CTX, time.Second*5) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateApple, err := idpRepo.GetApple(CTX, idpRepo.IDCondition(addApple.Id), instanceID, &orgID) + require.NoError(t, err) + + // event nstance.idp.apple.changed + // idp + assert.Equal(t, instanceID, updateApple.InstanceID) + assert.Equal(t, orgID, *updateApple.OrgID) + assert.Equal(t, addApple.Id, updateApple.ID) + assert.Equal(t, name, updateApple.Name) + assert.Equal(t, domain.IDPTypeApple.String(), updateApple.Type) + assert.Equal(t, true, updateApple.AllowLinking) + assert.Equal(t, true, updateApple.AllowCreation) + assert.Equal(t, true, updateApple.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingOptionUserName.String(), updateApple.AllowAutoLinking) + assert.WithinRange(t, updateApple.UpdatedAt, beforeCreate, afterCreate) + + // apple + assert.Equal(t, "new_clientID", updateApple.ClientID) + assert.Equal(t, "new_teamID", updateApple.TeamID) + assert.Equal(t, "new_kKeyId", updateApple.KeyID) + assert.NotEqual(t, apple.PrivateKey, updateApple.PrivateKey) + assert.Equal(t, []string{"new_scope"}, updateApple.Scopes) + }, retryDuration, tick) + }) + + t.Run("test instance saml added reduces", func(t *testing.T) { + name := gofakeit.Name() + federatedLogoutEnabled := false + + // add saml + beforeCreate := time.Now() + addSAML, err := MgmtClient.AddSAMLProvider(CTX, &management.AddSAMLProviderRequest{ + Name: name, + Metadata: &management.AddSAMLProviderRequest_MetadataXml{ + MetadataXml: validSAMLMetadata1, + }, + Binding: idp.SAMLBinding_SAML_BINDING_POST, + WithSignedRequest: false, + TransientMappingAttributeName: &name, + FederatedLogoutEnabled: &federatedLogoutEnabled, + NameIdFormat: idp.SAMLNameIDFormat_SAML_NAME_ID_FORMAT_TRANSIENT.Enum(), + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + afterCreate := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository(pool) + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, time.Second*5) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + saml, err := idpRepo.GetSAML(CTX, idpRepo.IDCondition(addSAML.Id), instanceID, &orgID) + require.NoError(t, err) + + // event instance.idp.saml.added + // idp + assert.Equal(t, instanceID, saml.InstanceID) + assert.Equal(t, orgID, *saml.OrgID) + assert.Equal(t, addSAML.Id, saml.ID) + assert.Equal(t, name, saml.Name) + assert.Equal(t, domain.IDPTypeSAML.String(), saml.Type) + assert.Equal(t, false, saml.AllowLinking) + assert.Equal(t, false, saml.AllowCreation) + assert.Equal(t, false, saml.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingOptionEmail.String(), saml.AllowAutoLinking) + assert.WithinRange(t, saml.CreatedAt, beforeCreate, afterCreate) + assert.WithinRange(t, saml.UpdatedAt, beforeCreate, afterCreate) + + // saml + assert.Equal(t, validSAMLMetadata1, saml.Metadata) + assert.NotNil(t, saml.Key) + assert.NotNil(t, saml.Certificate) + assert.NotNil(t, saml.Binding) + assert.Equal(t, false, saml.WithSignedRequest) + assert.Equal(t, zitadel_internal_domain.SAMLNameIDFormatTransient, *saml.NameIDFormat) + assert.Equal(t, name, saml.TransientMappingAttributeName) + assert.Equal(t, false, saml.FederatedLogoutEnabled) + }, retryDuration, tick) + }) + + t.Run("test instance saml changed reduces", func(t *testing.T) { + name := gofakeit.Name() + federatedLogoutEnabled := false + + // add saml + addSAML, err := MgmtClient.AddSAMLProvider(CTX, &management.AddSAMLProviderRequest{ + Name: name, + // Metadata: &admin.AddSAMLProviderRequest_MetadataXml{ + Metadata: &management.AddSAMLProviderRequest_MetadataXml{ + MetadataXml: validSAMLMetadata1, + }, + Binding: idp.SAMLBinding_SAML_BINDING_POST, + WithSignedRequest: false, + TransientMappingAttributeName: &name, + FederatedLogoutEnabled: &federatedLogoutEnabled, + NameIdFormat: idp.SAMLNameIDFormat_SAML_NAME_ID_FORMAT_TRANSIENT.Enum(), + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository(pool) + + var saml *domain.IDPSAML + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, time.Second*5) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + saml, err = idpRepo.GetSAML(CTX, idpRepo.IDCondition(addSAML.Id), instanceID, &orgID) + require.NoError(t, err) + assert.Equal(t, addSAML.Id, saml.ID) + }, retryDuration, tick) + + name = "new_" + name + federatedLogoutEnabled = true + // change saml + beforeCreate := time.Now() + _, err = MgmtClient.UpdateSAMLProvider(CTX, &management.UpdateSAMLProviderRequest{ + Id: addSAML.Id, + Name: name, + // Metadata: &admin.UpdateSAMLProviderRequest_MetadataXml{ + Metadata: &management.UpdateSAMLProviderRequest_MetadataXml{ + MetadataXml: validSAMLMetadata2, + }, + Binding: idp.SAMLBinding_SAML_BINDING_ARTIFACT, + WithSignedRequest: true, + TransientMappingAttributeName: &name, + FederatedLogoutEnabled: &federatedLogoutEnabled, + NameIdFormat: idp.SAMLNameIDFormat_SAML_NAME_ID_FORMAT_EMAIL_ADDRESS.Enum(), + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + afterCreate := time.Now() + require.NoError(t, err) + + // check values for apple + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(CTX, time.Second*5) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateSAML, err := idpRepo.GetSAML(CTX, idpRepo.IDCondition(addSAML.Id), instanceID, &orgID) + require.NoError(t, err) + + // event instance.idp.saml.changed + // idp + assert.Equal(t, instanceID, updateSAML.InstanceID) + assert.Equal(t, orgID, *updateSAML.OrgID) + assert.Equal(t, addSAML.Id, updateSAML.ID) + assert.Equal(t, name, updateSAML.Name) + assert.Equal(t, domain.IDPTypeSAML.String(), updateSAML.Type) + assert.Equal(t, true, updateSAML.AllowLinking) + assert.Equal(t, true, updateSAML.AllowCreation) + assert.Equal(t, true, updateSAML.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingOptionUserName.String(), updateSAML.AllowAutoLinking) + assert.WithinRange(t, updateSAML.UpdatedAt, beforeCreate, afterCreate) + + // saml + assert.Equal(t, validSAMLMetadata2, updateSAML.Metadata) + assert.NotNil(t, updateSAML.Key) + // assert.NotEqual(t, saml.Key, updateSAML.Key) // https://github.com/zitadel/zitadel/issues/10414 + assert.NotNil(t, updateSAML.Certificate) + // assert.NotEqual(t, saml.Certificate, updateSAML.Certificate) // https://github.com/zitadel/zitadel/issues/10414 + assert.NotNil(t, updateSAML.Binding) + assert.NotEqual(t, saml.Binding, updateSAML.Binding) + assert.Equal(t, true, updateSAML.WithSignedRequest) + assert.Equal(t, zitadel_internal_domain.SAMLNameIDFormatEmailAddress, *updateSAML.NameIDFormat) + assert.Equal(t, name, updateSAML.TransientMappingAttributeName) + assert.Equal(t, true, updateSAML.FederatedLogoutEnabled) + }, retryDuration, tick) + }) } diff --git a/backend/v3/storage/database/events_testing/id_provider_test.go b/backend/v3/storage/database/events_testing/id_provider_test.go index 150d0439db..ff44d80b1c 100644 --- a/backend/v3/storage/database/events_testing/id_provider_test.go +++ b/backend/v3/storage/database/events_testing/id_provider_test.go @@ -13,6 +13,7 @@ import ( "github.com/zitadel/zitadel/backend/v3/domain" "github.com/zitadel/zitadel/backend/v3/storage/database" "github.com/zitadel/zitadel/backend/v3/storage/database/repository" + zitadel_internal_domain "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/integration" "github.com/zitadel/zitadel/pkg/grpc/admin" "github.com/zitadel/zitadel/pkg/grpc/idp" @@ -20,6 +21,123 @@ import ( durationpb "google.golang.org/protobuf/types/known/durationpb" ) +var validSAMLMetadata1 = []byte(` + + + + + + + + + + + + Tyw4csdpNNq0E7wi5FXWdVNkdPNg+cM6kK21VB2+iF0= + + + hWQSYmnBJENy/okk2qRDuHaZiyqpDsdV6BF9/T/LNjUh/8z4dV2NEZvkNhFEyQ+bqdj+NmRWvKqpg1dtgNJxQc32+IsLQvXNYyhMCtyG570/jaTOtm8daV4NKJyTV7SdwM6yfXgubz5YCRTyV13W2gBIFYppIRImIv5NDcjz+lEmWhnrkw8G2wRSFUY7VvkDn9rgsTzw/Pnsw6hlzpjGDYPMPx3ux3kjFVevdhFGNo+VC7t9ozruuGyH3yue9Re6FZoqa4oyWaPSOwei0ZH6UNqkX93Eo5Y49QKwaO8Rm+kWsOhdTqebVmCc+SpWbbrZbQj4nSLgWGlvCkZSivmH7ezr4Ol1ZkRetQ92UQ7xJS7E0y6uXAGvdgpDnyqHCOFfhTS6yqltHtc3m7JZex327xkv6e69uAEOSiv++sifVUIE0h/5u3hZLvwmTPrkoRVY4wgZ4ieb86QPvhw4UPeYapOhCBk5RfjoEFIeYwPUw5rtOlpTyeBJiKMpH1+mDAoa+8HQytZoMrnnY1s612vINtY7jU5igMwIk6MitQpRGibnBVBHRc2A6aE+XS333ganFK9hX6TzNkpHUb66NINDZ8Rgb1thn3MABArGlomtM5/enrAixWExZp70TSElor7SBdBW57H7OZCYUCobZuPRDLsCO6LLKeVrbdygWeRqr/o= + + + MIIFIjCCAwqgAwIBAgICA7YwDQYJKoZIhvcNAQELBQAwLDEQMA4GA1UEChMHWklUQURFTDEYMBYGA1UEAxMPWklUQURFTCBTQU1MIENBMB4XDTI0MTEyNzEwMjc0NFoXDTI1MTEyNzE2Mjc0NFowMjEQMA4GA1UEChMHWklUQURFTDEeMBwGA1UEAxMVWklUQURFTCBTQU1MIG1ldGFkYXRhMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEApEpYT7EjbRBp0Hw7PGCiSgUoJtwd2nwZOhGy5WZVWvraAtHzW5ih2B6UwEShjwCmRJZeKYEN9JKJbpAy2EdL/l2rm/pArVNvSQu6sN4izz5p2rd9NfHAO3/EcvYdrelWLQj8WQx6LVM282Z4wbclp8Jz1y8Ow43352hGfFVc1x8gauoNl5MAy4kdbvs8UqihqcRmEyIOWl6UwTApb+XIRSRz0Yop99Fv9ALJwfUppsx+d4j9rlRDvrQJMJz7GC/19L9INTbY0HsVEiTltdAWHwREwrpwxNJQt42p3W/zpf1mjwXd3qNNDZAr1t2POPP4SXd598kabBZ3EMWGGxFw+NYYajyjG5EFOZw09FFJn2jIcovejvigfdqem5DGPECvHefqcqHkBPGukI3RaotXpAYyAGfnV7slVytSW484IX3KloAJLICbETbFGGsGQzIDw8rUqWyaOCOttw2fVNDyRFUMHrGe1PhJ9qA1If+KCWYD0iJqF03rIEhdrvNSdQNYkRa0DdtpacQLpzQtqsUioODqX0W3uzLceJEXLBbU0ZEk8mWZM/auwMo3ycPNXDVwrb6AkUKar+sqSumUuixw7da3KF1/mynh6M2Eo4NRB16oUiyN0EYrit/RRJjsTdH+71cj0V+8KqO88cBpmm+lO6x4RM5xpOf/EwwQHivxgRkCAwEAAaNIMEYwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMB8GA1UdIwQYMBaAFIzl7uckcPWldirXeOFL3rH6K8FLMA0GCSqGSIb3DQEBCwUAA4ICAQBz+7R99uX1Us9T4BB2RK3RD9K8Q5foNmxJ8GbxpOQFL8IG1DE3FqBssciJkOsKY+1+Y6eow2TgmD9MxfCY444C8k8YDDjxIcs+4dEaWMUxA6NoEy378ciy0U1E6rpYLxWYTxXmsELyODWwTrRNIiWfbBD2m0w9HYbK6QvX6IYQqYoTOJJ3WJKsMCeQ8XhQsJYNINZEq8RsERY/aikOlTWN7ax4Mkr3bfnz1euXGClExCOM6ej4m2I33i4nyYBvvRkRRZRQCfkAQ+5WFVZoVXrQHNe/Oifit7tfLaDuybcjgkzzY3o0YbczzbdV69fVoj53VpR3QQOB+PCF/VJPUMtUFPEC05yH76g24KVBiM/Ws8GaERW1AxgupHSmvTY3GSiwDXQ2NzgDxUHfRHo8rxenJdEcPlGM0DstbUONDSFGLwvGDiidUVtqj1UB4yGL26bgtmwf61G4qsTn9PJMWdRmCeeOf7fmloRxTA0EEey3bulBBHim466tWHUhgOP+g1X0iE7CnwL8aJ//CCiQOAv1O6x5RLyxrmVTehPLr1T8qvnBmxpmuYU0kfbYpO3tMVe7VLabBx0cYh7izClZKHhgEj1w4aE9tIk7nqVAwvVocT3io8RrcKixlnBrFd7RYIuF3+RsYC/kYEgnZYKAig5u2TySgGmJ7nIS24FYW68WDg== + + + + + + + urn:oasis:names:tc:SAML:2.0:profiles:attribute:basic + + + + + + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + + + http://localhost:8080/saml/v2/metadata IDP signing + + MIIFIjCCAwqgAwIBAgICA7QwDQYJKoZIhvcNAQELBQAwLDEQMA4GA1UEChMHWklUQURFTDEYMBYGA1UEAxMPWklUQURFTCBTQU1MIENBMB4XDTI0MTEyNzEwMjUwMloXDTI1MTEyNzE2MjUwMlowMjEQMA4GA1UEChMHWklUQURFTDEeMBwGA1UEAxMVWklUQURFTCBTQU1MIHJlc3BvbnNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2lUgaI6AS/9xvM9DNSWK6Ho64LpK8UIioM26QfvAfeQ/I2pgX6SwWxEbd7qv+PkJzaFTjrXSlwOmWsJYma+UsdyFClaGFRyCgY8SWxPceandC8a+hQIDS/irLd9XF33RWp0b/09HjQl+n0HZ4teUFDUd2U1mUf3XCpn0+Ho316bmi6xSW6zaMy5RsbUl01hgWj2fgapAsGAHSBphwCE3Dz/9I/UfHWQw1k2/UTgjc9uIujcza6WgOxfsKluXYIOxwNKTfmzzOJMUwXz6GRgB2jhQI29MuKOZOITA7pXq5kZKf0lSRU8zKFTMJaK4zAHQ6f877Drr8XdAHemuXGZ2JdH/Dbdwarzy3YBMCWsAYlpeEvaVAdiSpyR7fAZktNuHd39Zg00Vlj2wdc44Vk5yVssW7pv5qnVZ7JTrXX2uBYFecLAXmplQ2ph1VdSXZLEDGgjiNA2T/fBj7G4/VjsuCBZFm1I0KCJp3HWEJx5dwwhSVc5wOJEzl7fMuPYMKWH/RM6P/7LnO1ulpdmiKPa4gHzdg3hDZn42NKcVt3UYf0phtxpWMrZp/DUEeizhckrC4ed6cfGtS3CUtJEqoycrCROJ5Hy+ONHl5Aqxt+JoPU+t/XATuctfPxQVcDr0itHzo2cjh/AVTU+IC7C0oQHSS9CC8Fp58UqbtYwFtSAd7ecCAwEAAaNIMEYwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMB8GA1UdIwQYMBaAFIzl7uckcPWldirXeOFL3rH6K8FLMA0GCSqGSIb3DQEBCwUAA4ICAQAp+IGZScVIbRdCq5HPjlYBPOY7UbL8ZXnlMW/HLELV9GndnULuFhnuQTIdA5dquCsk8RI1fKsScEV1rqWvHZeSo5nVbvUaPJctoD/4GACqE6F8axs1AgSOvpJMyuycjSzSh6gDM1z37Fdqc/2IRqgi7SKdDsfJpi8XW8LtErpp4kyE1rEXopsXG2fe1UH25bZpXraUqYvp61rwVUCazAtV/U7ARG5AnT0mPqzUriIPrfL+v/+2ntV/BSc8/uCqYnHbwpIwjPURCaxo1Pmm6EEkm+V/Ss4ieNwwkD2bLLLST1LoVMim7Ebfy53PEKpsznKsGlVSu0YYKUsStWQVpwhKQw0bQLCJHdpvZtZSDgS9RbSMZz+aY/fpoNx6wDvmMgtdrb3pVXZ8vPKdq9YDrGfFqP60QdZ3CuSHXCM/zX4742GgImJ4KYAcTuF1+BkGf5JLAJOUZBkfCQ/kBT5wr8+EotLxASOC6717whLBYMEG6N8osEk+LDqoJRTLqkzirJsyOHWChKK47yGkdS3HBIZfo91QrJwKpfATYziBjEnqipkTu+6jFylBIkxKTPye4b3vgcodZP8LSNVXAsMGTPNPJxzPWQ37ba4zMnYZ5iUerlaox/SNsn68DT6RajIb1A1JDq+HNFc3hQP2bzk2y5pCax8zo5swjdklnm4clfB2Lw== + + + + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + urn:oasis:names:tc:SAML:2.0:profiles:attribute:basic + + + + + + + + + http://localhost:8080/saml/v2/metadata IDP signing + + MIIFIjCCAwqgAwIBAgICA7QwDQYJKoZIhvcNAQELBQAwLDEQMA4GA1UEChMHWklUQURFTDEYMBYGA1UEAxMPWklUQURFTCBTQU1MIENBMB4XDTI0MTEyNzEwMjUwMloXDTI1MTEyNzE2MjUwMlowMjEQMA4GA1UEChMHWklUQURFTDEeMBwGA1UEAxMVWklUQURFTCBTQU1MIHJlc3BvbnNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2lUgaI6AS/9xvM9DNSWK6Ho64LpK8UIioM26QfvAfeQ/I2pgX6SwWxEbd7qv+PkJzaFTjrXSlwOmWsJYma+UsdyFClaGFRyCgY8SWxPceandC8a+hQIDS/irLd9XF33RWp0b/09HjQl+n0HZ4teUFDUd2U1mUf3XCpn0+Ho316bmi6xSW6zaMy5RsbUl01hgWj2fgapAsGAHSBphwCE3Dz/9I/UfHWQw1k2/UTgjc9uIujcza6WgOxfsKluXYIOxwNKTfmzzOJMUwXz6GRgB2jhQI29MuKOZOITA7pXq5kZKf0lSRU8zKFTMJaK4zAHQ6f877Drr8XdAHemuXGZ2JdH/Dbdwarzy3YBMCWsAYlpeEvaVAdiSpyR7fAZktNuHd39Zg00Vlj2wdc44Vk5yVssW7pv5qnVZ7JTrXX2uBYFecLAXmplQ2ph1VdSXZLEDGgjiNA2T/fBj7G4/VjsuCBZFm1I0KCJp3HWEJx5dwwhSVc5wOJEzl7fMuPYMKWH/RM6P/7LnO1ulpdmiKPa4gHzdg3hDZn42NKcVt3UYf0phtxpWMrZp/DUEeizhckrC4ed6cfGtS3CUtJEqoycrCROJ5Hy+ONHl5Aqxt+JoPU+t/XATuctfPxQVcDr0itHzo2cjh/AVTU+IC7C0oQHSS9CC8Fp58UqbtYwFtSAd7ecCAwEAAaNIMEYwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMB8GA1UdIwQYMBaAFIzl7uckcPWldirXeOFL3rH6K8FLMA0GCSqGSIb3DQEBCwUAA4ICAQAp+IGZScVIbRdCq5HPjlYBPOY7UbL8ZXnlMW/HLELV9GndnULuFhnuQTIdA5dquCsk8RI1fKsScEV1rqWvHZeSo5nVbvUaPJctoD/4GACqE6F8axs1AgSOvpJMyuycjSzSh6gDM1z37Fdqc/2IRqgi7SKdDsfJpi8XW8LtErpp4kyE1rEXopsXG2fe1UH25bZpXraUqYvp61rwVUCazAtV/U7ARG5AnT0mPqzUriIPrfL+v/+2ntV/BSc8/uCqYnHbwpIwjPURCaxo1Pmm6EEkm+V/Ss4ieNwwkD2bLLLST1LoVMim7Ebfy53PEKpsznKsGlVSu0YYKUsStWQVpwhKQw0bQLCJHdpvZtZSDgS9RbSMZz+aY/fpoNx6wDvmMgtdrb3pVXZ8vPKdq9YDrGfFqP60QdZ3CuSHXCM/zX4742GgImJ4KYAcTuF1+BkGf5JLAJOUZBkfCQ/kBT5wr8+EotLxASOC6717whLBYMEG6N8osEk+LDqoJRTLqkzirJsyOHWChKK47yGkdS3HBIZfo91QrJwKpfATYziBjEnqipkTu+6jFylBIkxKTPye4b3vgcodZP8LSNVXAsMGTPNPJxzPWQ37ba4zMnYZ5iUerlaox/SNsn68DT6RajIb1A1JDq+HNFc3hQP2bzk2y5pCax8zo5swjdklnm4clfB2Lw== + + + + +`) + +var validSAMLMetadata2 = []byte(` + + + + + + MIID7TCCAtWgAwIBAgIJANn3qP9lF7M3MA0GCSqGSIb3DQEBCwUAMIGMMQswCQYDVQQGEwJVQTEXMBUGA1UE + CAwOS2hhcmtpdiBSZWdpb24xEDAOBgNVBAcMB0toYXJrb3YxDzANBgNVBAoMBk9yYWNsZTEYMBYGA1UEAwwPc3RzeWJvdi12bTEudWEzMScw + JQYJKoZIhvcNAQkBFhhzZXJnaWkudHN5Ym92QG9yYWNsZS5jb20wHhcNMTUxMjI1MTIyMjU5WhcNMjUxMjI0MTIyMjU5WjCBjDELMAkGA1UE + BhMCVUExFzAVBgNVBAgMDktoYXJraXYgUmVnaW9uMRAwDgYDVQQHDAdLaGFya292MQ8wDQYDVQQKDAZPcmFjbGUxGDAWBgNVBAMMD3N0c3lib + 3Ytdm0xLnVhMzEnMCUGCSqGSIb3DQEJARYYc2VyZ2lpLnRzeWJvdkBvcmFjbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCA + QEAw4OFwuUNjn6xxb/OuAnmQA6mCWPY2hKMoOz0cAajUHjNZZMwGnuEeUyPtEcULfz2MYo1yKQLxVj3pY0HTIQAzpY8o+xCqJFQmdMiakb + PFHlh4z/qqiS5jHng6JCeUpCIxeiTG9JXVwF1ErBEZbwZYjVxa6S+0grVkS3YxuH4uTyqxskuGnHK/AviTHLBrLfSrbFKYuQUrXyy6X22wpzo + bQ3Z+4bhEE8SXQtVbQdy7K0MKWYopNhX05SMTv7yMfUGp8EkGNyJ5Km8AuQt6ZCbVao6cHL2hSujQiN6aMjKbdzHeA1QEicppnnoG/Zefyi/ + okWdlLAaLjcpYrjUSWQJZQIDAQABo1AwTjAdBgNVHQ4EFgQUIKa0zeXmAJsCuNhJjhU0o7KiQgYwHwYDVR0jBBgwFoAUIKa0zeXmAJsCuNhJj + hU0o7KiQgYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAJawU5WRXqkW4emm+djpJAxZ0076qPgEsaaog6ng4MLAlU7RmfIY/ + l0VhXQegvhIBfG4OfduuzGaqd9y4IsQZFJ0yuotl96iEVcqg7hJ1LEY6UT6u6dZyGj1a9I6IlwJm/9CXFZHuVqGJkMfQZ4gaunE4c5gjbQA5/ + +PEJwPorKn48w8bojymV8hriqzrmaP8eQNuZUJsJdnKENOE5/asGyj+R2YfP6bmlOX3q0ozLcyJbXeZ6IvDFdRiDH5wO4JqW/ujvdvC553y + CO3xxsorB4xCupuHu/c7vkzNpaKjYdmGRkqhEqBcCqYSxdwIFc1xhOwYPWKJzgn7pGQsT7yNJg== + + + + + + + MIID7TCCAtWgAwIBAgIJANn3qP9lF7M3MA0GCSqGSIb3DQEBCwUAMIGMMQswCQYDVQQGEwJVQTEXMBUGA1 + UECAwOS2hhcmtpdiBSZWdpb24xEDAOBgNVBAcMB0toYXJrb3YxDzANBgNVBAoMBk9yYWNsZTEYMBYGA1UEAwwPc3RzeWJvdi12bTEud + WEzMScwJQYJKoZIhvcNAQkBFhhzZXJnaWkudHN5Ym92QG9yYWNsZS5jb20wHhcNMTUxMjI1MTIyMjU5WhcNMjUxMjI0MTIyMjU5WjCB + jDELMAkGA1UEBhMCVUExFzAVBgNVBAgMDktoYXJraXYgUmVnaW9uMRAwDgYDVQQHDAdLaGFya292MQ8wDQYDVQQKDAZPcmFjbGUxGDA + WBgNVBAMMD3N0c3lib3Ytdm0xLnVhMzEnMCUGCSqGSIb3DQEJARYYc2VyZ2lpLnRzeWJvdkBvcmFjbGUuY29tMIIBIjANBgkqhkiG9w0B + AQEFAAOCAQ8AMIIBCgKCAQEAw4OFwuUNjn6xxb/OuAnmQA6mCWPY2hKMoOz0cAajUHjNZZMwGnuEeUyPtEcULfz2MYo1yKQLxVj3pY0HT + IQAzpY8o+xCqJFQmdMiakbPFHlh4z/qqiS5jHng6JCeUpCIxeiTG9JXVwF1ErBEZbwZYjVxa6S+0grVkS3YxuH4uTyqxskuGnHK/ + AviTHLBrLfSrbFKYuQUrXyy6X22wpzobQ3Z+4bhEE8SXQtVbQdy7K0MKWYopNhX05SMTv7yMfUGp8EkGNyJ5Km8AuQt6ZCbVao6cHL2h + SujQiN6aMjKbdzHeA1QEicppnnoG/Zefyi/okWdlLAaLjcpYrjUSWQJZQIDAQABo1AwTjAdBgNVHQ4EFgQUIKa0zeXmAJsCuNhJjhU0o + 7KiQgYwHwYDVR0jBBgwFoAUIKa0zeXmAJsCuNhJjhU0o7KiQgYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAJawU5WRXq + kW4emm+djpJAxZ0076qPgEsaaog6ng4MLAlU7RmfIY/l0VhXQegvhIBfG4OfduuzGaqd9y4IsQZFJ0yuotl96iEVcqg7hJ1LEY6UT6u6d + ZyGj1a9I6IlwJm/9CXFZHuVqGJkMfQZ4gaunE4c5gjbQA5/+PEJwPorKn48w8bojymV8hriqzrmaP8eQNuZUJsJdnKENOE5/ + asGyj+R2YfP6bmlOX3q0ozLcyJbXeZ6IvDFdRiDH5wO4JqW/ujvdvC553yCO3xxsorB4xCupuHu/c7vkzNpaKjYdmGRkqhEqBcCqYSxd + wIFc1xhOwYPWKJzgn7pGQsT7yNJg== + + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + + + + Administrator + name@emailprovider.com + +`) + func TestServer_TestIDProviderReduces(t *testing.T) { instanceID := Instance.ID() @@ -2102,4 +2220,293 @@ func TestServer_TestIDProviderReduces(t *testing.T) { assert.Equal(t, "new_profileAttribute", updateLdap.ProfileAttribute) }, retryDuration, tick) }) + + t.Run("test instance apple added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add apple + beforeCreate := time.Now() + addApple, err := AdminClient.AddAppleProvider(CTX, &admin.AddAppleProviderRequest{ + Name: name, + ClientId: "clientID", + TeamId: "teamIDteam", + KeyId: "keyIDKeyId", + PrivateKey: []byte("privateKey"), + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + afterCreate := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository(pool) + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, time.Second*5) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + apple, err := idpRepo.GetApple(CTX, idpRepo.IDCondition(addApple.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.apple.added + // idp + assert.Equal(t, instanceID, apple.InstanceID) + assert.Nil(t, apple.OrgID) + assert.Equal(t, addApple.Id, apple.ID) + assert.Equal(t, name, apple.Name) + assert.Equal(t, domain.IDPTypeApple.String(), apple.Type) + assert.Equal(t, false, apple.AllowLinking) + assert.Equal(t, false, apple.AllowCreation) + assert.Equal(t, false, apple.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingOptionEmail.String(), apple.AllowAutoLinking) + assert.WithinRange(t, apple.CreatedAt, beforeCreate, afterCreate) + assert.WithinRange(t, apple.UpdatedAt, beforeCreate, afterCreate) + + // apple + assert.Equal(t, "clientID", apple.ClientID) + assert.Equal(t, "teamIDteam", apple.TeamID) + assert.Equal(t, "keyIDKeyId", apple.KeyID) + assert.NotNil(t, apple.PrivateKey) + assert.Equal(t, []string{"scope"}, apple.Scopes) + }, retryDuration, tick) + }) + + t.Run("test instance apple changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add apple + addApple, err := AdminClient.AddAppleProvider(CTX, &admin.AddAppleProviderRequest{ + Name: name, + ClientId: "clientID", + TeamId: "teamIDteam", + KeyId: "keyIDKeyId", + PrivateKey: []byte("privateKey"), + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository(pool) + + var apple *domain.IDPApple + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, time.Second*5) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + apple, err = idpRepo.GetApple(CTX, idpRepo.IDCondition(addApple.Id), instanceID, nil) + require.NoError(t, err) + assert.Equal(t, addApple.Id, apple.ID) + }, retryDuration, tick) + + name = "new_" + name + // change apple + beforeCreate := time.Now() + _, err = AdminClient.UpdateAppleProvider(CTX, &admin.UpdateAppleProviderRequest{ + Id: addApple.Id, + Name: name, + ClientId: "new_clientID", + TeamId: "new_teamID", + KeyId: "new_kKeyId", + PrivateKey: []byte("new_privateKey"), + Scopes: []string{"new_scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + afterCreate := time.Now() + require.NoError(t, err) + + // check values for apple + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(CTX, time.Second*5) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateApple, err := idpRepo.GetApple(CTX, idpRepo.IDCondition(addApple.Id), instanceID, nil) + require.NoError(t, err) + + // event nstance.idp.apple.changed + // idp + assert.Equal(t, instanceID, updateApple.InstanceID) + assert.Nil(t, updateApple.OrgID) + assert.Equal(t, addApple.Id, updateApple.ID) + assert.Equal(t, name, updateApple.Name) + assert.Equal(t, domain.IDPTypeApple.String(), updateApple.Type) + assert.Equal(t, true, updateApple.AllowLinking) + assert.Equal(t, true, updateApple.AllowCreation) + assert.Equal(t, true, updateApple.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingOptionUserName.String(), updateApple.AllowAutoLinking) + assert.WithinRange(t, updateApple.UpdatedAt, beforeCreate, afterCreate) + + // apple + assert.Equal(t, "new_clientID", updateApple.ClientID) + assert.Equal(t, "new_teamID", updateApple.TeamID) + assert.Equal(t, "new_kKeyId", updateApple.KeyID) + assert.NotEqual(t, apple.PrivateKey, updateApple.PrivateKey) + assert.Equal(t, []string{"new_scope"}, updateApple.Scopes) + }, retryDuration, tick) + }) + + t.Run("test instance saml added reduces", func(t *testing.T) { + name := gofakeit.Name() + federatedLogoutEnabled := false + + // add saml + beforeCreate := time.Now() + addSAML, err := AdminClient.AddSAMLProvider(CTX, &admin.AddSAMLProviderRequest{ + Name: name, + Metadata: &admin.AddSAMLProviderRequest_MetadataXml{ + MetadataXml: validSAMLMetadata1, + }, + Binding: idp.SAMLBinding_SAML_BINDING_POST, + WithSignedRequest: false, + TransientMappingAttributeName: &name, + FederatedLogoutEnabled: &federatedLogoutEnabled, + NameIdFormat: idp.SAMLNameIDFormat_SAML_NAME_ID_FORMAT_TRANSIENT.Enum(), + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + afterCreate := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository(pool) + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, time.Second*5) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + saml, err := idpRepo.GetSAML(CTX, idpRepo.IDCondition(addSAML.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.saml.added + // idp + assert.Equal(t, instanceID, saml.InstanceID) + assert.Nil(t, saml.OrgID) + assert.Equal(t, addSAML.Id, saml.ID) + assert.Equal(t, name, saml.Name) + assert.Equal(t, domain.IDPTypeSAML.String(), saml.Type) + assert.Equal(t, false, saml.AllowLinking) + assert.Equal(t, false, saml.AllowCreation) + assert.Equal(t, false, saml.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingOptionEmail.String(), saml.AllowAutoLinking) + assert.WithinRange(t, saml.CreatedAt, beforeCreate, afterCreate) + assert.WithinRange(t, saml.UpdatedAt, beforeCreate, afterCreate) + + // saml + assert.Equal(t, validSAMLMetadata1, saml.Metadata) + assert.NotNil(t, saml.Key) + assert.NotNil(t, saml.Certificate) + assert.NotNil(t, saml.Binding) + assert.Equal(t, false, saml.WithSignedRequest) + assert.Equal(t, zitadel_internal_domain.SAMLNameIDFormatTransient, *saml.NameIDFormat) + assert.Equal(t, name, saml.TransientMappingAttributeName) + assert.Equal(t, false, saml.FederatedLogoutEnabled) + }, retryDuration, tick) + }) + + t.Run("test instance saml changed reduces", func(t *testing.T) { + name := gofakeit.Name() + federatedLogoutEnabled := false + + // add saml + addSAML, err := AdminClient.AddSAMLProvider(CTX, &admin.AddSAMLProviderRequest{ + Name: name, + Metadata: &admin.AddSAMLProviderRequest_MetadataXml{ + MetadataXml: validSAMLMetadata1, + }, + Binding: idp.SAMLBinding_SAML_BINDING_POST, + WithSignedRequest: false, + TransientMappingAttributeName: &name, + FederatedLogoutEnabled: &federatedLogoutEnabled, + NameIdFormat: idp.SAMLNameIDFormat_SAML_NAME_ID_FORMAT_TRANSIENT.Enum(), + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository(pool) + + var saml *domain.IDPSAML + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, time.Second*5) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + saml, err = idpRepo.GetSAML(CTX, idpRepo.IDCondition(addSAML.Id), instanceID, nil) + require.NoError(t, err) + assert.Equal(t, addSAML.Id, saml.ID) + }, retryDuration, tick) + + name = "new_" + name + federatedLogoutEnabled = true + // change saml + beforeCreate := time.Now() + _, err = AdminClient.UpdateSAMLProvider(CTX, &admin.UpdateSAMLProviderRequest{ + Id: addSAML.Id, + Name: name, + Metadata: &admin.UpdateSAMLProviderRequest_MetadataXml{ + MetadataXml: validSAMLMetadata2, + }, + Binding: idp.SAMLBinding_SAML_BINDING_ARTIFACT, + WithSignedRequest: true, + TransientMappingAttributeName: &name, + FederatedLogoutEnabled: &federatedLogoutEnabled, + NameIdFormat: idp.SAMLNameIDFormat_SAML_NAME_ID_FORMAT_EMAIL_ADDRESS.Enum(), + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + afterCreate := time.Now() + require.NoError(t, err) + + // check values for apple + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(CTX, time.Second*5) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateSAML, err := idpRepo.GetSAML(CTX, idpRepo.IDCondition(addSAML.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.saml.changed + // idp + assert.Equal(t, instanceID, updateSAML.InstanceID) + assert.Nil(t, updateSAML.OrgID) + assert.Equal(t, addSAML.Id, updateSAML.ID) + assert.Equal(t, name, updateSAML.Name) + assert.Equal(t, domain.IDPTypeSAML.String(), updateSAML.Type) + assert.Equal(t, true, updateSAML.AllowLinking) + assert.Equal(t, true, updateSAML.AllowCreation) + assert.Equal(t, true, updateSAML.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingOptionUserName.String(), updateSAML.AllowAutoLinking) + assert.WithinRange(t, updateSAML.UpdatedAt, beforeCreate, afterCreate) + + // saml + assert.Equal(t, validSAMLMetadata2, updateSAML.Metadata) + assert.NotNil(t, updateSAML.Key) + // assert.NotEqual(t, saml.Key, updateSAML.Key) // https://github.com/zitadel/zitadel/issues/10414 + assert.NotNil(t, updateSAML.Certificate) + // assert.NotEqual(t, saml.Certificate, updateSAML.Certificate) // https://github.com/zitadel/zitadel/issues/10414 + assert.NotNil(t, updateSAML.Binding) + assert.NotEqual(t, saml.Binding, updateSAML.Binding) + assert.Equal(t, true, updateSAML.WithSignedRequest) + assert.Equal(t, zitadel_internal_domain.SAMLNameIDFormatEmailAddress, *updateSAML.NameIDFormat) + assert.Equal(t, name, updateSAML.TransientMappingAttributeName) + assert.Equal(t, true, updateSAML.FederatedLogoutEnabled) + }, retryDuration, tick) + }) } diff --git a/backend/v3/storage/database/repository/id_provider.go b/backend/v3/storage/database/repository/id_provider.go index 54ac2da9a6..3152eacad4 100644 --- a/backend/v3/storage/database/repository/id_provider.go +++ b/backend/v3/storage/database/repository/id_provider.go @@ -339,6 +339,50 @@ func (i *idProvider) GetLDAP(ctx context.Context, id domain.IDPIdentifierConditi return ldap, nil } +func (i *idProvider) GetApple(ctx context.Context, id domain.IDPIdentifierCondition, instnaceID string, orgID *string) (*domain.IDPApple, error) { + apple := &domain.IDPApple{} + var err error + + apple.IdentityProvider, err = i.Get(ctx, id, instnaceID, orgID) + if err != nil { + return nil, err + } + + if apple.Type != domain.IDPTypeApple.String() { + // TODO + return nil, errors.New("WRONG TYPE") + } + + err = json.Unmarshal([]byte(*apple.Payload), apple) + if err != nil { + return nil, err + } + + return apple, nil +} + +func (i *idProvider) GetSAML(ctx context.Context, id domain.IDPIdentifierCondition, instnaceID string, orgID *string) (*domain.IDPSAML, error) { + saml := &domain.IDPSAML{} + var err error + + saml.IdentityProvider, err = i.Get(ctx, id, instnaceID, orgID) + if err != nil { + return nil, err + } + + if saml.Type != domain.IDPTypeSAML.String() { + // TODO + return nil, errors.New("WRONG TYPE") + } + + err = json.Unmarshal([]byte(*saml.Payload), saml) + if err != nil { + return nil, err + } + + return saml, nil +} + // ------------------------------------------------------------- // columns // ------------------------------------------------------------- diff --git a/internal/query/projection/idp_template_relational.go b/internal/query/projection/idp_template_relational.go index be50c8e1f3..ef9b20151c 100644 --- a/internal/query/projection/idp_template_relational.go +++ b/internal/query/projection/idp_template_relational.go @@ -3,7 +3,9 @@ package projection import ( "context" "encoding/json" + "fmt" + "github.com/zitadel/zitadel/backend/v3/domain" "github.com/zitadel/zitadel/backend/v3/storage/database/dialect/postgres" "github.com/zitadel/zitadel/backend/v3/storage/database/repository" @@ -164,30 +166,30 @@ func (p *idpTemplateRelationalProjection) Reducers() []handler.AggregateReducer Event: instance.LDAPIDPChangedEventType, Reduce: p.reduceLDAPIDPChanged, }, - // { - // Event: instance.AppleIDPAddedEventType, - // Reduce: p.reduceAppleIDPAdded, - // }, - // { - // Event: instance.AppleIDPChangedEventType, - // Reduce: p.reduceAppleIDPChanged, - // }, - // { - // Event: instance.SAMLIDPAddedEventType, - // Reduce: p.reduceSAMLIDPAdded, - // }, - // { - // Event: instance.SAMLIDPChangedEventType, - // Reduce: p.reduceSAMLIDPChanged, - // }, + { + Event: instance.AppleIDPAddedEventType, + Reduce: p.reduceAppleIDPAdded, + }, + { + Event: instance.AppleIDPChangedEventType, + Reduce: p.reduceAppleIDPChanged, + }, + { + Event: instance.SAMLIDPAddedEventType, + Reduce: p.reduceSAMLIDPAdded, + }, + { + Event: instance.SAMLIDPChangedEventType, + Reduce: p.reduceSAMLIDPChanged, + }, // { // Event: instance.IDPConfigRemovedEventType, // Reduce: p.reduceIDPConfigRemoved, // }, - // { - // Event: instance.IDPRemovedEventType, - // Reduce: p.reduceIDPRemoved, - // }, + { + Event: instance.IDPRemovedEventType, + Reduce: p.reduceIDPRemoved, + }, // { // Event: instance.InstanceRemovedEventType, // Reduce: reduceInstanceRemovedHelper(IDPTemplateInstanceIDCol), @@ -309,30 +311,30 @@ func (p *idpTemplateRelationalProjection) Reducers() []handler.AggregateReducer Event: org.LDAPIDPChangedEventType, Reduce: p.reduceLDAPIDPChanged, }, - // { - // Event: org.AppleIDPAddedEventType, - // Reduce: p.reduceAppleIDPAdded, - // }, - // { - // Event: org.AppleIDPChangedEventType, - // Reduce: p.reduceAppleIDPChanged, - // }, - // { - // Event: org.SAMLIDPAddedEventType, - // Reduce: p.reduceSAMLIDPAdded, - // }, - // { - // Event: org.SAMLIDPChangedEventType, - // Reduce: p.reduceSAMLIDPChanged, - // }, + { + Event: org.AppleIDPAddedEventType, + Reduce: p.reduceAppleIDPAdded, + }, + { + Event: org.AppleIDPChangedEventType, + Reduce: p.reduceAppleIDPChanged, + }, + { + Event: org.SAMLIDPAddedEventType, + Reduce: p.reduceSAMLIDPAdded, + }, + { + Event: org.SAMLIDPChangedEventType, + Reduce: p.reduceSAMLIDPChanged, + }, // { // Event: org.IDPConfigRemovedEventType, // Reduce: p.reduceIDPConfigRemoved, // }, - // { - // Event: org.IDPRemovedEventType, - // Reduce: p.reduceIDPRemoved, - // }, + { + Event: org.IDPRemovedEventType, + Reduce: p.reduceIDPRemoved, + }, // { // Event: org.OrgRemovedEventType, // Reduce: p.reduceOwnerRemoved, @@ -1862,195 +1864,235 @@ func (p *idpTemplateRelationalProjection) reduceLDAPIDPChanged(event eventstore. // ), nil } -// func (p *idpTemplateProjection) reduceSAMLIDPAdded(event eventstore.Event) (*handler.Statement, error) { -// var idpEvent idp.SAMLIDPAddedEvent -// var idpOwnerType domain.IdentityProviderType -// switch e := event.(type) { -// case *org.SAMLIDPAddedEvent: -// idpEvent = e.SAMLIDPAddedEvent -// idpOwnerType = domain.IdentityProviderTypeOrg -// case *instance.SAMLIDPAddedEvent: -// idpEvent = e.SAMLIDPAddedEvent -// idpOwnerType = domain.IdentityProviderTypeSystem -// default: -// return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-9s02m1", "reduce.wrong.event.type %v", []eventstore.EventType{org.SAMLIDPAddedEventType, instance.SAMLIDPAddedEventType}) -// } +func (p *idpTemplateRelationalProjection) reduceAppleIDPAdded(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idp.AppleIDPAddedEvent + switch e := event.(type) { + case *org.AppleIDPAddedEvent: + idpEvent = e.AppleIDPAddedEvent + case *instance.AppleIDPAddedEvent: + idpEvent = e.AppleIDPAddedEvent + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-YFvg3", "reduce.wrong.event.type %v", []eventstore.EventType{org.AppleIDPAddedEventType /*, instance.AppleIDPAddedEventType*/}) + } -// columns := []handler.Column{ -// handler.NewCol(SAMLIDCol, idpEvent.ID), -// handler.NewCol(SAMLInstanceIDCol, idpEvent.Aggregate().InstanceID), -// handler.NewCol(SAMLMetadataCol, idpEvent.Metadata), -// handler.NewCol(SAMLKeyCol, idpEvent.Key), -// handler.NewCol(SAMLCertificateCol, idpEvent.Certificate), -// handler.NewCol(SAMLBindingCol, idpEvent.Binding), -// handler.NewCol(SAMLWithSignedRequestCol, idpEvent.WithSignedRequest), -// handler.NewCol(SAMLTransientMappingAttributeName, idpEvent.TransientMappingAttributeName), -// handler.NewCol(SAMLFederatedLogoutEnabled, idpEvent.FederatedLogoutEnabled), -// } -// if idpEvent.NameIDFormat != nil { -// columns = append(columns, handler.NewCol(SAMLNameIDFormatCol, *idpEvent.NameIDFormat)) -// } + apple := db_domain.Apple{ + ClientID: idpEvent.ClientID, + TeamID: idpEvent.TeamID, + KeyID: idpEvent.KeyID, + PrivateKey: idpEvent.PrivateKey, + Scopes: idpEvent.Scopes, + } -// return handler.NewMultiStatement( -// &idpEvent, -// handler.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.IDPTypeSAML), -// handler.NewCol(IDPTemplateIsCreationAllowedCol, idpEvent.IsCreationAllowed), -// handler.NewCol(IDPTemplateIsLinkingAllowedCol, idpEvent.IsLinkingAllowed), -// handler.NewCol(IDPTemplateIsAutoCreationCol, idpEvent.IsAutoCreation), -// handler.NewCol(IDPTemplateIsAutoUpdateCol, idpEvent.IsAutoUpdate), -// handler.NewCol(IDPTemplateAutoLinkingCol, idpEvent.AutoLinkingOption), -// }, -// ), -// handler.AddCreateStatement( -// columns, -// handler.WithTableSuffix(IDPTemplateSAMLSuffix), -// ), -// ), nil -// } + payload, err := json.Marshal(apple) + if err != nil { + return nil, err + } -// func (p *idpTemplateProjection) reduceSAMLIDPChanged(event eventstore.Event) (*handler.Statement, error) { -// var idpEvent idp.SAMLIDPChangedEvent -// switch e := event.(type) { -// case *org.SAMLIDPChangedEvent: -// idpEvent = e.SAMLIDPChangedEvent -// case *instance.SAMLIDPChangedEvent: -// idpEvent = e.SAMLIDPChangedEvent -// default: -// return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-o7c0fii4ad", "reduce.wrong.event.type %v", []eventstore.EventType{org.SAMLIDPChangedEventType, instance.SAMLIDPChangedEventType}) -// } + var orgId *string + if idpEvent.Aggregate().ResourceOwner != idpEvent.Agg.InstanceID { + orgId = &idpEvent.Aggregate().ResourceOwner + } -// ops := make([]func(eventstore.Event) handler.Exec, 0, 2) -// ops = append(ops, -// handler.AddUpdateStatement( -// reduceIDPChangedTemplateColumns(idpEvent.Name, idpEvent.CreationDate(), idpEvent.Sequence(), idpEvent.OptionChanges), -// []handler.Condition{ -// handler.NewCond(IDPTemplateIDCol, idpEvent.ID), -// handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), -// }, -// ), -// ) + return handler.NewMultiStatement( + &idpEvent, + handler.AddCreateStatement( + []handler.Column{ + handler.NewCol(IDPTemplateIDCol, idpEvent.ID), + handler.NewCol(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCol(IDPRelationalOrgId, orgId), + handler.NewCol(IDPTemplateNameCol, idpEvent.Name), + handler.NewCol(IDPTemplateTypeCol, db_domain.IDPTypeApple.String()), + handler.NewCol(IDPTemplateStateCol, db_domain.IDPStateActive.String()), + handler.NewCol(IDPRelationalAllowCreationCol, idpEvent.IsCreationAllowed), + handler.NewCol(IDPRelationalAllowLinkingCol, idpEvent.IsLinkingAllowed), + handler.NewCol(IDPRelationalAllowAutoCreationCol, idpEvent.IsAutoCreation), + handler.NewCol(IDPRelationalAllowAutoUpdateCol, idpEvent.IsAutoUpdate), + handler.NewCol(IDPRelationalAllowAutoLinkingCol, db_domain.IDPAutoLinkingOption(idpEvent.AutoLinkingOption).String()), + handler.NewCol(CreatedAt, idpEvent.CreationDate()), + handler.NewCol(IDPRelationalPayloadCol, payload), + }, + ), + ), nil +} -// SAMLCols := reduceSAMLIDPChangedColumns(idpEvent) -// if len(SAMLCols) > 0 { -// ops = append(ops, -// handler.AddUpdateStatement( -// SAMLCols, -// []handler.Condition{ -// handler.NewCond(SAMLIDCol, idpEvent.ID), -// handler.NewCond(SAMLInstanceIDCol, idpEvent.Aggregate().InstanceID), -// }, -// handler.WithTableSuffix(IDPTemplateSAMLSuffix), -// ), -// ) -// } +func (p *idpTemplateRelationalProjection) reduceAppleIDPChanged(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idp.AppleIDPChangedEvent + switch e := event.(type) { + case *org.AppleIDPChangedEvent: + idpEvent = e.AppleIDPChangedEvent + case *instance.AppleIDPChangedEvent: + idpEvent = e.AppleIDPChangedEvent + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-YBez3", "reduce.wrong.event.type %v", []eventstore.EventType{org.AppleIDPChangedEventType /*, instance.AppleIDPChangedEventType*/}) + } -// return handler.NewMultiStatement( -// &idpEvent, -// ops..., -// ), nil -// } + var orgId *string + if idpEvent.Aggregate().ResourceOwner != idpEvent.Agg.InstanceID { + orgId = &idpEvent.Aggregate().ResourceOwner + } -// func (p *idpTemplateProjection) reduceAppleIDPAdded(event eventstore.Event) (*handler.Statement, error) { -// var idpEvent idp.AppleIDPAddedEvent -// var idpOwnerType domain.IdentityProviderType -// switch e := event.(type) { -// case *org.AppleIDPAddedEvent: -// idpEvent = e.AppleIDPAddedEvent -// idpOwnerType = domain.IdentityProviderTypeOrg -// case *instance.AppleIDPAddedEvent: -// idpEvent = e.AppleIDPAddedEvent -// idpOwnerType = domain.IdentityProviderTypeSystem -// default: -// return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-SFvg3", "reduce.wrong.event.type %v", []eventstore.EventType{org.AppleIDPAddedEventType /*, instance.AppleIDPAddedEventType*/}) -// } + apple, err := p.idpRepo.GetApple(context.Background(), p.idpRepo.IDCondition(idpEvent.ID), idpEvent.Agg.InstanceID, orgId) + if err != nil { + return nil, err + } -// return handler.NewMultiStatement( -// &idpEvent, -// handler.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.IDPTypeApple), -// handler.NewCol(IDPTemplateIsCreationAllowedCol, idpEvent.IsCreationAllowed), -// handler.NewCol(IDPTemplateIsLinkingAllowedCol, idpEvent.IsLinkingAllowed), -// handler.NewCol(IDPTemplateIsAutoCreationCol, idpEvent.IsAutoCreation), -// handler.NewCol(IDPTemplateIsAutoUpdateCol, idpEvent.IsAutoUpdate), -// handler.NewCol(IDPTemplateAutoLinkingCol, idpEvent.AutoLinkingOption), -// }, -// ), -// handler.AddCreateStatement( -// []handler.Column{ -// handler.NewCol(AppleIDCol, idpEvent.ID), -// handler.NewCol(AppleInstanceIDCol, idpEvent.Aggregate().InstanceID), -// handler.NewCol(AppleClientIDCol, idpEvent.ClientID), -// handler.NewCol(AppleTeamIDCol, idpEvent.TeamID), -// handler.NewCol(AppleKeyIDCol, idpEvent.KeyID), -// handler.NewCol(ApplePrivateKeyCol, idpEvent.PrivateKey), -// handler.NewCol(AppleScopesCol, database.TextArray[string](idpEvent.Scopes)), -// }, -// handler.WithTableSuffix(IDPTemplateAppleSuffix), -// ), -// ), nil -// } + columns := make([]handler.Column, 0, 7) + reduceIDPRelationalChangedTemplateColumns(idpEvent.Name, idpEvent.OptionChanges, &columns) -// func (p *idpTemplateProjection) reduceAppleIDPChanged(event eventstore.Event) (*handler.Statement, error) { -// var idpEvent idp.AppleIDPChangedEvent -// switch e := event.(type) { -// case *org.AppleIDPChangedEvent: -// idpEvent = e.AppleIDPChangedEvent -// case *instance.AppleIDPChangedEvent: -// idpEvent = e.AppleIDPChangedEvent -// default: -// return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-GBez3", "reduce.wrong.event.type %v", []eventstore.EventType{org.AppleIDPChangedEventType /*, instance.AppleIDPChangedEventType*/}) -// } + payload := &apple.Apple + payloadChanged := reduceAppleIDPRelationalChangedColumns(payload, &idpEvent) + if payloadChanged { + payload, err := json.Marshal(payload) + if err != nil { + return nil, err + } + columns = append(columns, handler.NewCol(IDPRelationalPayloadCol, payload)) + } -// ops := make([]func(eventstore.Event) handler.Exec, 0, 2) -// ops = append(ops, -// handler.AddUpdateStatement( -// reduceIDPChangedTemplateColumns(idpEvent.Name, idpEvent.CreationDate(), idpEvent.Sequence(), idpEvent.OptionChanges), -// []handler.Condition{ -// handler.NewCond(IDPTemplateIDCol, idpEvent.ID), -// handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), -// }, -// ), -// ) -// appleCols := reduceAppleIDPChangedColumns(idpEvent) -// if len(appleCols) > 0 { -// ops = append(ops, -// handler.AddUpdateStatement( -// appleCols, -// []handler.Condition{ -// handler.NewCond(AppleIDCol, idpEvent.ID), -// handler.NewCond(AppleInstanceIDCol, idpEvent.Aggregate().InstanceID), -// }, -// handler.WithTableSuffix(IDPTemplateAppleSuffix), -// ), -// ) -// } + return handler.NewMultiStatement( + &idpEvent, + handler.AddUpdateStatement( + columns, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCond(IDPRelationalOrgId, orgId), + }, + ), + ), nil +} -// return handler.NewMultiStatement( -// &idpEvent, -// ops..., -// ), nil -// } +func (p *idpTemplateRelationalProjection) reduceSAMLIDPAdded(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idp.SAMLIDPAddedEvent + switch e := event.(type) { + case *org.SAMLIDPAddedEvent: + idpEvent = e.SAMLIDPAddedEvent + case *instance.SAMLIDPAddedEvent: + idpEvent = e.SAMLIDPAddedEvent + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Ys02m1", "reduce.wrong.event.type %v", []eventstore.EventType{org.SAMLIDPAddedEventType, instance.SAMLIDPAddedEventType}) + } + + fmt.Printf("@@ >>>>>>>>>>>>>>>>>>>>>>>>>>>> idpEvent.NameIDFormat = %+v\n", idpEvent.NameIDFormat) + + saml := db_domain.SAML{ + Metadata: idpEvent.Metadata, + Key: idpEvent.Key, + Certificate: idpEvent.Certificate, + Binding: idpEvent.Binding, + WithSignedRequest: idpEvent.WithSignedRequest, + NameIDFormat: idpEvent.NameIDFormat, + TransientMappingAttributeName: idpEvent.TransientMappingAttributeName, + FederatedLogoutEnabled: idpEvent.FederatedLogoutEnabled, + } + + payload, err := json.Marshal(saml) + if err != nil { + return nil, err + } + + var orgId *string + if idpEvent.Aggregate().ResourceOwner != idpEvent.Agg.InstanceID { + orgId = &idpEvent.Aggregate().ResourceOwner + } + + return handler.NewMultiStatement( + &idpEvent, + handler.AddCreateStatement( + []handler.Column{ + handler.NewCol(IDPTemplateIDCol, idpEvent.ID), + handler.NewCol(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCol(IDPRelationalOrgId, orgId), + handler.NewCol(IDPTemplateNameCol, idpEvent.Name), + handler.NewCol(IDPTemplateTypeCol, db_domain.IDPTypeSAML.String()), + handler.NewCol(IDPTemplateStateCol, db_domain.IDPStateActive.String()), + handler.NewCol(IDPRelationalAllowCreationCol, idpEvent.IsCreationAllowed), + handler.NewCol(IDPRelationalAllowLinkingCol, idpEvent.IsLinkingAllowed), + handler.NewCol(IDPRelationalAllowAutoCreationCol, idpEvent.IsAutoCreation), + handler.NewCol(IDPRelationalAllowAutoUpdateCol, idpEvent.IsAutoUpdate), + handler.NewCol(IDPRelationalAllowAutoLinkingCol, db_domain.IDPAutoLinkingOption(idpEvent.AutoLinkingOption).String()), + handler.NewCol(CreatedAt, idpEvent.CreationDate()), + handler.NewCol(IDPRelationalPayloadCol, payload), + }, + ), + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceSAMLIDPChanged(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idp.SAMLIDPChangedEvent + switch e := event.(type) { + case *org.SAMLIDPChangedEvent: + idpEvent = e.SAMLIDPChangedEvent + case *instance.SAMLIDPChangedEvent: + idpEvent = e.SAMLIDPChangedEvent + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Y7c0fii4ad", "reduce.wrong.event.type %v", []eventstore.EventType{org.SAMLIDPChangedEventType, instance.SAMLIDPChangedEventType}) + } + + var orgId *string + if idpEvent.Aggregate().ResourceOwner != idpEvent.Agg.InstanceID { + orgId = &idpEvent.Aggregate().ResourceOwner + } + + saml, err := p.idpRepo.GetSAML(context.Background(), p.idpRepo.IDCondition(idpEvent.ID), idpEvent.Agg.InstanceID, orgId) + if err != nil { + return nil, err + } + + columns := make([]handler.Column, 0, 7) + reduceIDPRelationalChangedTemplateColumns(idpEvent.Name, idpEvent.OptionChanges, &columns) + + payload := &saml.SAML + payloadChanged := reduceSAMLIDPRelationalChangedColumns(payload, &idpEvent) + if payloadChanged { + payload, err := json.Marshal(payload) + if err != nil { + return nil, err + } + columns = append(columns, handler.NewCol(IDPRelationalPayloadCol, payload)) + } + + return handler.NewMultiStatement( + &idpEvent, + handler.AddUpdateStatement( + columns, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCond(IDPRelationalOrgId, orgId), + }, + ), + ), nil + + // ops := make([]func(eventstore.Event) handler.Exec, 0, 2) + // ops = append(ops, + // handler.AddUpdateStatement( + // reduceIDPChangedTemplateColumns(idpEvent.Name, idpEvent.CreationDate(), idpEvent.Sequence(), idpEvent.OptionChanges), + // []handler.Condition{ + // handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + // handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + // }, + // ), + // ) + + // if len(SAMLCols) > 0 { + // ops = append(ops, + // handler.AddUpdateStatement( + // SAMLCols, + // []handler.Condition{ + // handler.NewCond(SAMLIDCol, idpEvent.ID), + // handler.NewCond(SAMLInstanceIDCol, idpEvent.Aggregate().InstanceID), + // }, + // handler.WithTableSuffix(IDPTemplateSAMLSuffix), + // ), + // ) + // } + + // return handler.NewMultiStatement( + // &idpEvent, + // ops..., + // ), nil +} // func (p *idpTemplateProjection) reduceIDPConfigRemoved(event eventstore.Event) (*handler.Statement, error) { // var idpEvent idpconfig.IDPConfigRemovedEvent @@ -2072,25 +2114,25 @@ func (p *idpTemplateRelationalProjection) reduceLDAPIDPChanged(event eventstore. // ), nil // } -// func (p *idpTemplateProjection) reduceIDPRemoved(event eventstore.Event) (*handler.Statement, error) { -// var idpEvent idp.RemovedEvent -// switch e := event.(type) { -// case *org.IDPRemovedEvent: -// idpEvent = e.RemovedEvent -// case *instance.IDPRemovedEvent: -// idpEvent = e.RemovedEvent -// default: -// return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-xbcvwin2", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPRemovedEventType, instance.IDPRemovedEventType}) -// } +func (p *idpTemplateRelationalProjection) reduceIDPRemoved(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idp.RemovedEvent + switch e := event.(type) { + case *org.IDPRemovedEvent: + idpEvent = e.RemovedEvent + case *instance.IDPRemovedEvent: + idpEvent = e.RemovedEvent + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-xbcvwin2", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPRemovedEventType, instance.IDPRemovedEventType}) + } -// return handler.NewDeleteStatement( -// &idpEvent, -// []handler.Condition{ -// handler.NewCond(IDPTemplateIDCol, idpEvent.ID), -// handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), -// }, -// ), nil -// } + return handler.NewDeleteStatement( + &idpEvent, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + }, + ), nil +} // func (p *idpTemplateProjection) reduceOwnerRemoved(event eventstore.Event) (*handler.Statement, error) { // e, ok := event.(*org.OrgRemovedEvent) @@ -2757,3 +2799,68 @@ func reduceLDAPIDPRelationalChangedColumns(payload *db_domain.LDAP, idpEvent *id } return payloadChange } + +func reduceAppleIDPRelationalChangedColumns(payload *domain.Apple, idpEvent *idp.AppleIDPChangedEvent) bool { + payloadChange := false + if idpEvent.ClientID != nil { + payloadChange = true + payload.ClientID = *idpEvent.ClientID + } + if idpEvent.TeamID != nil { + payloadChange = true + payload.TeamID = *idpEvent.TeamID + } + if idpEvent.KeyID != nil { + payloadChange = true + payload.KeyID = *idpEvent.KeyID + } + if idpEvent.PrivateKey != nil { + payloadChange = true + payload.PrivateKey = idpEvent.PrivateKey + } + if idpEvent.Scopes != nil { + payloadChange = true + payload.Scopes = idpEvent.Scopes + } + return payloadChange +} + +func reduceSAMLIDPRelationalChangedColumns(payload *domain.SAML, idpEvent *idp.SAMLIDPChangedEvent) bool { + payloadChange := false + if idpEvent.Metadata != nil { + payloadChange = true + payload.Metadata = idpEvent.Metadata + fmt.Println("@@ >>>>>>>>>>>>>>>>>>>>>>>>>>>> METTTADATA") + } + if idpEvent.Key != nil { + payloadChange = true + payload.Key = idpEvent.Key + fmt.Println("@@ >>>>>>>>>>>>>>>>>>>>>>>>>>>> KEEEEEEEEEEEEEEY") + } + if idpEvent.Certificate != nil { + payloadChange = true + payload.Certificate = idpEvent.Certificate + } + if idpEvent.Binding != nil { + payloadChange = true + payload.Binding = *idpEvent.Binding + fmt.Println("@@ >>>>>>>>>>>>>>>>>>>>>>>>>>>> BINDING") + } + if idpEvent.WithSignedRequest != nil { + payloadChange = true + payload.WithSignedRequest = *idpEvent.WithSignedRequest + } + if idpEvent.NameIDFormat != nil { + payloadChange = true + payload.NameIDFormat = idpEvent.NameIDFormat + } + if idpEvent.TransientMappingAttributeName != nil { + payloadChange = true + payload.TransientMappingAttributeName = *idpEvent.TransientMappingAttributeName + } + if idpEvent.FederatedLogoutEnabled != nil { + payloadChange = true + payload.FederatedLogoutEnabled = *idpEvent.FederatedLogoutEnabled + } + return payloadChange +}