From 08f41e034e760b11762d9a7025ef04048e5101d5 Mon Sep 17 00:00:00 2001 From: Iraq <66622793+kkrime@users.noreply.github.com> Date: Wed, 24 Sep 2025 15:19:09 +0200 Subject: [PATCH] feat(idp_table_relational): adding inital idp tables for relational repository (#10334) --- backend/v3/domain/azuretenanttype_enumer.go | 82 + backend/v3/domain/errors.go | 30 +- backend/v3/domain/id_provider.go | 355 +++ .../v3/domain/idpautolinkingfield_enumer.go | 79 + backend/v3/domain/idpstate_enumer.go | 109 + backend/v3/domain/idptype_enumer.go | 119 + backend/v3/domain/organization.go | 2 +- .../migration/005_identity_providers_table.go | 16 + .../005_identity_providers_table/down.sql | 2 + .../005_identity_providers_table/up.sql | 44 + .../database/events_testing/events_test.go | 8 + .../id_provider_instance_test.go | 2530 +++++++++++++++++ .../events_testing/id_provider_org_test.go | 2414 ++++++++++++++++ .../database/repository/id_provider.go | 638 +++++ .../database/repository/id_provider_test.go | 1954 +++++++++++++ .../storage/database/repository/org_test.go | 42 +- internal/api/grpc/admin/instance.go | 4 +- .../projection/idp_template_relational.go | 2523 ++++++++++++++++ internal/query/projection/projection.go | 3 + 19 files changed, 10917 insertions(+), 37 deletions(-) create mode 100644 backend/v3/domain/azuretenanttype_enumer.go create mode 100644 backend/v3/domain/id_provider.go create mode 100644 backend/v3/domain/idpautolinkingfield_enumer.go create mode 100644 backend/v3/domain/idpstate_enumer.go create mode 100644 backend/v3/domain/idptype_enumer.go create mode 100644 backend/v3/storage/database/dialect/postgres/migration/005_identity_providers_table.go create mode 100644 backend/v3/storage/database/dialect/postgres/migration/005_identity_providers_table/down.sql create mode 100644 backend/v3/storage/database/dialect/postgres/migration/005_identity_providers_table/up.sql create mode 100644 backend/v3/storage/database/events_testing/id_provider_instance_test.go create mode 100644 backend/v3/storage/database/events_testing/id_provider_org_test.go create mode 100644 backend/v3/storage/database/repository/id_provider.go create mode 100644 backend/v3/storage/database/repository/id_provider_test.go create mode 100644 internal/query/projection/idp_template_relational.go diff --git a/backend/v3/domain/azuretenanttype_enumer.go b/backend/v3/domain/azuretenanttype_enumer.go new file mode 100644 index 00000000000..0a083ba6d5c --- /dev/null +++ b/backend/v3/domain/azuretenanttype_enumer.go @@ -0,0 +1,82 @@ +// Code generated by "enumer -type AzureTenantType -transform lower -trimprefix AzureTenantType"; DO NOT EDIT. + +package domain + +import ( + "fmt" + "strings" +) + +const _AzureTenantTypeName = "commonorganizationsconsumers" + +var _AzureTenantTypeIndex = [...]uint8{0, 6, 19, 28} + +const _AzureTenantTypeLowerName = "commonorganizationsconsumers" + +func (i AzureTenantType) String() string { + if i >= AzureTenantType(len(_AzureTenantTypeIndex)-1) { + return fmt.Sprintf("AzureTenantType(%d)", i) + } + return _AzureTenantTypeName[_AzureTenantTypeIndex[i]:_AzureTenantTypeIndex[i+1]] +} + +// An "invalid array index" compiler error signifies that the constant values have changed. +// Re-run the stringer command to generate them again. +func _AzureTenantTypeNoOp() { + var x [1]struct{} + _ = x[AzureTenantTypeCommon-(0)] + _ = x[AzureTenantTypeOrganizations-(1)] + _ = x[AzureTenantTypeConsumers-(2)] +} + +var _AzureTenantTypeValues = []AzureTenantType{AzureTenantTypeCommon, AzureTenantTypeOrganizations, AzureTenantTypeConsumers} + +var _AzureTenantTypeNameToValueMap = map[string]AzureTenantType{ + _AzureTenantTypeName[0:6]: AzureTenantTypeCommon, + _AzureTenantTypeLowerName[0:6]: AzureTenantTypeCommon, + _AzureTenantTypeName[6:19]: AzureTenantTypeOrganizations, + _AzureTenantTypeLowerName[6:19]: AzureTenantTypeOrganizations, + _AzureTenantTypeName[19:28]: AzureTenantTypeConsumers, + _AzureTenantTypeLowerName[19:28]: AzureTenantTypeConsumers, +} + +var _AzureTenantTypeNames = []string{ + _AzureTenantTypeName[0:6], + _AzureTenantTypeName[6:19], + _AzureTenantTypeName[19:28], +} + +// AzureTenantTypeString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func AzureTenantTypeString(s string) (AzureTenantType, error) { + if val, ok := _AzureTenantTypeNameToValueMap[s]; ok { + return val, nil + } + + if val, ok := _AzureTenantTypeNameToValueMap[strings.ToLower(s)]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to AzureTenantType values", s) +} + +// AzureTenantTypeValues returns all values of the enum +func AzureTenantTypeValues() []AzureTenantType { + return _AzureTenantTypeValues +} + +// AzureTenantTypeStrings returns a slice of all String values of the enum +func AzureTenantTypeStrings() []string { + strs := make([]string, len(_AzureTenantTypeNames)) + copy(strs, _AzureTenantTypeNames) + return strs +} + +// IsAAzureTenantType returns "true" if the value is listed in the enum definition. "false" otherwise +func (i AzureTenantType) IsAAzureTenantType() bool { + for _, v := range _AzureTenantTypeValues { + if i == v { + return true + } + } + return false +} diff --git a/backend/v3/domain/errors.go b/backend/v3/domain/errors.go index a11c31c07d4..cb1a192c82f 100644 --- a/backend/v3/domain/errors.go +++ b/backend/v3/domain/errors.go @@ -1,7 +1,29 @@ package domain -import "errors" - -var ( - ErrNoAdminSpecified = errors.New("at least one admin must be specified") +import ( + "errors" + "fmt" ) + +var ErrNoAdminSpecified = errors.New("at least one admin must be specified") + +type wrongIDPTypeError struct { + expected IDPType + got string +} + +func NewIDPWrongTypeError(expected IDPType, got fmt.Stringer) error { + return &wrongIDPTypeError{ + expected: expected, + got: got.String(), + } +} + +func (e *wrongIDPTypeError) Error() string { + return fmt.Sprintf("wrong idp type returned, expected: %v, got: %v", e.expected, e.got) +} + +func (e *wrongIDPTypeError) Is(target error) bool { + _, ok := target.(*wrongIDPTypeError) + return ok +} diff --git a/backend/v3/domain/id_provider.go b/backend/v3/domain/id_provider.go new file mode 100644 index 00000000000..6f2bab1e3a1 --- /dev/null +++ b/backend/v3/domain/id_provider.go @@ -0,0 +1,355 @@ +package domain + +import ( + "context" + "encoding/json" + "time" + + "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 +type IDPType uint8 + +const ( + IDPTypeOIDC IDPType = iota + 1 + IDPTypeJWT + IDPTypeOAuth + IDPTypeSAML + IDPTypeLDAP + IDPTypeGitHub + IDPTypeGitHubEnterprise + IDPTypeGitLab + IDPTypeGitLabSelfHosted + IDPTypeAzure + IDPTypeGoogle + IDPTypeApple +) + +//go:generate enumer -type IDPState -transform lower -trimprefix IDPState -sql +type IDPState uint8 + +const ( + IDPStateActive IDPState = iota + IDPStateInactive +) + +//go:generate enumer -type IDPAutoLinkingField -transform lower -trimprefix IDPAutoLinkingField +type IDPAutoLinkingField uint8 + +const ( + IDPAutoLinkingFieldUserName IDPAutoLinkingField = iota + 1 + IDPAutoLinkingFieldEmail +) + +type OIDCMappingField int8 + +const ( + OIDCMappingFieldUnspecified OIDCMappingField = iota + OIDCMappingFieldPreferredLoginName + OIDCMappingFieldEmail + // count is for validation purposes + //nolint: unused + oidcMappingFieldCount +) + +type IdentityProvider struct { + InstanceID string `json:"instanceId,omitempty" db:"instance_id"` + OrgID *string `json:"orgId,omitempty" db:"org_id"` + ID string `json:"id,omitempty" db:"id"` + State IDPState `json:"state,omitempty" db:"state"` + Name string `json:"name,omitempty" db:"name"` + // Type represents the type of and idp. It is a pointer because it can be nil during the migration of the events + Type *IDPType `json:"type,omitempty" db:"type"` + AllowCreation bool `json:"allowCreation,omitempty" db:"allow_creation"` + AutoRegister bool `json:"autoRegister,omitempty" db:"auto_register"` + AllowAutoCreation bool `json:"allowAutoCreation,omitempty" db:"allow_auto_creation"` + AllowAutoUpdate bool `json:"allowAutoUpdate,omitempty" db:"allow_auto_update"` + AllowLinking bool `json:"allowLinking,omitempty" db:"allow_linking"` + AutoLinkingField *IDPAutoLinkingField `json:"autoLinkingField,omitempty" db:"auto_linking_field"` + StylingType *int16 `json:"stylingType,omitempty" db:"styling_type"` + Payload json.RawMessage `json:"payload,omitempty" db:"payload"` + CreatedAt time.Time `json:"createdAt,omitzero" db:"created_at"` + UpdatedAt time.Time `json:"updatedAt,omitzero" db:"updated_at"` +} + +type OIDC struct { + ClientID string `json:"clientId,omitempty"` + ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"` + Issuer string `json:"issuer,omitempty"` + AuthorizationEndpoint string `json:"authorizationEndpoint,omitempty"` + TokenEndpoint string `json:"tokenEndpoint,omitempty"` + Scopes []string `json:"scopes,omitempty"` + IDPDisplayNameMapping OIDCMappingField `json:"IDPDisplayNameMapping,omitempty"` + UserNameMapping OIDCMappingField `json:"usernameMapping,omitempty"` + IsIDTokenMapping bool `json:"idTokenMapping,omitempty"` + UsePKCE bool `json:"usePKCE,omitempty"` +} + +type IDPOIDC struct { + *IdentityProvider + OIDC +} + +type JWT struct { + IDPConfigID string `json:"idpConfigId"` + JWTEndpoint string `json:"jwtEndpoint,omitempty"` + Issuer string `json:"issuer,omitempty"` + KeysEndpoint string `json:"keysEndpoint,omitempty"` + HeaderName string `json:"headerName,omitempty"` +} + +type IDPJWT struct { + *IdentityProvider + JWT +} + +type OAuth struct { + 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"` + IDAttribute string `json:"idAttribute,omitempty"` + UsePKCE bool `json:"usePKCE,omitempty"` +} + +type IDPOAuth struct { + *IdentityProvider + OAuth +} + +//go:generate enumer -type AzureTenantType -transform lower -trimprefix AzureTenantType +type AzureTenantType uint8 + +const ( + AzureTenantTypeCommon AzureTenantType = iota + AzureTenantTypeOrganizations + AzureTenantTypeConsumers +) + +type Azure struct { + ClientID string `json:"client_id,omitempty"` + ClientSecret *crypto.CryptoValue `json:"client_secret,omitempty"` + Scopes []string `json:"scopes,omitempty"` + Tenant AzureTenantType `json:"tenant,omitempty"` + IsEmailVerified bool `json:"isEmailVerified,omitempty"` +} + +type IDPAzureAD struct { + *IdentityProvider + Azure +} + +type Google struct { + ClientID string `json:"clientId"` + ClientSecret *crypto.CryptoValue `json:"clientSecret"` + Scopes []string `json:"scopes,omitempty"` +} + +type IDPGoogle struct { + *IdentityProvider + Google +} + +type Github struct { + ClientID string `json:"clientId"` + ClientSecret *crypto.CryptoValue `json:"clientSecret"` + Scopes []string `json:"scopes,omitempty"` +} + +type IDPGithub struct { + *IdentityProvider + Github +} + +type GithubEnterprise struct { + 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"` +} + +type IDPGithubEnterprise struct { + *IdentityProvider + GithubEnterprise +} + +type Gitlab struct { + ClientID string `json:"clientId,omitempty"` + ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"` + Scopes []string `json:"scopes,omitempty"` +} + +type IDPGitlab struct { + *IdentityProvider + Gitlab +} + +type GitlabSelfHosting struct { + Issuer string `json:"issuer"` + ClientID string `json:"clientId,omitempty"` + ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"` + Scopes []string `json:"scopes,omitempty"` +} + +type IDPGitlabSelfHosting struct { + *IdentityProvider + GitlabSelfHosting +} + +type LDAP struct { + Servers []string `json:"servers"` + StartTLS bool `json:"startTLS"` + BaseDN string `json:"baseDN"` + BindDN string `json:"bindDN"` + BindPassword *crypto.CryptoValue `json:"bindPassword"` + UserBase string `json:"userBase"` + UserObjectClasses []string `json:"userObjectClasses"` + UserFilters []string `json:"userFilters"` + Timeout time.Duration `json:"timeout"` + RootCA []byte `json:"rootCA"` + + LDAPAttributes +} + +type LDAPAttributes struct { + IDAttribute string `json:"idAttribute,omitempty"` + FirstNameAttribute string `json:"firstNameAttribute,omitempty"` + LastNameAttribute string `json:"lastNameAttribute,omitempty"` + DisplayNameAttribute string `json:"displayNameAttribute,omitempty"` + NickNameAttribute string `json:"nickNameAttribute,omitempty"` + PreferredUsernameAttribute string `json:"preferredUsernameAttribute,omitempty"` + EmailAttribute string `json:"emailAttribute,omitempty"` + EmailVerifiedAttribute string `json:"emailVerifiedAttribute,omitempty"` + PhoneAttribute string `json:"phoneAttribute,omitempty"` + PhoneVerifiedAttribute string `json:"phoneVerifiedAttribute,omitempty"` + PreferredLanguageAttribute string `json:"preferredLanguageAttribute,omitempty"` + AvatarURLAttribute string `json:"avatarURLAttribute,omitempty"` + ProfileAttribute string `json:"profileAttribute,omitempty"` +} + +type IDPLDAP struct { + *IdentityProvider + 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"` + SignatureAlgorithm string `json:"signatureAlgorithm,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) +type IDPIdentifierCondition interface { + database.Condition +} + +type idProviderColumns interface { + InstanceIDColumn() database.Column + OrgIDColumn() database.Column + IDColumn() database.Column + StateColumn() database.Column + NameColumn() database.Column + TypeColumn() database.Column + AllowCreationColumn() database.Column + AutoRegisterColumn() database.Column + AllowAutoCreationColumn() database.Column + AllowAutoUpdateColumn() database.Column + AllowLinkingColumn() database.Column + AllowAutoLinkingColumn() database.Column + StylingTypeColumn() database.Column + PayloadColumn() database.Column + CreatedAtColumn() database.Column + UpdatedAtColumn() database.Column +} + +type idProviderConditions interface { + InstanceIDCondition(id string) database.Condition + OrgIDCondition(id *string) database.Condition + IDCondition(id string) IDPIdentifierCondition + StateCondition(state IDPState) database.Condition + NameCondition(name string) IDPIdentifierCondition + TypeCondition(typee IDPType) database.Condition + AutoRegisterCondition(allow bool) database.Condition + AllowCreationCondition(allow bool) database.Condition + AllowAutoCreationCondition(allow bool) database.Condition + AllowAutoUpdateCondition(allow bool) database.Condition + AllowLinkingCondition(allow bool) database.Condition + AllowAutoLinkingCondition(linkingType IDPAutoLinkingField) database.Condition + StylingTypeCondition(style int16) database.Condition + PayloadCondition(payload string) database.Condition +} + +type idProviderChanges interface { + SetName(name string) database.Change + SetState(state IDPState) database.Change + SetAllowCreation(allow bool) database.Change + SetAutoRegister(allow bool) database.Change + SetAllowAutoCreation(allow bool) database.Change + SetAllowAutoUpdate(allow bool) database.Change + SetAllowLinking(allow bool) database.Change + SetAutoAllowLinking(allow bool) database.Change + SetStylingType(stylingType int16) database.Change + SetPayload(payload string) database.Change + SetUpdatedAt(createdAt *time.Time) database.Change +} + +type IDProviderRepository interface { + idProviderColumns + idProviderConditions + idProviderChanges + + Get(ctx context.Context, client database.QueryExecutor, id IDPIdentifierCondition, instanceID string, orgID *string) (*IdentityProvider, error) + List(ctx context.Context, client database.QueryExecutor, conditions ...database.Condition) ([]*IdentityProvider, error) + + Create(ctx context.Context, client database.QueryExecutor, idp *IdentityProvider) error + Update(ctx context.Context, client database.QueryExecutor, id IDPIdentifierCondition, instanceID string, orgID *string, changes ...database.Change) (int64, error) + Delete(ctx context.Context, client database.QueryExecutor, id IDPIdentifierCondition, instanceID string, orgID *string) (int64, error) + + GetOIDC(ctx context.Context, client database.QueryExecutor, id IDPIdentifierCondition, instanceID string, orgID *string) (*IDPOIDC, error) + GetJWT(ctx context.Context, client database.QueryExecutor, id IDPIdentifierCondition, instanceID string, orgID *string) (*IDPJWT, error) + + GetOAuth(ctx context.Context, client database.QueryExecutor, id IDPIdentifierCondition, instanceID string, orgID *string) (*IDPOAuth, error) + + GetAzureAD(ctx context.Context, client database.QueryExecutor, id IDPIdentifierCondition, instanceID string, orgID *string) (*IDPAzureAD, error) + GetGoogle(ctx context.Context, client database.QueryExecutor, id IDPIdentifierCondition, instanceID string, orgID *string) (*IDPGoogle, error) + GetGithub(ctx context.Context, client database.QueryExecutor, id IDPIdentifierCondition, instanceID string, orgID *string) (*IDPGithub, error) + GetGithubEnterprise(ctx context.Context, client database.QueryExecutor, id IDPIdentifierCondition, instanceID string, orgID *string) (*IDPGithubEnterprise, error) + GetGitlab(ctx context.Context, client database.QueryExecutor, id IDPIdentifierCondition, instanceID string, orgID *string) (*IDPGitlab, error) + GetGitlabSelfHosting(ctx context.Context, client database.QueryExecutor, id IDPIdentifierCondition, instanceID string, orgID *string) (*IDPGitlabSelfHosting, error) + GetLDAP(ctx context.Context, client database.QueryExecutor, id IDPIdentifierCondition, instanceID string, orgID *string) (*IDPLDAP, error) + GetApple(ctx context.Context, client database.QueryExecutor, id IDPIdentifierCondition, instanceID string, orgID *string) (*IDPApple, error) + GetSAML(ctx context.Context, client database.QueryExecutor, id IDPIdentifierCondition, instanceID string, orgID *string) (*IDPSAML, error) +} diff --git a/backend/v3/domain/idpautolinkingfield_enumer.go b/backend/v3/domain/idpautolinkingfield_enumer.go new file mode 100644 index 00000000000..037c471ebe4 --- /dev/null +++ b/backend/v3/domain/idpautolinkingfield_enumer.go @@ -0,0 +1,79 @@ +// Code generated by "enumer -type IDPAutoLinkingField -transform lower -trimprefix IDPAutoLinkingField"; DO NOT EDIT. + +package domain + +import ( + "fmt" + "strings" +) + +const _IDPAutoLinkingFieldName = "usernameemail" + +var _IDPAutoLinkingFieldIndex = [...]uint8{0, 8, 13} + +const _IDPAutoLinkingFieldLowerName = "usernameemail" + +func (i IDPAutoLinkingField) String() string { + i -= 1 + if i >= IDPAutoLinkingField(len(_IDPAutoLinkingFieldIndex)-1) { + return fmt.Sprintf("IDPAutoLinkingField(%d)", i+1) + } + return _IDPAutoLinkingFieldName[_IDPAutoLinkingFieldIndex[i]:_IDPAutoLinkingFieldIndex[i+1]] +} + +// An "invalid array index" compiler error signifies that the constant values have changed. +// Re-run the stringer command to generate them again. +func _IDPAutoLinkingFieldNoOp() { + var x [1]struct{} + _ = x[IDPAutoLinkingFieldUserName-(1)] + _ = x[IDPAutoLinkingFieldEmail-(2)] +} + +var _IDPAutoLinkingFieldValues = []IDPAutoLinkingField{IDPAutoLinkingFieldUserName, IDPAutoLinkingFieldEmail} + +var _IDPAutoLinkingFieldNameToValueMap = map[string]IDPAutoLinkingField{ + _IDPAutoLinkingFieldName[0:8]: IDPAutoLinkingFieldUserName, + _IDPAutoLinkingFieldLowerName[0:8]: IDPAutoLinkingFieldUserName, + _IDPAutoLinkingFieldName[8:13]: IDPAutoLinkingFieldEmail, + _IDPAutoLinkingFieldLowerName[8:13]: IDPAutoLinkingFieldEmail, +} + +var _IDPAutoLinkingFieldNames = []string{ + _IDPAutoLinkingFieldName[0:8], + _IDPAutoLinkingFieldName[8:13], +} + +// IDPAutoLinkingFieldString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func IDPAutoLinkingFieldString(s string) (IDPAutoLinkingField, error) { + if val, ok := _IDPAutoLinkingFieldNameToValueMap[s]; ok { + return val, nil + } + + if val, ok := _IDPAutoLinkingFieldNameToValueMap[strings.ToLower(s)]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to IDPAutoLinkingField values", s) +} + +// IDPAutoLinkingFieldValues returns all values of the enum +func IDPAutoLinkingFieldValues() []IDPAutoLinkingField { + return _IDPAutoLinkingFieldValues +} + +// IDPAutoLinkingFieldStrings returns a slice of all String values of the enum +func IDPAutoLinkingFieldStrings() []string { + strs := make([]string, len(_IDPAutoLinkingFieldNames)) + copy(strs, _IDPAutoLinkingFieldNames) + return strs +} + +// IsAIDPAutoLinkingField returns "true" if the value is listed in the enum definition. "false" otherwise +func (i IDPAutoLinkingField) IsAIDPAutoLinkingField() bool { + for _, v := range _IDPAutoLinkingFieldValues { + if i == v { + return true + } + } + return false +} diff --git a/backend/v3/domain/idpstate_enumer.go b/backend/v3/domain/idpstate_enumer.go new file mode 100644 index 00000000000..1b063363eac --- /dev/null +++ b/backend/v3/domain/idpstate_enumer.go @@ -0,0 +1,109 @@ +// Code generated by "enumer -type IDPState -transform lower -trimprefix IDPState -sql"; DO NOT EDIT. + +package domain + +import ( + "database/sql/driver" + "fmt" + "strings" +) + +const _IDPStateName = "activeinactive" + +var _IDPStateIndex = [...]uint8{0, 6, 14} + +const _IDPStateLowerName = "activeinactive" + +func (i IDPState) String() string { + if i >= IDPState(len(_IDPStateIndex)-1) { + return fmt.Sprintf("IDPState(%d)", i) + } + return _IDPStateName[_IDPStateIndex[i]:_IDPStateIndex[i+1]] +} + +// An "invalid array index" compiler error signifies that the constant values have changed. +// Re-run the stringer command to generate them again. +func _IDPStateNoOp() { + var x [1]struct{} + _ = x[IDPStateActive-(0)] + _ = x[IDPStateInactive-(1)] +} + +var _IDPStateValues = []IDPState{IDPStateActive, IDPStateInactive} + +var _IDPStateNameToValueMap = map[string]IDPState{ + _IDPStateName[0:6]: IDPStateActive, + _IDPStateLowerName[0:6]: IDPStateActive, + _IDPStateName[6:14]: IDPStateInactive, + _IDPStateLowerName[6:14]: IDPStateInactive, +} + +var _IDPStateNames = []string{ + _IDPStateName[0:6], + _IDPStateName[6:14], +} + +// IDPStateString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func IDPStateString(s string) (IDPState, error) { + if val, ok := _IDPStateNameToValueMap[s]; ok { + return val, nil + } + + if val, ok := _IDPStateNameToValueMap[strings.ToLower(s)]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to IDPState values", s) +} + +// IDPStateValues returns all values of the enum +func IDPStateValues() []IDPState { + return _IDPStateValues +} + +// IDPStateStrings returns a slice of all String values of the enum +func IDPStateStrings() []string { + strs := make([]string, len(_IDPStateNames)) + copy(strs, _IDPStateNames) + return strs +} + +// IsAIDPState returns "true" if the value is listed in the enum definition. "false" otherwise +func (i IDPState) IsAIDPState() bool { + for _, v := range _IDPStateValues { + if i == v { + return true + } + } + return false +} + +func (i IDPState) Value() (driver.Value, error) { + return i.String(), nil +} + +func (i *IDPState) Scan(value interface{}) error { + if value == nil { + return nil + } + + var str string + switch v := value.(type) { + case []byte: + str = string(v) + case string: + str = v + case fmt.Stringer: + str = v.String() + default: + return fmt.Errorf("invalid value of IDPState: %[1]T(%[1]v)", value) + } + + val, err := IDPStateString(str) + if err != nil { + return err + } + + *i = val + return nil +} diff --git a/backend/v3/domain/idptype_enumer.go b/backend/v3/domain/idptype_enumer.go new file mode 100644 index 00000000000..f5e895ba714 --- /dev/null +++ b/backend/v3/domain/idptype_enumer.go @@ -0,0 +1,119 @@ +// Code generated by "enumer -type IDPType -transform lower -trimprefix IDPType"; DO NOT EDIT. + +package domain + +import ( + "fmt" + "strings" +) + +const _IDPTypeName = "oidcjwtoauthsamlldapgithubgithubenterprisegitlabgitlabselfhostedazuregoogleapple" + +var _IDPTypeIndex = [...]uint8{0, 4, 7, 12, 16, 20, 26, 42, 48, 64, 69, 75, 80} + +const _IDPTypeLowerName = "oidcjwtoauthsamlldapgithubgithubenterprisegitlabgitlabselfhostedazuregoogleapple" + +func (i IDPType) String() string { + i -= 1 + if i >= IDPType(len(_IDPTypeIndex)-1) { + return fmt.Sprintf("IDPType(%d)", i+1) + } + return _IDPTypeName[_IDPTypeIndex[i]:_IDPTypeIndex[i+1]] +} + +// An "invalid array index" compiler error signifies that the constant values have changed. +// Re-run the stringer command to generate them again. +func _IDPTypeNoOp() { + var x [1]struct{} + _ = x[IDPTypeOIDC-(1)] + _ = x[IDPTypeJWT-(2)] + _ = x[IDPTypeOAuth-(3)] + _ = x[IDPTypeSAML-(4)] + _ = x[IDPTypeLDAP-(5)] + _ = x[IDPTypeGitHub-(6)] + _ = x[IDPTypeGitHubEnterprise-(7)] + _ = x[IDPTypeGitLab-(8)] + _ = x[IDPTypeGitLabSelfHosted-(9)] + _ = x[IDPTypeAzure-(10)] + _ = x[IDPTypeGoogle-(11)] + _ = x[IDPTypeApple-(12)] +} + +var _IDPTypeValues = []IDPType{IDPTypeOIDC, IDPTypeJWT, IDPTypeOAuth, IDPTypeSAML, IDPTypeLDAP, IDPTypeGitHub, IDPTypeGitHubEnterprise, IDPTypeGitLab, IDPTypeGitLabSelfHosted, IDPTypeAzure, IDPTypeGoogle, IDPTypeApple} + +var _IDPTypeNameToValueMap = map[string]IDPType{ + _IDPTypeName[0:4]: IDPTypeOIDC, + _IDPTypeLowerName[0:4]: IDPTypeOIDC, + _IDPTypeName[4:7]: IDPTypeJWT, + _IDPTypeLowerName[4:7]: IDPTypeJWT, + _IDPTypeName[7:12]: IDPTypeOAuth, + _IDPTypeLowerName[7:12]: IDPTypeOAuth, + _IDPTypeName[12:16]: IDPTypeSAML, + _IDPTypeLowerName[12:16]: IDPTypeSAML, + _IDPTypeName[16:20]: IDPTypeLDAP, + _IDPTypeLowerName[16:20]: IDPTypeLDAP, + _IDPTypeName[20:26]: IDPTypeGitHub, + _IDPTypeLowerName[20:26]: IDPTypeGitHub, + _IDPTypeName[26:42]: IDPTypeGitHubEnterprise, + _IDPTypeLowerName[26:42]: IDPTypeGitHubEnterprise, + _IDPTypeName[42:48]: IDPTypeGitLab, + _IDPTypeLowerName[42:48]: IDPTypeGitLab, + _IDPTypeName[48:64]: IDPTypeGitLabSelfHosted, + _IDPTypeLowerName[48:64]: IDPTypeGitLabSelfHosted, + _IDPTypeName[64:69]: IDPTypeAzure, + _IDPTypeLowerName[64:69]: IDPTypeAzure, + _IDPTypeName[69:75]: IDPTypeGoogle, + _IDPTypeLowerName[69:75]: IDPTypeGoogle, + _IDPTypeName[75:80]: IDPTypeApple, + _IDPTypeLowerName[75:80]: IDPTypeApple, +} + +var _IDPTypeNames = []string{ + _IDPTypeName[0:4], + _IDPTypeName[4:7], + _IDPTypeName[7:12], + _IDPTypeName[12:16], + _IDPTypeName[16:20], + _IDPTypeName[20:26], + _IDPTypeName[26:42], + _IDPTypeName[42:48], + _IDPTypeName[48:64], + _IDPTypeName[64:69], + _IDPTypeName[69:75], + _IDPTypeName[75:80], +} + +// IDPTypeString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func IDPTypeString(s string) (IDPType, error) { + if val, ok := _IDPTypeNameToValueMap[s]; ok { + return val, nil + } + + if val, ok := _IDPTypeNameToValueMap[strings.ToLower(s)]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to IDPType values", s) +} + +// IDPTypeValues returns all values of the enum +func IDPTypeValues() []IDPType { + return _IDPTypeValues +} + +// IDPTypeStrings returns a slice of all String values of the enum +func IDPTypeStrings() []string { + strs := make([]string, len(_IDPTypeNames)) + copy(strs, _IDPTypeNames) + return strs +} + +// IsAIDPType returns "true" if the value is listed in the enum definition. "false" otherwise +func (i IDPType) IsAIDPType() bool { + for _, v := range _IDPTypeValues { + if i == v { + return true + } + } + return false +} diff --git a/backend/v3/domain/organization.go b/backend/v3/domain/organization.go index 32d6528ba86..da5407afdd5 100644 --- a/backend/v3/domain/organization.go +++ b/backend/v3/domain/organization.go @@ -32,7 +32,7 @@ type organizationColumns interface { IDColumn() database.Column // NameColumn returns the column for the name field. NameColumn() database.Column - // InstanceIDColumn returns the column for the default org id field + // InstanceIDColumn returns the column for the instance id field InstanceIDColumn() database.Column // StateColumn returns the column for the name field. StateColumn() database.Column diff --git a/backend/v3/storage/database/dialect/postgres/migration/005_identity_providers_table.go b/backend/v3/storage/database/dialect/postgres/migration/005_identity_providers_table.go new file mode 100644 index 00000000000..36bbe8e31c1 --- /dev/null +++ b/backend/v3/storage/database/dialect/postgres/migration/005_identity_providers_table.go @@ -0,0 +1,16 @@ +package migration + +import ( + _ "embed" +) + +var ( + //go:embed 005_identity_providers_table/up.sql + up005IdentityProvidersTable string + //go:embed 005_identity_providers_table/down.sql + down005IdentityProvidersTable string +) + +func init() { + registerSQLMigration(5, up005IdentityProvidersTable, down005IdentityProvidersTable) +} diff --git a/backend/v3/storage/database/dialect/postgres/migration/005_identity_providers_table/down.sql b/backend/v3/storage/database/dialect/postgres/migration/005_identity_providers_table/down.sql new file mode 100644 index 00000000000..e19e92fad0f --- /dev/null +++ b/backend/v3/storage/database/dialect/postgres/migration/005_identity_providers_table/down.sql @@ -0,0 +1,2 @@ +DROP TABLE zitadel.identity_providers; +DROP TYPE zitadel.idp_state; diff --git a/backend/v3/storage/database/dialect/postgres/migration/005_identity_providers_table/up.sql b/backend/v3/storage/database/dialect/postgres/migration/005_identity_providers_table/up.sql new file mode 100644 index 00000000000..760efeeb251 --- /dev/null +++ b/backend/v3/storage/database/dialect/postgres/migration/005_identity_providers_table/up.sql @@ -0,0 +1,44 @@ +CREATE TYPE zitadel.idp_state AS ENUM ( + 'active', + 'inactive' +); + +CREATE TABLE zitadel.identity_providers ( + instance_id TEXT NOT NULL + , org_id TEXT + , id TEXT NOT NULL CHECK (id <> '') + , state zitadel.idp_state NOT NULL DEFAULT 'active' + , name TEXT NOT NULL CHECK (name <> '') + , type SMALLINT DEFAULT NULL + , auto_register BOOLEAN NOT NULL DEFAULT TRUE + , allow_creation BOOLEAN NOT NULL DEFAULT TRUE + , allow_auto_creation BOOLEAN NOT NULL DEFAULT TRUE + , allow_auto_update BOOLEAN NOT NULL DEFAULT TRUE + , allow_linking BOOLEAN NOT NULL DEFAULT TRUE + , auto_linking_field SMALLINT DEFAULT NULL + , styling_type SMALLINT + , payload JSONB + + , created_at TIMESTAMPTZ NOT NULL DEFAULT now() + , updated_at TIMESTAMPTZ NOT NULL DEFAULT now() + + , PRIMARY KEY (instance_id, id) + , CONSTRAINT identity_providers_id_unique UNIQUE NULLS NOT DISTINCT (instance_id, org_id, id) + , CONSTRAINT identity_providers_name_unique UNIQUE NULLS NOT DISTINCT (instance_id, org_id, name) + , FOREIGN KEY (instance_id) REFERENCES zitadel.instances(id) + , FOREIGN KEY (instance_id, org_id) REFERENCES zitadel.organizations(instance_id, id) +); + +-- CREATE INDEX idx_identity_providers_org_id ON identity_providers(instance_id, org_id) WHERE org_id IS NOT NULL; +CREATE INDEX idx_identity_providers_state ON zitadel.identity_providers(instance_id, state); +CREATE INDEX idx_identity_providers_type ON zitadel.identity_providers(instance_id, type); +-- CREATE INDEX idx_identity_providers_created_at ON identity_providers(created_at); +-- CREATE INDEX idx_identity_providers_deleted_at ON identity_providers(deleted_at) WHERE deleted_at IS NOT NULL; + + +CREATE TRIGGER trigger_set_updated_at +BEFORE UPDATE ON zitadel.identity_providers +FOR EACH ROW +WHEN (NEW.updated_at IS NULL) +EXECUTE FUNCTION zitadel.set_updated_at(); + diff --git a/backend/v3/storage/database/events_testing/events_test.go b/backend/v3/storage/database/events_testing/events_test.go index f2427ba4b59..cf279b897df 100644 --- a/backend/v3/storage/database/events_testing/events_test.go +++ b/backend/v3/storage/database/events_testing/events_test.go @@ -15,7 +15,9 @@ import ( "github.com/zitadel/zitadel/backend/v3/storage/database" "github.com/zitadel/zitadel/backend/v3/storage/database/dialect/postgres" "github.com/zitadel/zitadel/internal/integration" + "github.com/zitadel/zitadel/pkg/grpc/admin" v2beta "github.com/zitadel/zitadel/pkg/grpc/instance/v2beta" + mgmt "github.com/zitadel/zitadel/pkg/grpc/management" v2beta_org "github.com/zitadel/zitadel/pkg/grpc/org/v2beta" "github.com/zitadel/zitadel/pkg/grpc/system" ) @@ -25,9 +27,12 @@ const ConnString = "host=localhost port=5432 user=zitadel password=zitadel dbnam var ( dbPool *pgxpool.Pool CTX context.Context + IAMCTX context.Context Instance *integration.Instance SystemClient system.SystemServiceClient OrgClient v2beta_org.OrganizationServiceClient + AdminClient admin.AdminServiceClient + MgmtClient mgmt.ManagementServiceClient ) var pool database.Pool @@ -40,8 +45,11 @@ func TestMain(m *testing.M) { CTX = integration.WithSystemAuthorization(ctx) Instance = integration.NewInstance(CTX) + IAMCTX = Instance.WithAuthorization(ctx, integration.UserTypeIAMOwner) SystemClient = integration.SystemClient() OrgClient = Instance.Client.OrgV2beta + AdminClient = Instance.Client.Admin + MgmtClient = Instance.Client.Mgmt defer func() { _, err := Instance.Client.InstanceV2Beta.DeleteInstance(CTX, &v2beta.DeleteInstanceRequest{ diff --git a/backend/v3/storage/database/events_testing/id_provider_instance_test.go b/backend/v3/storage/database/events_testing/id_provider_instance_test.go new file mode 100644 index 00000000000..43060add66f --- /dev/null +++ b/backend/v3/storage/database/events_testing/id_provider_instance_test.go @@ -0,0 +1,2530 @@ +//go:build integration + +package events_test + +import ( + "testing" + "time" + + "github.com/brianvoe/gofakeit/v6" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + durationpb "google.golang.org/protobuf/types/known/durationpb" + + "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" + idp_grpc "github.com/zitadel/zitadel/pkg/grpc/idp" +) + +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_TestIDProviderInstanceReduces(t *testing.T) { + instanceID := Instance.ID() + + t.Run("test iam idp add reduces", func(t *testing.T) { + name := gofakeit.Name() + + before := time.Now() + addOIDC, err := AdminClient.AddOIDCIDP(IAMCTX, &admin.AddOIDCIDPRequest{ + Name: name, + StylingType: idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE, + ClientId: "clientID", + ClientSecret: "clientSecret", + Issuer: "issuer", + Scopes: []string{"scope"}, + DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + AutoRegister: true, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + idp, err := idpRepo.Get(IAMCTX, pool, + idpRepo.NameCondition(name), + instanceID, + nil, + ) + require.NoError(t, err) + + // event iam.idp.config.added + assert.Equal(t, instanceID, idp.InstanceID) + assert.Nil(t, idp.OrgID) + assert.Equal(t, addOIDC.IdpId, idp.ID) + assert.Equal(t, domain.IDPStateActive, idp.State) + assert.Equal(t, name, idp.Name) + assert.Equal(t, true, idp.AutoRegister) + assert.Equal(t, true, idp.AllowCreation) + assert.Equal(t, false, idp.AllowAutoUpdate) + assert.Equal(t, true, idp.AllowLinking) + assert.Nil(t, idp.AutoLinkingField) + assert.Equal(t, int16(idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE), *idp.StylingType) + assert.WithinRange(t, idp.UpdatedAt, before, after) + assert.WithinRange(t, idp.CreatedAt, before, after) + }, retryDuration, tick) + }) + + t.Run("test iam idp update reduces", func(t *testing.T) { + name := gofakeit.Name() + + addOIDC, err := AdminClient.AddOIDCIDP(IAMCTX, &admin.AddOIDCIDPRequest{ + Name: name, + StylingType: idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE, + ClientId: "clientID", + ClientSecret: "clientSecret", + Issuer: "issuer", + Scopes: []string{"scope"}, + DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + AutoRegister: true, + }) + require.NoError(t, err) + + name = "new_" + name + + before := time.Now() + _, err = AdminClient.UpdateIDP(IAMCTX, &admin.UpdateIDPRequest{ + IdpId: addOIDC.IdpId, + Name: name, + StylingType: idp_grpc.IDPStylingType_STYLING_TYPE_UNSPECIFIED, + AutoRegister: false, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + idp, err := idpRepo.Get(IAMCTX, pool, + idpRepo.NameCondition(name), + instanceID, + nil, + ) + require.NoError(t, err) + + // event iam.idp.config.changed + assert.Equal(t, addOIDC.IdpId, idp.ID) + assert.Equal(t, name, idp.Name) + assert.Equal(t, false, idp.AutoRegister) + assert.Equal(t, int16(idp_grpc.IDPStylingType_STYLING_TYPE_UNSPECIFIED), *idp.StylingType) + assert.WithinRange(t, idp.UpdatedAt, before, after) + }, retryDuration, tick) + }) + + t.Run("test iam idp deactivate reduces", func(t *testing.T) { + name := gofakeit.Name() + + addOIDC, err := AdminClient.AddOIDCIDP(IAMCTX, &admin.AddOIDCIDPRequest{ + Name: name, + StylingType: idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE, + ClientId: "clientID", + ClientSecret: "clientSecret", + Issuer: "issuer", + Scopes: []string{"scope"}, + DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + AutoRegister: true, + }) + require.NoError(t, err) + + // deactivate idp + before := time.Now() + _, err = AdminClient.DeactivateIDP(IAMCTX, &admin.DeactivateIDPRequest{ + IdpId: addOIDC.IdpId, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + idp, err := idpRepo.Get(IAMCTX, pool, + idpRepo.IDCondition(addOIDC.IdpId), + instanceID, + nil, + ) + require.NoError(t, err) + + // event iam.idp.config.deactivated + assert.Equal(t, addOIDC.IdpId, idp.ID) + assert.Equal(t, domain.IDPStateInactive, idp.State) + assert.WithinRange(t, idp.UpdatedAt, before, after) + }, retryDuration, tick) + }) + + t.Run("test iam idp config reactivate reduces", func(t *testing.T) { + name := gofakeit.Name() + + addOIDC, err := AdminClient.AddOIDCIDP(IAMCTX, &admin.AddOIDCIDPRequest{ + Name: name, + StylingType: idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE, + ClientId: "clientID", + ClientSecret: "clientSecret", + Issuer: "issuer", + Scopes: []string{"scope"}, + DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + AutoRegister: true, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // deactivate idp + _, err = AdminClient.DeactivateIDP(IAMCTX, &admin.DeactivateIDPRequest{ + IdpId: addOIDC.IdpId, + }) + require.NoError(t, err) + // wait for idp to be deactivated + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + idp, err := idpRepo.Get(IAMCTX, pool, + idpRepo.IDCondition(addOIDC.IdpId), + instanceID, + nil, + ) + require.NoError(t, err) + + assert.Equal(t, addOIDC.IdpId, idp.ID) + assert.Equal(t, domain.IDPStateInactive, idp.State) + }, retryDuration, tick) + + // reactivate idp + before := time.Now() + _, err = AdminClient.ReactivateIDP(IAMCTX, &admin.ReactivateIDPRequest{ + IdpId: addOIDC.IdpId, + }) + after := time.Now() + require.NoError(t, err) + + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + idp, err := idpRepo.Get(IAMCTX, pool, + idpRepo.IDCondition(addOIDC.IdpId), + instanceID, + nil, + ) + require.NoError(t, err) + + // event iam.idp.config.reactivated + assert.Equal(t, addOIDC.IdpId, idp.ID) + assert.Equal(t, domain.IDPStateActive, idp.State) + assert.WithinRange(t, idp.UpdatedAt, before, after) + }, retryDuration, tick) + }) + + t.Run("test iam idp config remove reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add idp + addOIDC, err := AdminClient.AddOIDCIDP(IAMCTX, &admin.AddOIDCIDPRequest{ + Name: name, + StylingType: idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE, + ClientId: "clientID", + ClientSecret: "clientSecret", + Issuer: "issuer", + Scopes: []string{"scope"}, + DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + AutoRegister: true, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // remove idp + _, err = AdminClient.RemoveIDP(IAMCTX, &admin.RemoveIDPRequest{ + IdpId: addOIDC.IdpId, + }) + require.NoError(t, err) + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + _, err := idpRepo.Get(IAMCTX, pool, + idpRepo.IDCondition(addOIDC.IdpId), + instanceID, + nil, + ) + + // event iam.idp.config.remove + require.ErrorIs(t, &database.NoRowFoundError{}, err) + }, retryDuration, tick) + }) + + t.Run("test iam idp oidc added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add oidc + addOIDC, err := AdminClient.AddOIDCIDP(IAMCTX, &admin.AddOIDCIDPRequest{ + Name: name, + StylingType: idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE, + ClientId: "clientID", + ClientSecret: "clientSecret", + Issuer: "issuer", + Scopes: []string{"scope"}, + DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + AutoRegister: false, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + oidc, err := idpRepo.GetOIDC(IAMCTX, pool, + idpRepo.IDCondition(addOIDC.IdpId), + instanceID, + nil, + ) + require.NoError(t, err) + + // event org.idp.oidc.config.added + // idp + assert.Equal(t, instanceID, oidc.InstanceID) + assert.Nil(t, oidc.OrgID) + assert.Equal(t, name, oidc.Name) + assert.Equal(t, addOIDC.IdpId, oidc.ID) + assert.Equal(t, domain.IDPTypeOIDC, domain.IDPType(*oidc.Type)) + + // oidc + assert.Equal(t, "issuer", oidc.Issuer) + assert.Equal(t, "clientID", oidc.ClientID) + assert.Equal(t, []string{"scope"}, oidc.Scopes) + assert.Equal(t, int16(idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE), *oidc.StylingType) + assert.Equal(t, false, oidc.AutoRegister) + assert.Equal(t, domain.OIDCMappingField(idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL), oidc.IDPDisplayNameMapping) + assert.Equal(t, domain.OIDCMappingField(idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL), oidc.UserNameMapping) + }, retryDuration, tick) + }) + + t.Run("test iam idp oidc changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add oidc + addOIDC, err := AdminClient.AddOIDCIDP(IAMCTX, &admin.AddOIDCIDPRequest{ + Name: name, + StylingType: idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE, + ClientId: "clientID", + ClientSecret: "clientSecret", + Issuer: "issuer", + Scopes: []string{"scope"}, + DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + AutoRegister: true, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check original values for OCID + var oidc *domain.IDPOIDC + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + oidc, err = idpRepo.GetOIDC(IAMCTX, pool, idpRepo.IDCondition(addOIDC.IdpId), instanceID, nil) + require.NoError(t, err) + assert.Equal(t, addOIDC.IdpId, oidc.ID) + }, retryDuration, tick) + + before := time.Now() + _, err = AdminClient.UpdateIDPOIDCConfig(IAMCTX, &admin.UpdateIDPOIDCConfigRequest{ + IdpId: addOIDC.IdpId, + ClientId: "new_clientID", + ClientSecret: "new_clientSecret", + Issuer: "new_issuer", + Scopes: []string{"new_scope"}, + DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_PREFERRED_USERNAME, + UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_PREFERRED_USERNAME, + }) + after := time.Now() + require.NoError(t, err) + + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateOIDC, err := idpRepo.GetOIDC(IAMCTX, pool, + idpRepo.IDCondition(addOIDC.IdpId), + instanceID, + nil, + ) + require.NoError(t, err) + + // event org.idp.oidc.config.changed + // idp + assert.Equal(t, instanceID, oidc.InstanceID) + assert.Nil(t, oidc.OrgID) + assert.Equal(t, name, oidc.Name) + assert.Equal(t, addOIDC.IdpId, updateOIDC.ID) + assert.Equal(t, domain.IDPTypeOIDC, domain.IDPType(*updateOIDC.Type)) + assert.WithinRange(t, updateOIDC.UpdatedAt, before, after) + + // oidc + assert.Equal(t, instanceID, oidc.InstanceID) + assert.Nil(t, oidc.OrgID) + assert.Equal(t, "new_issuer", updateOIDC.Issuer) + assert.Equal(t, "new_clientID", updateOIDC.ClientID) + assert.NotNil(t, oidc.ClientSecret) + assert.NotEqual(t, oidc.ClientSecret, updateOIDC.ClientSecret) + assert.Equal(t, []string{"new_scope"}, updateOIDC.Scopes) + assert.Equal(t, domain.OIDCMappingField(idp.OIDCMappingField_OIDC_MAPPING_FIELD_PREFERRED_USERNAME), updateOIDC.IDPDisplayNameMapping) + assert.Equal(t, domain.OIDCMappingField(idp.OIDCMappingField_OIDC_MAPPING_FIELD_PREFERRED_USERNAME), updateOIDC.UserNameMapping) + }, retryDuration, tick) + }) + + t.Run("test iam idp jwt added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add jwt + addJWT, err := AdminClient.AddJWTIDP(IAMCTX, &admin.AddJWTIDPRequest{ + Name: name, + StylingType: idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE, + JwtEndpoint: "jwtEndpoint", + Issuer: "issuer", + KeysEndpoint: "keyEndpoint", + HeaderName: "headerName", + AutoRegister: true, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + jwt, err := idpRepo.GetJWT(IAMCTX, pool, + idpRepo.IDCondition(addJWT.IdpId), + instanceID, + nil, + ) + require.NoError(t, err) + + // event iam.idp.jwt.config.added + // idp + assert.Equal(t, instanceID, jwt.InstanceID) + assert.Nil(t, jwt.OrgID) + assert.Equal(t, name, jwt.Name) + assert.Equal(t, addJWT.IdpId, jwt.ID) + assert.Equal(t, domain.IDPTypeJWT, domain.IDPType(*jwt.Type)) + assert.Equal(t, int16(idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE), *jwt.StylingType) + + // jwt + assert.Equal(t, "jwtEndpoint", jwt.JWTEndpoint) + assert.Equal(t, "issuer", jwt.Issuer) + assert.Equal(t, "keyEndpoint", jwt.KeysEndpoint) + assert.Equal(t, "headerName", jwt.HeaderName) + }, retryDuration, tick) + }) + + t.Run("test iam idp jwt changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add jwt + addJWT, err := AdminClient.AddJWTIDP(IAMCTX, &admin.AddJWTIDPRequest{ + Name: name, + StylingType: idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE, + JwtEndpoint: "jwtEndpoint", + Issuer: "issuer", + KeysEndpoint: "keyEndpoint", + HeaderName: "headerName", + AutoRegister: true, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + before := time.Now() + _, err = AdminClient.UpdateIDPJWTConfig(IAMCTX, &admin.UpdateIDPJWTConfigRequest{ + IdpId: addJWT.IdpId, + JwtEndpoint: "new_jwtEndpoint", + Issuer: "new_issuer", + KeysEndpoint: "new_keyEndpoint", + HeaderName: "new_headerName", + }) + after := time.Now() + require.NoError(t, err) + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateJWT, err := idpRepo.GetJWT(IAMCTX, pool, + idpRepo.IDCondition(addJWT.IdpId), + instanceID, + nil, + ) + require.NoError(t, err) + + // event iam.idp.jwt.config.changed + // idp + assert.Equal(t, addJWT.IdpId, updateJWT.ID) + assert.Equal(t, domain.IDPTypeJWT, domain.IDPType(*updateJWT.Type)) + assert.WithinRange(t, updateJWT.UpdatedAt, before, after) + + // jwt + assert.Equal(t, "new_jwtEndpoint", updateJWT.JWTEndpoint) + assert.Equal(t, "new_issuer", updateJWT.Issuer) + assert.Equal(t, "new_keyEndpoint", updateJWT.KeysEndpoint) + assert.Equal(t, "new_headerName", updateJWT.HeaderName) + }, retryDuration, tick) + }) + + t.Run("test instance idp oauth added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add oauth + before := time.Now() + addOAuth, err := AdminClient.AddGenericOAuthProvider(IAMCTX, &admin.AddGenericOAuthProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + AuthorizationEndpoint: "authorizationEndpoint", + TokenEndpoint: "tokenEndpoint", + UserEndpoint: "userEndpoint", + Scopes: []string{"scope"}, + IdAttribute: "idAttribute", + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + UsePkce: false, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for oauth + var oauth *domain.IDPOAuth + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + oauth, err = idpRepo.GetOAuth(IAMCTX, pool, idpRepo.IDCondition(addOAuth.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.oauth.added + // idp + assert.Equal(t, instanceID, oauth.InstanceID) + assert.Nil(t, oauth.OrgID) + assert.Equal(t, addOAuth.Id, oauth.ID) + assert.Equal(t, name, oauth.Name) + assert.Equal(t, domain.IDPTypeOAuth, domain.IDPType(*oauth.Type)) + assert.Equal(t, false, oauth.AllowLinking) + assert.Equal(t, false, oauth.AllowCreation) + assert.Equal(t, false, oauth.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*oauth.AutoLinkingField)) + assert.WithinRange(t, oauth.CreatedAt, before, after) + assert.WithinRange(t, oauth.UpdatedAt, before, after) + + // oauth + assert.Equal(t, "clientId", oauth.ClientID) + assert.NotNil(t, oauth.ClientSecret) + assert.Equal(t, "authorizationEndpoint", oauth.AuthorizationEndpoint) + assert.Equal(t, "tokenEndpoint", oauth.TokenEndpoint) + assert.Equal(t, "userEndpoint", oauth.UserEndpoint) + assert.Equal(t, []string{"scope"}, oauth.Scopes) + assert.Equal(t, "idAttribute", oauth.IDAttribute) + assert.Equal(t, false, oauth.UsePKCE) + }, retryDuration, tick) + }) + + t.Run("test instance idp oauth changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add oauth + addOAuth, err := AdminClient.AddGenericOAuthProvider(IAMCTX, &admin.AddGenericOAuthProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + AuthorizationEndpoint: "authorizationEndpoint", + TokenEndpoint: "tokenEndpoint", + UserEndpoint: "userEndpoint", + Scopes: []string{"scope"}, + IdAttribute: "idAttribute", + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + UsePkce: false, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for oauth + var oauth *domain.IDPOAuth + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + oauth, err = idpRepo.GetOAuth(IAMCTX, pool, idpRepo.IDCondition(addOAuth.Id), instanceID, nil) + require.NoError(t, err) + assert.Equal(t, addOAuth.Id, oauth.ID) + }, retryDuration, tick) + + name = "new_" + name + before := time.Now() + _, err = AdminClient.UpdateGenericOAuthProvider(IAMCTX, &admin.UpdateGenericOAuthProviderRequest{ + Id: addOAuth.Id, + Name: name, + ClientId: "new_clientId", + ClientSecret: "new_clientSecret", + AuthorizationEndpoint: "new_authorizationEndpoint", + TokenEndpoint: "new_tokenEndpoint", + UserEndpoint: "new_userEndpoint", + Scopes: []string{"new_scope"}, + IdAttribute: "new_idAttribute", + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + UsePkce: true, + }) + after := time.Now() + require.NoError(t, err) + + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateOauth, err := idpRepo.GetOAuth(IAMCTX, pool, + idpRepo.IDCondition(addOAuth.Id), + instanceID, + nil, + ) + require.NoError(t, err) + + // event instance.idp.oauth.changed + // idp + assert.Equal(t, instanceID, oauth.InstanceID) + assert.Nil(t, oauth.OrgID) + assert.Equal(t, addOAuth.Id, updateOauth.ID) + assert.Equal(t, name, updateOauth.Name) + assert.Equal(t, domain.IDPTypeOAuth, domain.IDPType(*oauth.Type)) + assert.Equal(t, true, updateOauth.AllowLinking) + assert.Equal(t, true, updateOauth.AllowCreation) + assert.Equal(t, true, updateOauth.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*updateOauth.AutoLinkingField)) + assert.Equal(t, true, updateOauth.UsePKCE) + assert.WithinRange(t, updateOauth.UpdatedAt, before, after) + + // oauth + assert.Equal(t, "new_clientId", updateOauth.ClientID) + assert.NotEqual(t, oauth.ClientSecret, updateOauth.ClientSecret) + assert.Equal(t, "new_authorizationEndpoint", updateOauth.AuthorizationEndpoint) + assert.Equal(t, "new_tokenEndpoint", updateOauth.TokenEndpoint) + assert.Equal(t, "new_userEndpoint", updateOauth.UserEndpoint) + assert.Equal(t, []string{"new_scope"}, updateOauth.Scopes) + }, retryDuration, tick) + }) + + t.Run("test instance idp oidc added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add oidc + before := time.Now() + addOIDC, err := AdminClient.AddGenericOIDCProvider(IAMCTX, &admin.AddGenericOIDCProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + Scopes: []string{"scope"}, + Issuer: "issuer", + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + IsIdTokenMapping: false, + UsePkce: false, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for oidc + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + oidc, err := idpRepo.GetOIDC(IAMCTX, pool, idpRepo.IDCondition(addOIDC.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.oidc added + // idp + assert.Equal(t, instanceID, oidc.InstanceID) + assert.Nil(t, oidc.OrgID) + assert.Equal(t, addOIDC.Id, oidc.ID) + assert.Equal(t, name, oidc.Name) + assert.Equal(t, domain.IDPTypeOIDC, domain.IDPType(*oidc.Type)) + assert.Equal(t, false, oidc.AllowLinking) + assert.Equal(t, false, oidc.AllowCreation) + assert.Equal(t, false, oidc.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*oidc.AutoLinkingField)) + assert.WithinRange(t, oidc.CreatedAt, before, after) + assert.WithinRange(t, oidc.UpdatedAt, before, after) + + // oidc + assert.Equal(t, addOIDC.Id, oidc.ID) + assert.Equal(t, "clientId", oidc.ClientID) + assert.NotNil(t, oidc.ClientSecret) + assert.Equal(t, []string{"scope"}, oidc.Scopes) + assert.Equal(t, "issuer", oidc.Issuer) + assert.Equal(t, false, oidc.IsIDTokenMapping) + assert.Equal(t, false, oidc.UsePKCE) + }, retryDuration, tick) + }) + + t.Run("test instanceidp oidc changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + addOIDC, err := AdminClient.AddGenericOIDCProvider(IAMCTX, &admin.AddGenericOIDCProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + Scopes: []string{"scope"}, + Issuer: "issuer", + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + IsIdTokenMapping: false, + UsePkce: false, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for oidc + var oidc *domain.IDPOIDC + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + oidc, err = idpRepo.GetOIDC(IAMCTX, pool, idpRepo.IDCondition(addOIDC.Id), instanceID, nil) + require.NoError(t, err) + }, retryDuration, tick) + + name = "new_" + name + before := time.Now() + _, err = AdminClient.UpdateGenericOIDCProvider(IAMCTX, &admin.UpdateGenericOIDCProviderRequest{ + Id: addOIDC.Id, + Name: name, + Issuer: "new_issuer", + ClientId: "new_clientId", + ClientSecret: "new_clientSecret", + Scopes: []string{"new_scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + IsIdTokenMapping: true, + UsePkce: true, + }) + after := time.Now() + require.NoError(t, err) + + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateOIDC, err := idpRepo.GetOIDC(IAMCTX, pool, + idpRepo.IDCondition(addOIDC.Id), + instanceID, + nil, + ) + require.NoError(t, err) + + // event instance.idp.oidc.changed + // idp + assert.Equal(t, instanceID, oidc.InstanceID) + assert.Nil(t, oidc.OrgID) + assert.Equal(t, addOIDC.Id, oidc.ID) + assert.Equal(t, name, updateOIDC.Name) + assert.Equal(t, domain.IDPTypeOIDC, domain.IDPType(*oidc.Type)) + assert.Equal(t, true, updateOIDC.AllowLinking) + assert.Equal(t, true, updateOIDC.AllowCreation) + assert.Equal(t, true, updateOIDC.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*updateOIDC.AutoLinkingField)) + assert.WithinRange(t, updateOIDC.UpdatedAt, before, after) + + // oidc + assert.Equal(t, addOIDC.Id, updateOIDC.ID) + assert.Equal(t, "new_clientId", updateOIDC.ClientID) + assert.NotEqual(t, oidc.ClientSecret, updateOIDC.ClientSecret) + assert.Equal(t, []string{"new_scope"}, updateOIDC.Scopes) + assert.Equal(t, true, updateOIDC.IsIDTokenMapping) + assert.Equal(t, true, updateOIDC.UsePKCE) + }, retryDuration, tick) + }) + + t.Run("test instance idp oidc migrated azure migration reduces", func(t *testing.T) { + name := gofakeit.Name() + + // create OIDC + addOIDC, err := AdminClient.AddGenericOIDCProvider(IAMCTX, &admin.AddGenericOIDCProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + Scopes: []string{"scope"}, + Issuer: "issuer", + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + IsIdTokenMapping: false, + UsePkce: false, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + var oidc *domain.IDPOIDC + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + oidc, err = idpRepo.GetOIDC(IAMCTX, pool, idpRepo.IDCondition(addOIDC.Id), instanceID, nil) + require.NoError(t, err) + assert.Equal(t, domain.IDPTypeOIDC, domain.IDPType(*oidc.Type)) + }, retryDuration, tick) + + before := time.Now() + _, err = AdminClient.MigrateGenericOIDCProvider(IAMCTX, &admin.MigrateGenericOIDCProviderRequest{ + Id: addOIDC.Id, + Template: &admin.MigrateGenericOIDCProviderRequest_Azure{ + Azure: &admin.AddAzureADProviderRequest{ + Name: name, + ClientId: "new_clientId", + ClientSecret: "new_clientSecret", + Tenant: &idp_grpc.AzureADTenant{ + Type: &idp_grpc.AzureADTenant_TenantType{ + TenantType: idp.AzureADTenantType_AZURE_AD_TENANT_TYPE_ORGANISATIONS, + }, + }, + EmailVerified: true, + Scopes: []string{"new_scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }, + }, + }) + after := time.Now() + require.NoError(t, err) + + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + azure, err := idpRepo.GetAzureAD(IAMCTX, pool, idpRepo.IDCondition(addOIDC.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.oidc.migrated.azure + // idp + assert.Equal(t, instanceID, azure.InstanceID) + assert.Nil(t, azure.OrgID) + assert.Equal(t, addOIDC.Id, azure.ID) + assert.Equal(t, name, azure.Name) + // type = azure + assert.Equal(t, domain.IDPTypeAzure, domain.IDPType(*azure.Type)) + assert.Equal(t, true, azure.AllowLinking) + assert.Equal(t, true, azure.AllowCreation) + assert.Equal(t, true, azure.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*azure.AutoLinkingField)) + assert.WithinRange(t, azure.UpdatedAt, before, after) + + // oidc + assert.Equal(t, "new_clientId", azure.ClientID) + assert.NotEqual(t, oidc.ClientSecret, azure.ClientSecret) + assert.Equal(t, domain.AzureTenantTypeOrganizations, azure.Tenant) + assert.Equal(t, true, azure.IsEmailVerified) + assert.Equal(t, []string{"new_scope"}, azure.Scopes) + }, retryDuration, tick) + }) + + t.Run("test instance idp oidc migrated google migration reduces", func(t *testing.T) { + name := gofakeit.Name() + + // create OIDC + addOIDC, err := AdminClient.AddGenericOIDCProvider(IAMCTX, &admin.AddGenericOIDCProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + Scopes: []string{"scope"}, + Issuer: "issuer", + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + IsIdTokenMapping: false, + UsePkce: false, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + var oidc *domain.IDPOIDC + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + oidc, err = idpRepo.GetOIDC(IAMCTX, pool, idpRepo.IDCondition(addOIDC.Id), instanceID, nil) + require.NoError(t, err) + assert.Equal(t, domain.IDPTypeOIDC, domain.IDPType(*oidc.Type)) + }, retryDuration, tick) + + before := time.Now() + _, err = AdminClient.MigrateGenericOIDCProvider(IAMCTX, &admin.MigrateGenericOIDCProviderRequest{ + Id: addOIDC.Id, + Template: &admin.MigrateGenericOIDCProviderRequest_Google{ + Google: &admin.AddGoogleProviderRequest{ + Name: name, + ClientId: "new_clientId", + ClientSecret: "new_clientSecret", + Scopes: []string{"new_scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }, + }, + }) + after := time.Now() + require.NoError(t, err) + + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + google, err := idpRepo.GetGoogle(IAMCTX, pool, idpRepo.IDCondition(addOIDC.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.oidc.migrated.google + // idp + assert.Equal(t, instanceID, google.InstanceID) + assert.Nil(t, google.OrgID) + assert.Equal(t, addOIDC.Id, google.ID) + assert.Equal(t, name, google.Name) + // type = google + assert.Equal(t, domain.IDPTypeGoogle, domain.IDPType(*google.Type)) + assert.Equal(t, true, google.AllowLinking) + assert.Equal(t, true, google.AllowCreation) + assert.Equal(t, true, google.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*google.AutoLinkingField)) + assert.WithinRange(t, google.UpdatedAt, before, after) + + // oidc + assert.Equal(t, "new_clientId", google.ClientID) + assert.NotEqual(t, oidc.ClientSecret, google.ClientSecret) + assert.Equal(t, []string{"new_scope"}, google.Scopes) + }, retryDuration, tick) + }) + + t.Run("test instance idp jwt added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add jwt + before := time.Now() + addJWT, err := AdminClient.AddJWTProvider(IAMCTX, &admin.AddJWTProviderRequest{ + Name: name, + Issuer: "issuer", + JwtEndpoint: "jwtEndpoint", + KeysEndpoint: "keyEndpoint", + HeaderName: "headerName", + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for jwt + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + jwt, err := idpRepo.GetJWT(IAMCTX, pool, idpRepo.IDCondition(addJWT.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.jwt.added + // idp + assert.Equal(t, instanceID, jwt.InstanceID) + assert.Nil(t, jwt.OrgID) + assert.Equal(t, addJWT.Id, jwt.ID) + assert.Equal(t, name, jwt.Name) + assert.Equal(t, domain.IDPTypeJWT, domain.IDPType(*jwt.Type)) + assert.Equal(t, false, jwt.AllowLinking) + assert.Equal(t, false, jwt.AllowCreation) + assert.Equal(t, false, jwt.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*jwt.AutoLinkingField)) + assert.WithinRange(t, jwt.CreatedAt, before, after) + assert.WithinRange(t, jwt.UpdatedAt, before, after) + + // jwt + assert.Equal(t, "jwtEndpoint", jwt.JWTEndpoint) + assert.Equal(t, "issuer", jwt.Issuer) + assert.Equal(t, "keyEndpoint", jwt.KeysEndpoint) + assert.Equal(t, "headerName", jwt.HeaderName) + }, retryDuration, tick) + }) + + t.Run("test instance idp jwt changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add jwt + addJWT, err := AdminClient.AddJWTProvider(IAMCTX, &admin.AddJWTProviderRequest{ + Name: name, + Issuer: "issuer", + JwtEndpoint: "jwtEndpoint", + KeysEndpoint: "keyEndpoint", + HeaderName: "headerName", + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + require.NoError(t, err) + + name = "new_" + name + // change jwt + before := time.Now() + _, err = AdminClient.UpdateJWTProvider(IAMCTX, &admin.UpdateJWTProviderRequest{ + Id: addJWT.Id, + Name: name, + Issuer: "new_issuer", + JwtEndpoint: "new_jwtEndpoint", + KeysEndpoint: "new_keyEndpoint", + HeaderName: "new_headerName", + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for jwt + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + jwt, err := idpRepo.GetJWT(IAMCTX, pool, idpRepo.IDCondition(addJWT.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.jwt.added + // idp + assert.Equal(t, instanceID, jwt.InstanceID) + assert.Nil(t, jwt.OrgID) + assert.Equal(t, addJWT.Id, jwt.ID) + assert.Equal(t, name, jwt.Name) + assert.Equal(t, domain.IDPTypeJWT, domain.IDPType(*jwt.Type)) + assert.Equal(t, true, jwt.AllowLinking) + assert.Equal(t, true, jwt.AllowCreation) + assert.Equal(t, true, jwt.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*jwt.AutoLinkingField)) + assert.WithinRange(t, jwt.UpdatedAt, before, after) + + // jwt + assert.Equal(t, "new_jwtEndpoint", jwt.JWTEndpoint) + assert.Equal(t, "new_issuer", jwt.Issuer) + assert.Equal(t, "new_keyEndpoint", jwt.KeysEndpoint) + assert.Equal(t, "new_headerName", jwt.HeaderName) + }, retryDuration, tick) + }) + + t.Run("test instance idp azure added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add azure + before := time.Now() + addAzure, err := AdminClient.AddAzureADProvider(IAMCTX, &admin.AddAzureADProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + Tenant: &idp_grpc.AzureADTenant{ + Type: &idp_grpc.AzureADTenant_TenantType{ + TenantType: idp.AzureADTenantType_AZURE_AD_TENANT_TYPE_ORGANISATIONS, + }, + }, + EmailVerified: true, + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for azure + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + azure, err := idpRepo.GetAzureAD(IAMCTX, pool, idpRepo.IDCondition(addAzure.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.azure.added + // idp + assert.Equal(t, instanceID, azure.InstanceID) + assert.Nil(t, azure.OrgID) + assert.Equal(t, addAzure.Id, azure.ID) + assert.Equal(t, name, azure.Name) + assert.Equal(t, domain.IDPTypeAzure, domain.IDPType(*azure.Type)) + assert.Equal(t, true, azure.AllowLinking) + assert.Equal(t, true, azure.AllowCreation) + assert.Equal(t, true, azure.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*azure.AutoLinkingField)) + assert.WithinRange(t, azure.UpdatedAt, before, after) + + // azure + assert.Equal(t, "clientId", azure.ClientID) + assert.NotNil(t, azure.ClientSecret) + assert.Equal(t, domain.AzureTenantTypeOrganizations, azure.Tenant) + assert.Equal(t, true, azure.IsEmailVerified) + assert.Equal(t, []string{"scope"}, azure.Scopes) + }, retryDuration, tick) + }) + + t.Run("test instance idp azure changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add azure + addAzure, err := AdminClient.AddAzureADProvider(IAMCTX, &admin.AddAzureADProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + Tenant: &idp_grpc.AzureADTenant{ + Type: &idp_grpc.AzureADTenant_TenantType{ + TenantType: idp.AzureADTenantType_AZURE_AD_TENANT_TYPE_ORGANISATIONS, + }, + }, + EmailVerified: false, + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + var azure *domain.IDPAzureAD + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + azure, err = idpRepo.GetAzureAD(IAMCTX, pool, idpRepo.IDCondition(addAzure.Id), instanceID, nil) + require.NoError(t, err) + assert.Equal(t, addAzure.Id, azure.ID) + }, retryDuration, tick) + + name = "new_" + name + // change azure + before := time.Now() + _, err = AdminClient.UpdateAzureADProvider(IAMCTX, &admin.UpdateAzureADProviderRequest{ + Id: addAzure.Id, + Name: name, + ClientId: "new_clientId", + ClientSecret: "new_clientSecret", + Tenant: &idp_grpc.AzureADTenant{ + Type: &idp_grpc.AzureADTenant_TenantType{ + TenantType: idp.AzureADTenantType_AZURE_AD_TENANT_TYPE_CONSUMERS, + }, + }, + EmailVerified: true, + Scopes: []string{"new_scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + after := time.Now() + require.NoError(t, err) + + // check values for azure + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateAzure, err := idpRepo.GetAzureAD(IAMCTX, pool, idpRepo.IDCondition(addAzure.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.azure.changed + // idp + assert.Equal(t, instanceID, updateAzure.InstanceID) + assert.Nil(t, updateAzure.OrgID) + assert.Equal(t, addAzure.Id, updateAzure.ID) + assert.Equal(t, name, updateAzure.Name) + assert.Equal(t, domain.IDPTypeAzure, domain.IDPType(*updateAzure.Type)) + assert.Equal(t, true, updateAzure.AllowLinking) + assert.Equal(t, true, updateAzure.AllowCreation) + assert.Equal(t, true, updateAzure.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*updateAzure.AutoLinkingField)) + assert.WithinRange(t, updateAzure.UpdatedAt, before, after) + + // azure + assert.Equal(t, "new_clientId", updateAzure.ClientID) + assert.NotEqual(t, azure.ClientSecret, updateAzure.ClientSecret) + assert.Equal(t, domain.AzureTenantTypeConsumers, updateAzure.Tenant) + assert.Equal(t, true, updateAzure.IsEmailVerified) + assert.Equal(t, []string{"new_scope"}, updateAzure.Scopes) + }, retryDuration, tick) + }) + + t.Run("test instance idp github added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add github + before := time.Now() + addGithub, err := AdminClient.AddGitHubProvider(IAMCTX, &admin.AddGitHubProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for github + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + github, err := idpRepo.GetGithub(IAMCTX, pool, idpRepo.IDCondition(addGithub.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.github.added + // idp + assert.Equal(t, instanceID, github.InstanceID) + assert.Nil(t, github.OrgID) + assert.Equal(t, addGithub.Id, github.ID) + assert.Equal(t, name, github.Name) + assert.Equal(t, domain.IDPTypeGitHub, domain.IDPType(*github.Type)) + assert.Equal(t, false, github.AllowLinking) + assert.Equal(t, false, github.AllowCreation) + assert.Equal(t, false, github.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*github.AutoLinkingField)) + assert.WithinRange(t, github.UpdatedAt, before, after) + + assert.Equal(t, "clientId", github.ClientID) + assert.NotNil(t, github.ClientSecret) + assert.Equal(t, []string{"scope"}, github.Scopes) + }, retryDuration, tick) + }) + + t.Run("test instance idp github changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add github + addGithub, err := AdminClient.AddGitHubProvider(IAMCTX, &admin.AddGitHubProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + var github *domain.IDPGithub + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + github, err = idpRepo.GetGithub(IAMCTX, pool, idpRepo.IDCondition(addGithub.Id), instanceID, nil) + require.NoError(t, err) + assert.Equal(t, addGithub.Id, github.ID) + }, retryDuration, tick) + + name = "new_" + name + // change github + before := time.Now() + _, err = AdminClient.UpdateGitHubProvider(IAMCTX, &admin.UpdateGitHubProviderRequest{ + Id: addGithub.Id, + Name: name, + ClientId: "new_clientId", + ClientSecret: "new_clientSecret", + Scopes: []string{"new_scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + after := time.Now() + require.NoError(t, err) + + // check values for azure + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateGithub, err := idpRepo.GetGithub(IAMCTX, pool, idpRepo.IDCondition(addGithub.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.github.changed + // idp + assert.Equal(t, instanceID, updateGithub.InstanceID) + assert.Nil(t, updateGithub.OrgID) + assert.Equal(t, addGithub.Id, updateGithub.ID) + assert.Equal(t, name, updateGithub.Name) + assert.Equal(t, domain.IDPTypeGitHub, domain.IDPType(*updateGithub.Type)) + assert.Equal(t, true, updateGithub.AllowLinking) + assert.Equal(t, true, updateGithub.AllowCreation) + assert.Equal(t, true, updateGithub.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*updateGithub.AutoLinkingField)) + assert.WithinRange(t, updateGithub.UpdatedAt, before, after) + + // github + assert.Equal(t, "new_clientId", updateGithub.ClientID) + assert.NotEqual(t, github.ClientSecret, updateGithub.ClientSecret) + assert.Equal(t, []string{"new_scope"}, updateGithub.Scopes) + }, retryDuration, tick) + }) + + t.Run("test instance idp github enterprise added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add github enterprise + before := time.Now() + addGithubEnterprise, err := AdminClient.AddGitHubEnterpriseServerProvider(IAMCTX, &admin.AddGitHubEnterpriseServerProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + AuthorizationEndpoint: "authorizationEndpoint", + TokenEndpoint: "tokenEndpoint", + UserEndpoint: "userEndpoint", + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for github enterprise + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + githubEnterprise, err := idpRepo.GetGithubEnterprise(IAMCTX, pool, idpRepo.IDCondition(addGithubEnterprise.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.github_enterprise.added + // idp + assert.Equal(t, instanceID, githubEnterprise.InstanceID) + assert.Nil(t, githubEnterprise.OrgID) + assert.Equal(t, addGithubEnterprise.Id, githubEnterprise.ID) + assert.Equal(t, name, githubEnterprise.Name) + assert.Equal(t, domain.IDPTypeGitHubEnterprise, domain.IDPType(*githubEnterprise.Type)) + assert.Equal(t, false, githubEnterprise.AllowLinking) + assert.Equal(t, false, githubEnterprise.AllowCreation) + assert.Equal(t, false, githubEnterprise.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*githubEnterprise.AutoLinkingField)) + assert.WithinRange(t, githubEnterprise.CreatedAt, before, after) + assert.WithinRange(t, githubEnterprise.UpdatedAt, before, after) + + // github enterprise + assert.Equal(t, "clientId", githubEnterprise.ClientID) + assert.NotNil(t, githubEnterprise.ClientSecret) + assert.Equal(t, "authorizationEndpoint", githubEnterprise.AuthorizationEndpoint) + assert.Equal(t, "tokenEndpoint", githubEnterprise.TokenEndpoint) + assert.Equal(t, "userEndpoint", githubEnterprise.UserEndpoint) + assert.Equal(t, []string{"scope"}, githubEnterprise.Scopes) + }, retryDuration, tick) + }) + + t.Run("test instance idp github enterprise changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add github enterprise + addGithubEnterprise, err := AdminClient.AddGitHubEnterpriseServerProvider(IAMCTX, &admin.AddGitHubEnterpriseServerProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + AuthorizationEndpoint: "authorizationEndpoint", + TokenEndpoint: "tokenEndpoint", + UserEndpoint: "userEndpoint", + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + var githubEnterprise *domain.IDPGithubEnterprise + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + githubEnterprise, err = idpRepo.GetGithubEnterprise(IAMCTX, pool, idpRepo.IDCondition(addGithubEnterprise.Id), instanceID, nil) + require.NoError(t, err) + assert.Equal(t, addGithubEnterprise.Id, githubEnterprise.ID) + }, retryDuration, tick) + + name = "new_" + name + // change github enterprise + before := time.Now() + _, err = AdminClient.UpdateGitHubEnterpriseServerProvider(IAMCTX, &admin.UpdateGitHubEnterpriseServerProviderRequest{ + Id: addGithubEnterprise.Id, + Name: name, + ClientId: "new_clientId", + ClientSecret: "new_clientSecret", + AuthorizationEndpoint: "new_authorizationEndpoint", + TokenEndpoint: "new_tokenEndpoint", + UserEndpoint: "new_userEndpoint", + Scopes: []string{"new_scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + after := time.Now() + require.NoError(t, err) + + // check values for azure + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateGithubEnterprise, err := idpRepo.GetGithubEnterprise(IAMCTX, pool, idpRepo.IDCondition(addGithubEnterprise.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.github_enterprise.changed + // idp + assert.Equal(t, instanceID, githubEnterprise.InstanceID) + assert.Nil(t, githubEnterprise.OrgID) + assert.Equal(t, addGithubEnterprise.Id, updateGithubEnterprise.ID) + assert.Equal(t, name, updateGithubEnterprise.Name) + assert.Equal(t, domain.IDPTypeGitHubEnterprise, domain.IDPType(*updateGithubEnterprise.Type)) + assert.Equal(t, false, updateGithubEnterprise.AllowLinking) + assert.Equal(t, false, updateGithubEnterprise.AllowCreation) + assert.Equal(t, false, updateGithubEnterprise.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*updateGithubEnterprise.AutoLinkingField)) + assert.WithinRange(t, updateGithubEnterprise.UpdatedAt, before, after) + + // github enterprise + assert.Equal(t, "new_clientId", updateGithubEnterprise.ClientID) + assert.NotNil(t, updateGithubEnterprise.ClientSecret) + assert.Equal(t, "new_authorizationEndpoint", updateGithubEnterprise.AuthorizationEndpoint) + assert.Equal(t, "new_tokenEndpoint", updateGithubEnterprise.TokenEndpoint) + assert.Equal(t, "new_userEndpoint", updateGithubEnterprise.UserEndpoint) + assert.Equal(t, []string{"new_scope"}, updateGithubEnterprise.Scopes) + }, retryDuration, tick) + }) + + t.Run("test instance idp gitlab added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add gitlab + before := time.Now() + addGithub, err := AdminClient.AddGitLabProvider(IAMCTX, &admin.AddGitLabProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for gitlab + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + gitlab, err := idpRepo.GetGitlab(IAMCTX, pool, idpRepo.IDCondition(addGithub.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.gitlab.added + // idp + assert.Equal(t, instanceID, gitlab.InstanceID) + assert.Nil(t, gitlab.OrgID) + assert.Equal(t, addGithub.Id, gitlab.ID) + assert.Equal(t, name, gitlab.Name) + assert.Equal(t, domain.IDPTypeGitLab, domain.IDPType(*gitlab.Type)) + assert.Equal(t, false, gitlab.AllowLinking) + assert.Equal(t, false, gitlab.AllowCreation) + assert.Equal(t, false, gitlab.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*gitlab.AutoLinkingField)) + assert.WithinRange(t, gitlab.CreatedAt, before, after) + assert.WithinRange(t, gitlab.UpdatedAt, before, after) + + // gitlab + assert.Equal(t, "clientId", gitlab.ClientID) + assert.NotNil(t, gitlab.ClientSecret) + assert.Equal(t, []string{"scope"}, gitlab.Scopes) + }, retryDuration, tick) + }) + + t.Run("test instance idp gitlab changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add gitlab + addGitlab, err := AdminClient.AddGitLabProvider(IAMCTX, &admin.AddGitLabProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + var githlab *domain.IDPGitlab + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + githlab, err = idpRepo.GetGitlab(IAMCTX, pool, idpRepo.IDCondition(addGitlab.Id), instanceID, nil) + require.NoError(t, err) + assert.Equal(t, addGitlab.Id, githlab.ID) + }, retryDuration, tick) + + name = "new_" + name + // change gitlab + before := time.Now() + _, err = AdminClient.UpdateGitLabProvider(IAMCTX, &admin.UpdateGitLabProviderRequest{ + Id: addGitlab.Id, + Name: name, + ClientId: "new_clientId", + ClientSecret: "new_clientSecret", + Scopes: []string{"new_scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + after := time.Now() + require.NoError(t, err) + + // check values for gitlab + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateGitlab, err := idpRepo.GetGitlab(IAMCTX, pool, idpRepo.IDCondition(addGitlab.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.gitlab.changed + // idp + assert.Equal(t, instanceID, updateGitlab.InstanceID) + assert.Nil(t, updateGitlab.OrgID) + assert.Equal(t, addGitlab.Id, updateGitlab.ID) + assert.Equal(t, name, updateGitlab.Name) + assert.Equal(t, true, updateGitlab.AllowLinking) + assert.Equal(t, true, updateGitlab.AllowCreation) + assert.Equal(t, true, updateGitlab.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*updateGitlab.AutoLinkingField)) + assert.WithinRange(t, updateGitlab.UpdatedAt, before, after) + + // gitlab + assert.Equal(t, "new_clientId", updateGitlab.ClientID) + assert.NotEqual(t, githlab.ClientSecret, updateGitlab.ClientSecret) + assert.Equal(t, domain.IDPTypeGitLab, domain.IDPType(*updateGitlab.Type)) + assert.Equal(t, []string{"new_scope"}, updateGitlab.Scopes) + }, retryDuration, tick) + }) + + t.Run("test instance idp gitlab self hosted added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add gitlab self hosted + before := time.Now() + addGitlabSelfHosted, err := AdminClient.AddGitLabSelfHostedProvider(IAMCTX, &admin.AddGitLabSelfHostedProviderRequest{ + Name: name, + Issuer: "issuer", + ClientId: "clientId", + ClientSecret: "clientSecret", + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for gitlab self hosted + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + gitlabSelfHosted, err := idpRepo.GetGitlabSelfHosting(IAMCTX, pool, idpRepo.IDCondition(addGitlabSelfHosted.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.gitlab_self_hosted.added + // idp + assert.Equal(t, instanceID, gitlabSelfHosted.InstanceID) + assert.Nil(t, gitlabSelfHosted.OrgID) + assert.Equal(t, addGitlabSelfHosted.Id, gitlabSelfHosted.ID) + assert.Equal(t, name, gitlabSelfHosted.Name) + assert.Equal(t, domain.IDPTypeGitLabSelfHosted, domain.IDPType(*gitlabSelfHosted.Type)) + assert.Equal(t, false, gitlabSelfHosted.AllowLinking) + assert.Equal(t, false, gitlabSelfHosted.AllowCreation) + assert.Equal(t, false, gitlabSelfHosted.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*gitlabSelfHosted.AutoLinkingField)) + assert.WithinRange(t, gitlabSelfHosted.CreatedAt, before, after) + assert.WithinRange(t, gitlabSelfHosted.UpdatedAt, before, after) + + // gitlab self hosted + assert.Equal(t, "clientId", gitlabSelfHosted.ClientID) + assert.Equal(t, "issuer", gitlabSelfHosted.Issuer) + assert.NotNil(t, gitlabSelfHosted.ClientSecret) + assert.Equal(t, []string{"scope"}, gitlabSelfHosted.Scopes) + }, retryDuration, tick) + }) + + t.Run("test instance idp gitlab self hosted changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add gitlab self hosted + addGitlabSelfHosted, err := AdminClient.AddGitLabSelfHostedProvider(IAMCTX, &admin.AddGitLabSelfHostedProviderRequest{ + Name: name, + Issuer: "issuer", + ClientId: "clientId", + ClientSecret: "clientSecret", + 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() + + var githlabSelfHosted *domain.IDPGitlabSelfHosting + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + githlabSelfHosted, err = idpRepo.GetGitlabSelfHosting(IAMCTX, pool, idpRepo.IDCondition(addGitlabSelfHosted.Id), instanceID, nil) + require.NoError(t, err) + assert.Equal(t, addGitlabSelfHosted.Id, githlabSelfHosted.ID) + }, retryDuration, tick) + + name = "new_" + name + // change gitlab self hosted + before := time.Now() + _, err = AdminClient.UpdateGitLabSelfHostedProvider(IAMCTX, &admin.UpdateGitLabSelfHostedProviderRequest{ + Id: addGitlabSelfHosted.Id, + Name: name, + ClientId: "new_clientId", + Issuer: "new_issuer", + ClientSecret: "new_clientSecret", + Scopes: []string{"new_scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + after := time.Now() + require.NoError(t, err) + + // check values for gitlab self hosted + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateGitlabSelfHosted, err := idpRepo.GetGitlabSelfHosting(IAMCTX, pool, idpRepo.IDCondition(addGitlabSelfHosted.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.gitlab_self_hosted.changed + // idp + assert.Equal(t, instanceID, updateGitlabSelfHosted.InstanceID) + assert.Nil(t, updateGitlabSelfHosted.OrgID) + assert.Equal(t, addGitlabSelfHosted.Id, updateGitlabSelfHosted.ID) + assert.Equal(t, name, updateGitlabSelfHosted.Name) + assert.Equal(t, domain.IDPTypeGitLabSelfHosted, domain.IDPType(*updateGitlabSelfHosted.Type)) + assert.Equal(t, true, updateGitlabSelfHosted.AllowLinking) + assert.Equal(t, true, updateGitlabSelfHosted.AllowCreation) + assert.Equal(t, true, updateGitlabSelfHosted.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*updateGitlabSelfHosted.AutoLinkingField)) + assert.WithinRange(t, updateGitlabSelfHosted.UpdatedAt, before, after) + + // gitlab self hosted + assert.Equal(t, "new_clientId", updateGitlabSelfHosted.ClientID) + assert.Equal(t, "new_issuer", updateGitlabSelfHosted.Issuer) + assert.NotEqual(t, githlabSelfHosted.ClientSecret, updateGitlabSelfHosted.ClientSecret) + assert.Equal(t, []string{"new_scope"}, updateGitlabSelfHosted.Scopes) + }, retryDuration, tick) + }) + + t.Run("test instance idp google added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add google + before := time.Now() + addGoogle, err := AdminClient.AddGoogleProvider(IAMCTX, &admin.AddGoogleProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for google + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + google, err := idpRepo.GetGoogle(IAMCTX, pool, idpRepo.IDCondition(addGoogle.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.google.added + // idp + assert.Equal(t, instanceID, google.InstanceID) + assert.Nil(t, google.OrgID) + assert.Equal(t, addGoogle.Id, google.ID) + assert.Equal(t, name, google.Name) + assert.Equal(t, domain.IDPTypeGoogle, domain.IDPType(*google.Type)) + assert.Equal(t, false, google.AllowLinking) + assert.Equal(t, false, google.AllowCreation) + assert.Equal(t, false, google.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*google.AutoLinkingField)) + assert.WithinRange(t, google.CreatedAt, before, after) + assert.WithinRange(t, google.UpdatedAt, before, after) + + // google + assert.Equal(t, "clientId", google.ClientID) + assert.NotNil(t, google.ClientSecret) + assert.Equal(t, []string{"scope"}, google.Scopes) + }, retryDuration, tick) + }) + + t.Run("test instance idp google changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add google + addGoogle, err := AdminClient.AddGoogleProvider(IAMCTX, &admin.AddGoogleProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + 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() + + var google *domain.IDPGoogle + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + google, err = idpRepo.GetGoogle(IAMCTX, pool, idpRepo.IDCondition(addGoogle.Id), instanceID, nil) + require.NoError(t, err) + assert.Equal(t, addGoogle.Id, google.ID) + }, retryDuration, tick) + + name = "new_" + name + // change google + before := time.Now() + _, err = AdminClient.UpdateGoogleProvider(IAMCTX, &admin.UpdateGoogleProviderRequest{ + Id: addGoogle.Id, + Name: name, + ClientId: "new_clientId", + ClientSecret: "new_clientSecret", + Scopes: []string{"new_scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + after := time.Now() + require.NoError(t, err) + + // check values for google + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateGoogle, err := idpRepo.GetGoogle(IAMCTX, pool, idpRepo.IDCondition(addGoogle.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.google.changed + // idp + assert.Equal(t, instanceID, updateGoogle.InstanceID) + assert.Nil(t, updateGoogle.OrgID) + assert.Equal(t, addGoogle.Id, updateGoogle.ID) + assert.Equal(t, name, updateGoogle.Name) + assert.Equal(t, domain.IDPTypeGoogle, domain.IDPType(*updateGoogle.Type)) + assert.Equal(t, true, updateGoogle.AllowLinking) + assert.Equal(t, true, updateGoogle.AllowCreation) + assert.Equal(t, true, updateGoogle.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*updateGoogle.AutoLinkingField)) + assert.WithinRange(t, updateGoogle.UpdatedAt, before, after) + + // google + assert.Equal(t, "new_clientId", updateGoogle.ClientID) + assert.NotEqual(t, google.ClientSecret, updateGoogle.ClientSecret) + assert.Equal(t, []string{"new_scope"}, updateGoogle.Scopes) + }, retryDuration, tick) + }) + + t.Run("test instance ldap added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add ldap + before := time.Now() + addLdap, err := AdminClient.AddLDAPProvider(IAMCTX, &admin.AddLDAPProviderRequest{ + Name: name, + Servers: []string{"servers"}, + StartTls: true, + BaseDn: "baseDN", + BindDn: "bindND", + BindPassword: "bindPassword", + UserBase: "userBase", + UserObjectClasses: []string{"userOhjectClasses"}, + UserFilters: []string{"userFilters"}, + Timeout: durationpb.New(time.Minute), + Attributes: &idp_grpc.LDAPAttributes{ + IdAttribute: "idAttribute", + FirstNameAttribute: "firstNameAttribute", + LastNameAttribute: "lastNameAttribute", + DisplayNameAttribute: "displayNameAttribute", + NickNameAttribute: "nickNameAttribute", + PreferredUsernameAttribute: "preferredUsernameAttribute", + EmailAttribute: "emailAttribute", + EmailVerifiedAttribute: "emailVerifiedAttribute", + PhoneAttribute: "phoneAttribute", + PhoneVerifiedAttribute: "phoneVerifiedAttribute", + PreferredLanguageAttribute: "preferredLanguageAttribute", + AvatarUrlAttribute: "avatarUrlAttribute", + ProfileAttribute: "profileAttribute", + }, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + ldap, err := idpRepo.GetLDAP(IAMCTX, pool, idpRepo.IDCondition(addLdap.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.ldap.v2.added + // idp + assert.Equal(t, instanceID, ldap.InstanceID) + assert.Nil(t, ldap.OrgID) + assert.Equal(t, addLdap.Id, ldap.ID) + assert.Equal(t, name, ldap.Name) + assert.Equal(t, domain.IDPTypeLDAP, domain.IDPType(*ldap.Type)) + assert.Equal(t, false, ldap.AllowLinking) + assert.Equal(t, false, ldap.AllowCreation) + assert.Equal(t, false, ldap.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*ldap.AutoLinkingField)) + assert.WithinRange(t, ldap.CreatedAt, before, after) + assert.WithinRange(t, ldap.UpdatedAt, before, after) + + // ldap + assert.Equal(t, []string{"servers"}, ldap.Servers) + assert.Equal(t, true, ldap.StartTLS) + assert.Equal(t, "baseDN", ldap.BaseDN) + assert.Equal(t, "bindND", ldap.BindDN) + assert.NotNil(t, ldap.BindPassword) + assert.Equal(t, "userBase", ldap.UserBase) + assert.Equal(t, []string{"userOhjectClasses"}, ldap.UserObjectClasses) + assert.Equal(t, []string{"userFilters"}, ldap.UserFilters) + assert.Equal(t, time.Minute, ldap.Timeout) + assert.Equal(t, "idAttribute", ldap.IDAttribute) + assert.Equal(t, "firstNameAttribute", ldap.FirstNameAttribute) + assert.Equal(t, "lastNameAttribute", ldap.LastNameAttribute) + assert.Equal(t, "displayNameAttribute", ldap.DisplayNameAttribute) + assert.Equal(t, "nickNameAttribute", ldap.NickNameAttribute) + assert.Equal(t, "preferredUsernameAttribute", ldap.PreferredUsernameAttribute) + assert.Equal(t, "emailAttribute", ldap.EmailAttribute) + assert.Equal(t, "emailVerifiedAttribute", ldap.EmailVerifiedAttribute) + assert.Equal(t, "phoneAttribute", ldap.PhoneAttribute) + assert.Equal(t, "phoneVerifiedAttribute", ldap.PhoneVerifiedAttribute) + assert.Equal(t, "preferredLanguageAttribute", ldap.PreferredLanguageAttribute) + assert.Equal(t, "avatarUrlAttribute", ldap.AvatarURLAttribute) + assert.Equal(t, "profileAttribute", ldap.ProfileAttribute) + }, retryDuration, tick) + }) + + t.Run("test instance ldap changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add ldap + addLdap, err := AdminClient.AddLDAPProvider(IAMCTX, &admin.AddLDAPProviderRequest{ + Name: name, + Servers: []string{"servers"}, + StartTls: true, + BaseDn: "baseDN", + BindDn: "bindND", + BindPassword: "bindPassword", + UserBase: "userBase", + UserObjectClasses: []string{"userOhjectClasses"}, + UserFilters: []string{"userFilters"}, + Timeout: durationpb.New(time.Minute), + Attributes: &idp_grpc.LDAPAttributes{ + IdAttribute: "idAttribute", + FirstNameAttribute: "firstNameAttribute", + LastNameAttribute: "lastNameAttribute", + DisplayNameAttribute: "displayNameAttribute", + NickNameAttribute: "nickNameAttribute", + PreferredUsernameAttribute: "preferredUsernameAttribute", + EmailAttribute: "emailAttribute", + EmailVerifiedAttribute: "emailVerifiedAttribute", + PhoneAttribute: "phoneAttribute", + PhoneVerifiedAttribute: "phoneVerifiedAttribute", + PreferredLanguageAttribute: "preferredLanguageAttribute", + AvatarUrlAttribute: "avatarUrlAttribute", + ProfileAttribute: "profileAttribute", + }, + 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() + + var ldap *domain.IDPLDAP + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + ldap, err = idpRepo.GetLDAP(IAMCTX, pool, idpRepo.IDCondition(addLdap.Id), instanceID, nil) + require.NoError(t, err) + assert.Equal(t, addLdap.Id, ldap.ID) + }, retryDuration, tick) + + name = "new_" + name + // change ldap + before := time.Now() + _, err = AdminClient.UpdateLDAPProvider(IAMCTX, &admin.UpdateLDAPProviderRequest{ + Id: addLdap.Id, + Name: name, + Servers: []string{"new_servers"}, + StartTls: false, + BaseDn: "new_baseDN", + BindDn: "new_bindND", + BindPassword: "new_bindPassword", + UserBase: "new_userBase", + UserObjectClasses: []string{"new_userOhjectClasses"}, + UserFilters: []string{"new_userFilters"}, + Timeout: durationpb.New(time.Second), + Attributes: &idp_grpc.LDAPAttributes{ + IdAttribute: "new_idAttribute", + FirstNameAttribute: "new_firstNameAttribute", + LastNameAttribute: "new_lastNameAttribute", + DisplayNameAttribute: "new_displayNameAttribute", + NickNameAttribute: "new_nickNameAttribute", + PreferredUsernameAttribute: "new_preferredUsernameAttribute", + EmailAttribute: "new_emailAttribute", + EmailVerifiedAttribute: "new_emailVerifiedAttribute", + PhoneAttribute: "new_phoneAttribute", + PhoneVerifiedAttribute: "new_phoneVerifiedAttribute", + PreferredLanguageAttribute: "new_preferredLanguageAttribute", + AvatarUrlAttribute: "new_avatarUrlAttribute", + ProfileAttribute: "new_profileAttribute", + }, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + after := time.Now() + require.NoError(t, err) + + // check values for ldap + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateLdap, err := idpRepo.GetLDAP(IAMCTX, pool, idpRepo.IDCondition(addLdap.Id), instanceID, nil) + require.NoError(t, err) + + // event instance.idp.ldap.v2.changed + // idp + assert.Equal(t, instanceID, updateLdap.InstanceID) + assert.Nil(t, updateLdap.OrgID) + assert.Equal(t, addLdap.Id, updateLdap.ID) + assert.Equal(t, name, updateLdap.Name) + assert.Equal(t, domain.IDPTypeLDAP, domain.IDPType(*updateLdap.Type)) + assert.Equal(t, true, updateLdap.AllowLinking) + assert.Equal(t, true, updateLdap.AllowCreation) + assert.Equal(t, true, updateLdap.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*updateLdap.AutoLinkingField)) + assert.WithinRange(t, updateLdap.UpdatedAt, before, after) + + // ldap + assert.Equal(t, []string{"new_servers"}, updateLdap.Servers) + assert.Equal(t, false, updateLdap.StartTLS) + assert.Equal(t, "new_baseDN", updateLdap.BaseDN) + assert.Equal(t, "new_bindND", updateLdap.BindDN) + assert.NotEqual(t, ldap.BindPassword, updateLdap.BindPassword) + assert.Equal(t, "new_userBase", updateLdap.UserBase) + assert.Equal(t, []string{"new_userOhjectClasses"}, updateLdap.UserObjectClasses) + assert.Equal(t, []string{"new_userFilters"}, updateLdap.UserFilters) + assert.Equal(t, time.Second, updateLdap.Timeout) + assert.Equal(t, "new_idAttribute", updateLdap.IDAttribute) + assert.Equal(t, "new_firstNameAttribute", updateLdap.FirstNameAttribute) + assert.Equal(t, "new_lastNameAttribute", updateLdap.LastNameAttribute) + assert.Equal(t, "new_displayNameAttribute", updateLdap.DisplayNameAttribute) + assert.Equal(t, "new_nickNameAttribute", updateLdap.NickNameAttribute) + assert.Equal(t, "new_preferredUsernameAttribute", updateLdap.PreferredUsernameAttribute) + assert.Equal(t, "new_emailAttribute", updateLdap.EmailAttribute) + assert.Equal(t, "new_emailVerifiedAttribute", updateLdap.EmailVerifiedAttribute) + assert.Equal(t, "new_phoneAttribute", updateLdap.PhoneAttribute) + assert.Equal(t, "new_phoneVerifiedAttribute", updateLdap.PhoneVerifiedAttribute) + assert.Equal(t, "new_preferredLanguageAttribute", updateLdap.PreferredLanguageAttribute) + assert.Equal(t, "new_avatarUrlAttribute", updateLdap.AvatarURLAttribute) + 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 + before := time.Now() + addApple, err := AdminClient.AddAppleProvider(IAMCTX, &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, + }, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + apple, err := idpRepo.GetApple(IAMCTX, pool, 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, domain.IDPType(*apple.Type)) + assert.Equal(t, false, apple.AllowLinking) + assert.Equal(t, false, apple.AllowCreation) + assert.Equal(t, false, apple.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*apple.AutoLinkingField)) + assert.WithinRange(t, apple.CreatedAt, before, after) + assert.WithinRange(t, apple.UpdatedAt, before, after) + + // 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(IAMCTX, &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() + + var apple *domain.IDPApple + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + apple, err = idpRepo.GetApple(IAMCTX, pool, idpRepo.IDCondition(addApple.Id), instanceID, nil) + require.NoError(t, err) + assert.Equal(t, addApple.Id, apple.ID) + }, retryDuration, tick) + + name = "new_" + name + // change apple + before := time.Now() + _, err = AdminClient.UpdateAppleProvider(IAMCTX, &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, + }, + }) + after := time.Now() + require.NoError(t, err) + + // check values for apple + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateApple, err := idpRepo.GetApple(IAMCTX, pool, 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, domain.IDPType(*updateApple.Type)) + assert.Equal(t, true, updateApple.AllowLinking) + assert.Equal(t, true, updateApple.AllowCreation) + assert.Equal(t, true, updateApple.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*updateApple.AutoLinkingField)) + assert.WithinRange(t, updateApple.UpdatedAt, before, after) + + // 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 + before := time.Now() + addSAML, err := AdminClient.AddSAMLProvider(IAMCTX, &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, + }, + SignatureAlgorithm: idp.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA1, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + saml, err := idpRepo.GetSAML(IAMCTX, pool, 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, domain.IDPType(*saml.Type)) + assert.Equal(t, false, saml.AllowLinking) + assert.Equal(t, false, saml.AllowCreation) + assert.Equal(t, false, saml.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*saml.AutoLinkingField)) + assert.WithinRange(t, saml.CreatedAt, before, after) + assert.WithinRange(t, saml.UpdatedAt, before, after) + + // 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) + assert.Equal(t, "http://www.w3.org/2000/09/xmldsig#rsa-sha1", saml.SignatureAlgorithm) + }, retryDuration, tick) + }) + + t.Run("test instance saml changed reduces", func(t *testing.T) { + name := gofakeit.Name() + federatedLogoutEnabled := false + + // add saml + addSAML, err := AdminClient.AddSAMLProvider(IAMCTX, &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, + }, + SignatureAlgorithm: idp.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA1, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + var saml *domain.IDPSAML + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + saml, err = idpRepo.GetSAML(IAMCTX, pool, 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 + before := time.Now() + _, err = AdminClient.UpdateSAMLProvider(IAMCTX, &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, + }, + SignatureAlgorithm: idp.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA256, + }) + after := time.Now() + require.NoError(t, err) + + // check values for apple + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateSAML, err := idpRepo.GetSAML(IAMCTX, pool, 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, domain.IDPType(*updateSAML.Type)) + assert.Equal(t, true, updateSAML.AllowLinking) + assert.Equal(t, true, updateSAML.AllowCreation) + assert.Equal(t, true, updateSAML.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*updateSAML.AutoLinkingField)) + assert.WithinRange(t, updateSAML.UpdatedAt, before, after) + + // saml + assert.Equal(t, validSAMLMetadata2, updateSAML.Metadata) + assert.NotNil(t, updateSAML.Key) + assert.NotNil(t, updateSAML.Certificate) + 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) + assert.Equal(t, "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", updateSAML.SignatureAlgorithm) + }, retryDuration, tick) + }) + + t.Run("test instance iam remove reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add idp + addOIDC, err := AdminClient.AddOIDCIDP(IAMCTX, &admin.AddOIDCIDPRequest{ + Name: name, + StylingType: idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE, + ClientId: "clientID", + ClientSecret: "clientSecret", + Issuer: "issuer", + Scopes: []string{"scope"}, + DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + AutoRegister: true, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check idp exists + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + _, err := idpRepo.Get(IAMCTX, pool, + idpRepo.IDCondition(addOIDC.IdpId), + instanceID, + nil, + ) + require.NoError(t, err) + }, retryDuration, tick) + + // remove idp + _, err = AdminClient.DeleteProvider(IAMCTX, &admin.DeleteProviderRequest{ + Id: addOIDC.IdpId, + }) + require.NoError(t, err) + + // check idp is removed + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + _, err := idpRepo.Get(IAMCTX, pool, + idpRepo.IDCondition(addOIDC.IdpId), + instanceID, + nil, + ) + require.ErrorIs(t, &database.NoRowFoundError{}, err) + }, retryDuration, tick) + }) +} 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 new file mode 100644 index 00000000000..d361f6276fb --- /dev/null +++ b/backend/v3/storage/database/events_testing/id_provider_org_test.go @@ -0,0 +1,2414 @@ +//go:build integration + +package events_test + +import ( + "testing" + "time" + + "github.com/brianvoe/gofakeit/v6" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + durationpb "google.golang.org/protobuf/types/known/durationpb" + + "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" + "github.com/zitadel/zitadel/pkg/grpc/management" +) + +func TestServer_TestIDProviderOrgReduces(t *testing.T) { + instanceID := Instance.ID() + orgID := Instance.DefaultOrg.Id + + t.Run("test iam idp add reduces", func(t *testing.T) { + name := gofakeit.Name() + + before := time.Now() + addOIDC, err := MgmtClient.AddOrgOIDCIDP(IAMCTX, &management.AddOrgOIDCIDPRequest{ + Name: name, + StylingType: idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE, + ClientId: "clientID", + ClientSecret: "clientSecret", + Issuer: "issuer", + Scopes: []string{"scope"}, + DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + AutoRegister: true, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + idp, err := idpRepo.Get(IAMCTX, pool, + idpRepo.NameCondition(name), + instanceID, + &orgID, + ) + require.NoError(t, err) + + // event org.idp.config.added + assert.Equal(t, instanceID, idp.InstanceID) + assert.Equal(t, orgID, *idp.OrgID) + assert.Equal(t, addOIDC.IdpId, idp.ID) + assert.Equal(t, domain.IDPStateActive, idp.State) + assert.Equal(t, name, idp.Name) + assert.Equal(t, true, idp.AutoRegister) + assert.Equal(t, true, idp.AllowCreation) + assert.Equal(t, false, idp.AllowAutoUpdate) + assert.Equal(t, true, idp.AllowLinking) + assert.Nil(t, idp.AutoLinkingField) + assert.Equal(t, int16(idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE), *idp.StylingType) + assert.WithinRange(t, idp.UpdatedAt, before, after) + assert.WithinRange(t, idp.CreatedAt, before, after) + }, retryDuration, tick) + }) + + // t.Run("test iam idp update reduces", func(t *testing.T) { + // name := gofakeit.Name() + + // addOIDC, err := MgmtClient.AddOrgOIDCIDP(IAMCTX, &management.AddOrgOIDCIDPRequest{ + // Name: name, + // StylingType: idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE, + // ClientId: "clientID", + // ClientSecret: "clientSecret", + // Issuer: "issuer", + // Scopes: []string{"scope"}, + // DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + // UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + // AutoRegister: true, + // }) + // require.NoError(t, err) + + // name = "new_" + name + + // before := time.Now() + // _, err = MgmtClient.UpdateOrgIDP(IAMCTX, &management.UpdateOrgIDPRequest{ + // IdpId: addOIDC.IdpId, + // Name: name, + // StylingType: idp_grpc.IDPStylingType_STYLING_TYPE_UNSPECIFIED, + // AutoRegister: false, + // }) + // after := time.Now() + // require.NoError(t, err) + + // idpRepo := repository.IDProviderRepository() + + // retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + // assert.EventuallyWithT(t, func(t *assert.CollectT) { + // idp, err := idpRepo.Get(IAMCTX, pool, + // idpRepo.NameCondition(name), + // instanceID, + // &orgID, + // ) + // require.NoError(t, err) + + // // event org.idp.config.changed + // assert.Equal(t, addOIDC.IdpId, idp.ID) + // assert.Equal(t, name, idp.Name) + // assert.Equal(t, false, idp.AutoRegister) + // assert.Equal(t, int16(idp_grpc.IDPStylingType_STYLING_TYPE_UNSPECIFIED), *idp.StylingType) + // assert.WithinRange(t, idp.UpdatedAt, before, after) + // }, retryDuration, tick) + // }) + + t.Run("test iam idp deactivate reduces", func(t *testing.T) { + name := gofakeit.Name() + + addOIDC, err := MgmtClient.AddOrgOIDCIDP(IAMCTX, &management.AddOrgOIDCIDPRequest{ + Name: name, + StylingType: idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE, + ClientId: "clientID", + ClientSecret: "clientSecret", + Issuer: "issuer", + Scopes: []string{"scope"}, + DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + AutoRegister: true, + }) + require.NoError(t, err) + + // deactivate idp + before := time.Now() + _, err = MgmtClient.DeactivateOrgIDP(IAMCTX, &management.DeactivateOrgIDPRequest{ + IdpId: addOIDC.IdpId, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + idp, err := idpRepo.Get(IAMCTX, pool, + idpRepo.IDCondition(addOIDC.IdpId), + instanceID, + &orgID, + ) + require.NoError(t, err) + + // event org.idp.config.deactivated + assert.Equal(t, addOIDC.IdpId, idp.ID) + assert.Equal(t, domain.IDPStateInactive, idp.State) + assert.WithinRange(t, idp.UpdatedAt, before, after) + }, retryDuration, tick) + }) + + t.Run("test iam idp reactivate reduces", func(t *testing.T) { + name := gofakeit.Name() + + addOIDC, err := MgmtClient.AddOrgOIDCIDP(IAMCTX, &management.AddOrgOIDCIDPRequest{ + Name: name, + StylingType: idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE, + ClientId: "clientID", + ClientSecret: "clientSecret", + Issuer: "issuer", + Scopes: []string{"scope"}, + DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + AutoRegister: true, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // deactivate idp + _, err = MgmtClient.DeactivateOrgIDP(IAMCTX, &management.DeactivateOrgIDPRequest{ + IdpId: addOIDC.IdpId, + }) + require.NoError(t, err) + // wait for idp to be deactivated + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + idp, err := idpRepo.Get(IAMCTX, pool, + idpRepo.IDCondition(addOIDC.IdpId), + instanceID, + &orgID, + ) + require.NoError(t, err) + + assert.Equal(t, addOIDC.IdpId, idp.ID) + assert.Equal(t, domain.IDPStateInactive, idp.State) + }, retryDuration, tick) + + // reactivate idp + before := time.Now() + _, err = MgmtClient.ReactivateOrgIDP(IAMCTX, &management.ReactivateOrgIDPRequest{ + IdpId: addOIDC.IdpId, + }) + after := time.Now() + require.NoError(t, err) + + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + idp, err := idpRepo.Get(IAMCTX, pool, + idpRepo.IDCondition(addOIDC.IdpId), + instanceID, + &orgID, + ) + require.NoError(t, err) + + // event org.idp.config.reactivated + assert.Equal(t, addOIDC.IdpId, idp.ID) + assert.Equal(t, domain.IDPStateActive, idp.State) + assert.WithinRange(t, idp.UpdatedAt, before, after) + }, retryDuration, tick) + }) + + t.Run("test iam idp remove reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add idp + addOIDC, err := MgmtClient.AddOrgOIDCIDP(IAMCTX, &management.AddOrgOIDCIDPRequest{ + Name: name, + StylingType: idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE, + ClientId: "clientID", + ClientSecret: "clientSecret", + Issuer: "issuer", + Scopes: []string{"scope"}, + DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + AutoRegister: true, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // remove idp + _, err = MgmtClient.RemoveOrgIDP(IAMCTX, &management.RemoveOrgIDPRequest{ + IdpId: addOIDC.IdpId, + }) + require.NoError(t, err) + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*20) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + _, err := idpRepo.Get(IAMCTX, pool, + idpRepo.IDCondition(addOIDC.IdpId), + instanceID, + &orgID, + ) + + // event org.idp.config.remove + require.ErrorIs(t, &database.NoRowFoundError{}, err) + }, retryDuration, tick) + }) + + t.Run("test iam idp oidc added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add oidc + addOIDC, err := MgmtClient.AddOrgOIDCIDP(IAMCTX, &management.AddOrgOIDCIDPRequest{ + Name: name, + StylingType: idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE, + ClientId: "clientID", + ClientSecret: "clientSecret", + Issuer: "issuer", + Scopes: []string{"scope"}, + DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + AutoRegister: false, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + oidc, err := idpRepo.GetOIDC(IAMCTX, pool, + idpRepo.IDCondition(addOIDC.IdpId), + instanceID, + &orgID, + ) + require.NoError(t, err) + + // event org.idp.oidc.config.added + // idp + assert.Equal(t, instanceID, oidc.InstanceID) + assert.Equal(t, orgID, *oidc.OrgID) + assert.Equal(t, name, oidc.Name) + assert.Equal(t, addOIDC.IdpId, oidc.ID) + assert.Equal(t, domain.IDPTypeOIDC, domain.IDPType(*oidc.Type)) + + // oidc + assert.Equal(t, "issuer", oidc.Issuer) + assert.Equal(t, "clientID", oidc.ClientID) + assert.Equal(t, []string{"scope"}, oidc.Scopes) + assert.Equal(t, int16(idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE), *oidc.StylingType) + assert.Equal(t, false, oidc.AutoRegister) + assert.Equal(t, domain.OIDCMappingField(idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL), oidc.IDPDisplayNameMapping) + assert.Equal(t, domain.OIDCMappingField(idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL), oidc.UserNameMapping) + }, retryDuration, tick) + }) + + t.Run("test iam idp oidc changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add oidc + addOIDC, err := MgmtClient.AddOrgOIDCIDP(IAMCTX, &management.AddOrgOIDCIDPRequest{ + Name: name, + StylingType: idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE, + ClientId: "clientID", + ClientSecret: "clientSecret", + Issuer: "issuer", + Scopes: []string{"scope"}, + DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + AutoRegister: true, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check original values for OCID + var oidc *domain.IDPOIDC + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + oidc, err = idpRepo.GetOIDC(IAMCTX, pool, idpRepo.IDCondition(addOIDC.IdpId), instanceID, &orgID) + require.NoError(t, err) + assert.Equal(t, addOIDC.IdpId, oidc.ID) + }, retryDuration, tick) + + before := time.Now() + _, err = MgmtClient.UpdateOrgIDPOIDCConfig(IAMCTX, &management.UpdateOrgIDPOIDCConfigRequest{ + IdpId: addOIDC.IdpId, + ClientId: "new_clientID", + ClientSecret: "new_clientSecret", + Issuer: "new_issuer", + Scopes: []string{"new_scope"}, + DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_PREFERRED_USERNAME, + UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_PREFERRED_USERNAME, + }) + after := time.Now() + require.NoError(t, err) + + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateOIDC, err := idpRepo.GetOIDC(IAMCTX, pool, + idpRepo.IDCondition(addOIDC.IdpId), + instanceID, + &orgID, + ) + require.NoError(t, err) + + // event org.idp.oidc.config.changed + // idp + assert.Equal(t, instanceID, oidc.InstanceID) + assert.Equal(t, orgID, *oidc.OrgID) + assert.Equal(t, name, oidc.Name) + assert.Equal(t, addOIDC.IdpId, updateOIDC.ID) + assert.Equal(t, domain.IDPTypeOIDC, domain.IDPType(*updateOIDC.Type)) + assert.WithinRange(t, updateOIDC.UpdatedAt, before, after) + + // oidc + assert.Equal(t, instanceID, oidc.InstanceID) + assert.Equal(t, orgID, *oidc.OrgID) + assert.Equal(t, "new_issuer", updateOIDC.Issuer) + assert.Equal(t, "new_clientID", updateOIDC.ClientID) + assert.NotNil(t, oidc.ClientSecret) + assert.NotEqual(t, oidc.ClientSecret, updateOIDC.ClientSecret) + assert.Equal(t, []string{"new_scope"}, updateOIDC.Scopes) + assert.Equal(t, domain.OIDCMappingField(idp.OIDCMappingField_OIDC_MAPPING_FIELD_PREFERRED_USERNAME), updateOIDC.IDPDisplayNameMapping) + assert.Equal(t, domain.OIDCMappingField(idp.OIDCMappingField_OIDC_MAPPING_FIELD_PREFERRED_USERNAME), updateOIDC.UserNameMapping) + }, retryDuration, tick) + }) + + t.Run("test iam idp jwt added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add jwt + addJWT, err := MgmtClient.AddOrgJWTIDP(IAMCTX, &management.AddOrgJWTIDPRequest{ + Name: name, + StylingType: idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE, + JwtEndpoint: "jwtEndpoint", + Issuer: "issuer", + KeysEndpoint: "keyEndpoint", + HeaderName: "headerName", + AutoRegister: true, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + jwt, err := idpRepo.GetJWT(IAMCTX, pool, + idpRepo.IDCondition(addJWT.IdpId), + instanceID, + &orgID, + ) + require.NoError(t, err) + + // event org.idp.jwt.config.added + // idp + assert.Equal(t, instanceID, jwt.InstanceID) + assert.Equal(t, orgID, *jwt.OrgID) + assert.Equal(t, name, jwt.Name) + assert.Equal(t, addJWT.IdpId, jwt.ID) + assert.Equal(t, domain.IDPTypeJWT, domain.IDPType(*jwt.Type)) + assert.Equal(t, int16(idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE), *jwt.StylingType) + + // jwt + assert.Equal(t, "jwtEndpoint", jwt.JWTEndpoint) + assert.Equal(t, "issuer", jwt.Issuer) + assert.Equal(t, "keyEndpoint", jwt.KeysEndpoint) + assert.Equal(t, "headerName", jwt.HeaderName) + }, retryDuration, tick) + }) + + t.Run("test iam idp jwt changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add jwt + addJWT, err := MgmtClient.AddOrgJWTIDP(IAMCTX, &management.AddOrgJWTIDPRequest{ + Name: name, + StylingType: idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE, + JwtEndpoint: "jwtEndpoint", + Issuer: "issuer", + KeysEndpoint: "keyEndpoint", + HeaderName: "headerName", + AutoRegister: true, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + before := time.Now() + _, err = MgmtClient.UpdateOrgIDPJWTConfig(IAMCTX, &management.UpdateOrgIDPJWTConfigRequest{ + IdpId: addJWT.IdpId, + JwtEndpoint: "new_jwtEndpoint", + Issuer: "new_issuer", + KeysEndpoint: "new_keyEndpoint", + HeaderName: "new_headerName", + }) + after := time.Now() + require.NoError(t, err) + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateJWT, err := idpRepo.GetJWT(IAMCTX, pool, + idpRepo.IDCondition(addJWT.IdpId), + instanceID, + &orgID, + ) + require.NoError(t, err) + + // event org.idp.jwt.config.changed + // idp + assert.Equal(t, addJWT.IdpId, updateJWT.ID) + assert.Equal(t, orgID, *updateJWT.OrgID) + assert.Equal(t, domain.IDPTypeJWT, domain.IDPType(*updateJWT.Type)) + assert.WithinRange(t, updateJWT.UpdatedAt, before, after) + + // jwt + assert.Equal(t, "new_jwtEndpoint", updateJWT.JWTEndpoint) + assert.Equal(t, "new_issuer", updateJWT.Issuer) + assert.Equal(t, "new_keyEndpoint", updateJWT.KeysEndpoint) + assert.Equal(t, "new_headerName", updateJWT.HeaderName) + }, retryDuration, tick) + }) + + t.Run("test org idp oauth added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add oauth + before := time.Now() + addOAuth, err := MgmtClient.AddGenericOAuthProvider(IAMCTX, &management.AddGenericOAuthProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + AuthorizationEndpoint: "authoizationEndpoint", + TokenEndpoint: "tokenEndpoint", + UserEndpoint: "userEndpoint", + Scopes: []string{"scope"}, + IdAttribute: "idAttribute", + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + UsePkce: false, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for oauth + var oauth *domain.IDPOAuth + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + oauth, err = idpRepo.GetOAuth(IAMCTX, pool, idpRepo.IDCondition(addOAuth.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.idp.oauth.added + // idp + assert.Equal(t, instanceID, oauth.InstanceID) + assert.Equal(t, orgID, *oauth.OrgID) + assert.Equal(t, addOAuth.Id, oauth.ID) + assert.Equal(t, name, oauth.Name) + assert.Equal(t, domain.IDPTypeOAuth, domain.IDPType(*oauth.Type)) + assert.Equal(t, false, oauth.AllowLinking) + assert.Equal(t, false, oauth.AllowCreation) + assert.Equal(t, false, oauth.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*oauth.AutoLinkingField)) + assert.WithinRange(t, oauth.CreatedAt, before, after) + assert.WithinRange(t, oauth.UpdatedAt, before, after) + + // oauth + assert.Equal(t, "clientId", oauth.ClientID) + assert.NotNil(t, oauth.ClientSecret) + assert.Equal(t, "authoizationEndpoint", oauth.AuthorizationEndpoint) + assert.Equal(t, "tokenEndpoint", oauth.TokenEndpoint) + assert.Equal(t, "userEndpoint", oauth.UserEndpoint) + assert.Equal(t, []string{"scope"}, oauth.Scopes) + assert.Equal(t, "idAttribute", oauth.IDAttribute) + assert.Equal(t, false, oauth.UsePKCE) + }, retryDuration, tick) + }) + + t.Run("test org idp oauth changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add oauth + addOAuth, err := MgmtClient.AddGenericOAuthProvider(IAMCTX, &management.AddGenericOAuthProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + AuthorizationEndpoint: "authoizationEndpoint", + TokenEndpoint: "tokenEndpoint", + UserEndpoint: "userEndpoint", + Scopes: []string{"scope"}, + IdAttribute: "idAttribute", + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + UsePkce: false, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for oauth + var oauth *domain.IDPOAuth + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + oauth, err = idpRepo.GetOAuth(IAMCTX, pool, idpRepo.IDCondition(addOAuth.Id), instanceID, &orgID) + require.NoError(t, err) + assert.Equal(t, addOAuth.Id, oauth.ID) + }, retryDuration, tick) + + name = "new_" + name + before := time.Now() + _, err = MgmtClient.UpdateGenericOAuthProvider(IAMCTX, &management.UpdateGenericOAuthProviderRequest{ + Id: addOAuth.Id, + Name: name, + ClientId: "new_clientId", + ClientSecret: "new_clientSecret", + AuthorizationEndpoint: "new_authoizationEndpoint", + TokenEndpoint: "new_tokenEndpoint", + UserEndpoint: "new_userEndpoint", + Scopes: []string{"new_scope"}, + IdAttribute: "new_idAttribute", + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + UsePkce: true, + }) + after := time.Now() + require.NoError(t, err) + + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateOauth, err := idpRepo.GetOAuth(IAMCTX, pool, + idpRepo.IDCondition(addOAuth.Id), + instanceID, + &orgID, + ) + require.NoError(t, err) + + // event org.idp.oauth.changed + // idp + assert.Equal(t, instanceID, updateOauth.InstanceID) + assert.Equal(t, orgID, *updateOauth.OrgID) + assert.Equal(t, addOAuth.Id, updateOauth.ID) + assert.Equal(t, name, updateOauth.Name) + assert.Equal(t, domain.IDPTypeOAuth, domain.IDPType(*updateOauth.Type)) + assert.Equal(t, true, updateOauth.AllowLinking) + assert.Equal(t, true, updateOauth.AllowCreation) + assert.Equal(t, true, updateOauth.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*updateOauth.AutoLinkingField)) + assert.Equal(t, true, updateOauth.UsePKCE) + assert.WithinRange(t, updateOauth.UpdatedAt, before, after) + + // oauth + assert.Equal(t, "new_clientId", updateOauth.ClientID) + assert.NotEqual(t, oauth.ClientSecret, updateOauth.ClientSecret) + assert.Equal(t, "new_authoizationEndpoint", updateOauth.AuthorizationEndpoint) + assert.Equal(t, "new_tokenEndpoint", updateOauth.TokenEndpoint) + assert.Equal(t, "new_userEndpoint", updateOauth.UserEndpoint) + assert.Equal(t, []string{"new_scope"}, updateOauth.Scopes) + }, retryDuration, tick) + }) + + t.Run("test org idp oidc added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add oidc + before := time.Now() + addOIDC, err := MgmtClient.AddGenericOIDCProvider(IAMCTX, &management.AddGenericOIDCProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + Scopes: []string{"scope"}, + Issuer: "issuer", + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + IsIdTokenMapping: false, + UsePkce: false, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for oidc + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + oidc, err := idpRepo.GetOIDC(IAMCTX, pool, idpRepo.IDCondition(addOIDC.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.idp.oidc added + // idp + assert.Equal(t, instanceID, oidc.InstanceID) + assert.Equal(t, orgID, *oidc.OrgID) + assert.Equal(t, addOIDC.Id, oidc.ID) + assert.Equal(t, name, oidc.Name) + assert.Equal(t, domain.IDPTypeOIDC, domain.IDPType(*oidc.Type)) + assert.Equal(t, false, oidc.AllowLinking) + assert.Equal(t, false, oidc.AllowCreation) + assert.Equal(t, false, oidc.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*oidc.AutoLinkingField)) + assert.WithinRange(t, oidc.CreatedAt, before, after) + assert.WithinRange(t, oidc.UpdatedAt, before, after) + + // oidc + assert.Equal(t, addOIDC.Id, oidc.ID) + assert.Equal(t, "clientId", oidc.ClientID) + assert.NotNil(t, oidc.ClientSecret) + assert.Equal(t, []string{"scope"}, oidc.Scopes) + assert.Equal(t, "issuer", oidc.Issuer) + assert.Equal(t, false, oidc.IsIDTokenMapping) + assert.Equal(t, false, oidc.UsePKCE) + }, retryDuration, tick) + }) + + t.Run("test instanceidp oidc changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + addOIDC, err := MgmtClient.AddGenericOIDCProvider(IAMCTX, &management.AddGenericOIDCProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + Scopes: []string{"scope"}, + Issuer: "issuer", + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + IsIdTokenMapping: false, + UsePkce: false, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for oidc + var oidc *domain.IDPOIDC + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + oidc, err = idpRepo.GetOIDC(IAMCTX, pool, idpRepo.IDCondition(addOIDC.Id), instanceID, &orgID) + require.NoError(t, err) + }, retryDuration, tick) + + name = "new_" + name + before := time.Now() + _, err = MgmtClient.UpdateGenericOIDCProvider(IAMCTX, &management.UpdateGenericOIDCProviderRequest{ + Id: addOIDC.Id, + Name: name, + Issuer: "new_issuer", + ClientId: "new_clientId", + ClientSecret: "new_clientSecret", + Scopes: []string{"new_scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + IsIdTokenMapping: true, + UsePkce: true, + }) + after := time.Now() + require.NoError(t, err) + + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateOIDC, err := idpRepo.GetOIDC(IAMCTX, pool, + idpRepo.IDCondition(addOIDC.Id), + instanceID, + &orgID, + ) + require.NoError(t, err) + + // event org.idp.oidc.changed + // idp + assert.Equal(t, instanceID, updateOIDC.InstanceID) + assert.Equal(t, orgID, *updateOIDC.OrgID) + assert.Equal(t, addOIDC.Id, updateOIDC.ID) + assert.Equal(t, name, updateOIDC.Name) + assert.Equal(t, domain.IDPTypeOIDC, domain.IDPType(*updateOIDC.Type)) + assert.Equal(t, true, updateOIDC.AllowLinking) + assert.Equal(t, true, updateOIDC.AllowCreation) + assert.Equal(t, true, updateOIDC.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*updateOIDC.AutoLinkingField)) + assert.WithinRange(t, updateOIDC.UpdatedAt, before, after) + + // oidc + assert.Equal(t, "new_clientId", updateOIDC.ClientID) + assert.NotEqual(t, oidc.ClientSecret, updateOIDC.ClientSecret) + assert.Equal(t, []string{"new_scope"}, updateOIDC.Scopes) + assert.Equal(t, true, updateOIDC.IsIDTokenMapping) + assert.Equal(t, true, updateOIDC.UsePKCE) + }, retryDuration, tick) + }) + + t.Run("test org idp oidc migrated azure migration reduces", func(t *testing.T) { + name := gofakeit.Name() + + // create OIDC + addOIDC, err := MgmtClient.AddGenericOIDCProvider(IAMCTX, &management.AddGenericOIDCProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + Scopes: []string{"scope"}, + Issuer: "issuer", + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + IsIdTokenMapping: false, + UsePkce: false, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + var oidc *domain.IDPOIDC + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + oidc, err = idpRepo.GetOIDC(IAMCTX, pool, idpRepo.IDCondition(addOIDC.Id), instanceID, &orgID) + require.NoError(t, err) + assert.Equal(t, domain.IDPTypeOIDC, domain.IDPType(*oidc.Type)) + }, retryDuration, tick) + + before := time.Now() + _, err = MgmtClient.MigrateGenericOIDCProvider(IAMCTX, &management.MigrateGenericOIDCProviderRequest{ + Id: addOIDC.Id, + Template: &management.MigrateGenericOIDCProviderRequest_Azure{ + Azure: &management.AddAzureADProviderRequest{ + Name: name, + ClientId: "new_clientId", + ClientSecret: "new_clientSecret", + Tenant: &idp_grpc.AzureADTenant{ + Type: &idp_grpc.AzureADTenant_TenantType{ + TenantType: idp.AzureADTenantType_AZURE_AD_TENANT_TYPE_ORGANISATIONS, + }, + }, + EmailVerified: true, + Scopes: []string{"new_scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }, + }, + }) + after := time.Now() + require.NoError(t, err) + + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + azure, err := idpRepo.GetAzureAD(IAMCTX, pool, idpRepo.IDCondition(addOIDC.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.idp.oidc.migrated.azure + // idp + assert.Equal(t, instanceID, azure.InstanceID) + assert.Equal(t, orgID, *azure.OrgID) + assert.Equal(t, addOIDC.Id, azure.ID) + assert.Equal(t, name, azure.Name) + // type = azure + assert.Equal(t, domain.IDPTypeAzure, domain.IDPType(*azure.Type)) + assert.Equal(t, true, azure.AllowLinking) + assert.Equal(t, true, azure.AllowCreation) + assert.Equal(t, true, azure.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*azure.AutoLinkingField)) + assert.WithinRange(t, azure.UpdatedAt, before, after) + + // oidc + assert.Equal(t, "new_clientId", azure.ClientID) + assert.NotEqual(t, oidc.ClientSecret, azure.ClientSecret) + assert.Equal(t, domain.AzureTenantTypeOrganizations, azure.Tenant) + assert.Equal(t, true, azure.IsEmailVerified) + assert.Equal(t, []string{"new_scope"}, azure.Scopes) + }, retryDuration, tick) + }) + + t.Run("test org idp oidc migrated google migration reduces", func(t *testing.T) { + name := gofakeit.Name() + + // create OIDC + addOIDC, err := MgmtClient.AddGenericOIDCProvider(IAMCTX, &management.AddGenericOIDCProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + Scopes: []string{"scope"}, + Issuer: "issuer", + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + IsIdTokenMapping: false, + UsePkce: false, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + var oidc *domain.IDPOIDC + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + oidc, err = idpRepo.GetOIDC(IAMCTX, pool, idpRepo.IDCondition(addOIDC.Id), instanceID, &orgID) + require.NoError(t, err) + assert.Equal(t, domain.IDPTypeOIDC, domain.IDPType(*oidc.Type)) + }, retryDuration, tick) + + before := time.Now() + _, err = MgmtClient.MigrateGenericOIDCProvider(IAMCTX, &management.MigrateGenericOIDCProviderRequest{ + Id: addOIDC.Id, + Template: &management.MigrateGenericOIDCProviderRequest_Google{ + Google: &management.AddGoogleProviderRequest{ + Name: name, + ClientId: "new_clientId", + ClientSecret: "new_clientSecret", + Scopes: []string{"new_scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }, + }, + }) + after := time.Now() + require.NoError(t, err) + + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + google, err := idpRepo.GetGoogle(IAMCTX, pool, idpRepo.IDCondition(addOIDC.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.idp.oidc.migrated.google + // idp + assert.Equal(t, instanceID, google.InstanceID) + assert.Equal(t, orgID, *google.OrgID) + assert.Equal(t, addOIDC.Id, google.ID) + assert.Equal(t, name, google.Name) + // type = google + assert.Equal(t, domain.IDPTypeGoogle, domain.IDPType(*google.Type)) + assert.Equal(t, true, google.AllowLinking) + assert.Equal(t, true, google.AllowCreation) + assert.Equal(t, true, google.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*google.AutoLinkingField)) + assert.WithinRange(t, google.UpdatedAt, before, after) + + // oidc + assert.Equal(t, "new_clientId", google.ClientID) + assert.NotEqual(t, oidc.ClientSecret, google.ClientSecret) + assert.Equal(t, []string{"new_scope"}, google.Scopes) + }, retryDuration, tick) + }) + + t.Run("test org idp jwt added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add jwt + before := time.Now() + addJWT, err := MgmtClient.AddJWTProvider(IAMCTX, &management.AddJWTProviderRequest{ + Name: name, + Issuer: "issuer", + JwtEndpoint: "jwtEndpoint", + KeysEndpoint: "keyEndpoint", + HeaderName: "headerName", + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for jwt + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + jwt, err := idpRepo.GetJWT(IAMCTX, pool, idpRepo.IDCondition(addJWT.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.idp.jwt.added + // idp + assert.Equal(t, instanceID, jwt.InstanceID) + assert.Equal(t, orgID, *jwt.OrgID) + assert.Equal(t, addJWT.Id, jwt.ID) + assert.Equal(t, name, jwt.Name) + assert.Equal(t, domain.IDPTypeJWT, domain.IDPType(*jwt.Type)) + assert.Equal(t, false, jwt.AllowLinking) + assert.Equal(t, false, jwt.AllowCreation) + assert.Equal(t, false, jwt.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*jwt.AutoLinkingField)) + assert.WithinRange(t, jwt.CreatedAt, before, after) + assert.WithinRange(t, jwt.UpdatedAt, before, after) + + // jwt + assert.Equal(t, "jwtEndpoint", jwt.JWTEndpoint) + assert.Equal(t, "issuer", jwt.Issuer) + assert.Equal(t, "keyEndpoint", jwt.KeysEndpoint) + assert.Equal(t, "headerName", jwt.HeaderName) + }, retryDuration, tick) + }) + + t.Run("test org idp jwt changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add jwt + addJWT, err := MgmtClient.AddJWTProvider(IAMCTX, &management.AddJWTProviderRequest{ + Name: name, + Issuer: "issuer", + JwtEndpoint: "jwtEndpoint", + KeysEndpoint: "keyEndpoint", + HeaderName: "headerName", + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + require.NoError(t, err) + + name = "new_" + name + // change jwt + before := time.Now() + _, err = MgmtClient.UpdateJWTProvider(IAMCTX, &management.UpdateJWTProviderRequest{ + Id: addJWT.Id, + Name: name, + Issuer: "new_issuer", + JwtEndpoint: "new_jwtEndpoint", + KeysEndpoint: "new_keyEndpoint", + HeaderName: "new_headerName", + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for jwt + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateJwt, err := idpRepo.GetJWT(IAMCTX, pool, idpRepo.IDCondition(addJWT.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.idp.jwt.added + // idp + assert.Equal(t, instanceID, updateJwt.InstanceID) + assert.Equal(t, orgID, *updateJwt.OrgID) + assert.Equal(t, addJWT.Id, updateJwt.ID) + assert.Equal(t, name, updateJwt.Name) + assert.Equal(t, domain.IDPTypeJWT, domain.IDPType(*updateJwt.Type)) + assert.Equal(t, true, updateJwt.AllowLinking) + assert.Equal(t, true, updateJwt.AllowCreation) + assert.Equal(t, true, updateJwt.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*updateJwt.AutoLinkingField)) + assert.WithinRange(t, updateJwt.UpdatedAt, before, after) + + // jwt + assert.Equal(t, "new_jwtEndpoint", updateJwt.JWTEndpoint) + assert.Equal(t, "new_issuer", updateJwt.Issuer) + assert.Equal(t, "new_keyEndpoint", updateJwt.KeysEndpoint) + assert.Equal(t, "new_headerName", updateJwt.HeaderName) + }, retryDuration, tick) + }) + + t.Run("test org idp azure added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add azure + before := time.Now() + addAzure, err := MgmtClient.AddAzureADProvider(IAMCTX, &management.AddAzureADProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + Tenant: &idp_grpc.AzureADTenant{ + Type: &idp_grpc.AzureADTenant_TenantType{ + TenantType: idp.AzureADTenantType_AZURE_AD_TENANT_TYPE_ORGANISATIONS, + }, + }, + EmailVerified: true, + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for azure + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + azure, err := idpRepo.GetAzureAD(IAMCTX, pool, idpRepo.IDCondition(addAzure.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.idp.azure.added + // idp + assert.Equal(t, instanceID, azure.InstanceID) + assert.Equal(t, orgID, *azure.OrgID) + assert.Equal(t, addAzure.Id, azure.ID) + assert.Equal(t, name, azure.Name) + assert.Equal(t, domain.IDPTypeAzure, domain.IDPType(*azure.Type)) + assert.Equal(t, true, azure.AllowLinking) + assert.Equal(t, true, azure.AllowCreation) + assert.Equal(t, true, azure.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*azure.AutoLinkingField)) + assert.WithinRange(t, azure.UpdatedAt, before, after) + + // azure + assert.Equal(t, "clientId", azure.ClientID) + assert.NotNil(t, azure.ClientSecret) + assert.Equal(t, domain.AzureTenantTypeOrganizations, azure.Tenant) + assert.Equal(t, true, azure.IsEmailVerified) + assert.Equal(t, []string{"scope"}, azure.Scopes) + }, retryDuration, tick) + }) + + t.Run("test org idp azure changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add azure + addAzure, err := MgmtClient.AddAzureADProvider(IAMCTX, &management.AddAzureADProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + Tenant: &idp_grpc.AzureADTenant{ + Type: &idp_grpc.AzureADTenant_TenantType{ + TenantType: idp.AzureADTenantType_AZURE_AD_TENANT_TYPE_ORGANISATIONS, + }, + }, + EmailVerified: false, + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + var azure *domain.IDPAzureAD + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + azure, err = idpRepo.GetAzureAD(IAMCTX, pool, idpRepo.IDCondition(addAzure.Id), instanceID, &orgID) + require.NoError(t, err) + assert.Equal(t, addAzure.Id, azure.ID) + }, retryDuration, tick) + + name = "new_" + name + // change azure + before := time.Now() + _, err = MgmtClient.UpdateAzureADProvider(IAMCTX, &management.UpdateAzureADProviderRequest{ + Id: addAzure.Id, + Name: name, + ClientId: "new_clientId", + ClientSecret: "new_clientSecret", + Tenant: &idp_grpc.AzureADTenant{ + Type: &idp_grpc.AzureADTenant_TenantType{ + TenantType: idp.AzureADTenantType_AZURE_AD_TENANT_TYPE_CONSUMERS, + }, + }, + EmailVerified: true, + Scopes: []string{"new_scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + after := time.Now() + require.NoError(t, err) + + // check values for azure + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateAzure, err := idpRepo.GetAzureAD(IAMCTX, pool, idpRepo.IDCondition(addAzure.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.idp.azure.changed + // idp + assert.Equal(t, instanceID, updateAzure.InstanceID) + assert.Equal(t, orgID, *updateAzure.OrgID) + assert.Equal(t, addAzure.Id, updateAzure.ID) + assert.Equal(t, name, updateAzure.Name) + assert.Equal(t, domain.IDPTypeAzure, domain.IDPType(*updateAzure.Type)) + assert.Equal(t, true, updateAzure.AllowLinking) + assert.Equal(t, true, updateAzure.AllowCreation) + assert.Equal(t, true, updateAzure.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*updateAzure.AutoLinkingField)) + assert.WithinRange(t, updateAzure.UpdatedAt, before, after) + + // azure + assert.Equal(t, "new_clientId", updateAzure.ClientID) + assert.NotEqual(t, azure.ClientSecret, updateAzure.ClientSecret) + assert.Equal(t, domain.AzureTenantTypeConsumers, updateAzure.Tenant) + assert.Equal(t, true, updateAzure.IsEmailVerified) + assert.Equal(t, []string{"new_scope"}, updateAzure.Scopes) + }, retryDuration, tick) + }) + + t.Run("test org idp github added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add github + before := time.Now() + addGithub, err := MgmtClient.AddGitHubProvider(IAMCTX, &management.AddGitHubProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for github + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + github, err := idpRepo.GetGithub(IAMCTX, pool, idpRepo.IDCondition(addGithub.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.idp.github.added + // idp + assert.Equal(t, instanceID, github.InstanceID) + assert.Equal(t, orgID, *github.OrgID) + assert.Equal(t, addGithub.Id, github.ID) + assert.Equal(t, name, github.Name) + assert.Equal(t, domain.IDPTypeGitHub, domain.IDPType(*github.Type)) + assert.Equal(t, false, github.AllowLinking) + assert.Equal(t, false, github.AllowCreation) + assert.Equal(t, false, github.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*github.AutoLinkingField)) + assert.WithinRange(t, github.UpdatedAt, before, after) + + assert.Equal(t, "clientId", github.ClientID) + assert.NotNil(t, github.ClientSecret) + assert.Equal(t, []string{"scope"}, github.Scopes) + }, retryDuration, tick) + }) + + t.Run("test org idp github changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add github + addGithub, err := MgmtClient.AddGitHubProvider(IAMCTX, &management.AddGitHubProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + var github *domain.IDPGithub + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + github, err = idpRepo.GetGithub(IAMCTX, pool, idpRepo.IDCondition(addGithub.Id), instanceID, &orgID) + require.NoError(t, err) + assert.Equal(t, addGithub.Id, github.ID) + }, retryDuration, tick) + + name = "new_" + name + // change github + before := time.Now() + _, err = MgmtClient.UpdateGitHubProvider(IAMCTX, &management.UpdateGitHubProviderRequest{ + Id: addGithub.Id, + Name: name, + ClientId: "new_clientId", + ClientSecret: "new_clientSecret", + Scopes: []string{"new_scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + after := time.Now() + require.NoError(t, err) + + // check values for azure + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateGithub, err := idpRepo.GetGithub(IAMCTX, pool, idpRepo.IDCondition(addGithub.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.idp.github.changed + // idp + assert.Equal(t, instanceID, updateGithub.InstanceID) + assert.Equal(t, orgID, *updateGithub.OrgID) + assert.Equal(t, addGithub.Id, updateGithub.ID) + assert.Equal(t, name, updateGithub.Name) + assert.Equal(t, domain.IDPTypeGitHub, domain.IDPType(*updateGithub.Type)) + assert.Equal(t, true, updateGithub.AllowLinking) + assert.Equal(t, true, updateGithub.AllowCreation) + assert.Equal(t, true, updateGithub.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*updateGithub.AutoLinkingField)) + assert.WithinRange(t, updateGithub.UpdatedAt, before, after) + + // github + assert.Equal(t, "new_clientId", updateGithub.ClientID) + assert.NotEqual(t, github.ClientSecret, updateGithub.ClientSecret) + assert.Equal(t, []string{"new_scope"}, updateGithub.Scopes) + }, retryDuration, tick) + }) + + t.Run("test org idp github enterprise added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add github enterprise + before := time.Now() + addGithubEnterprise, err := MgmtClient.AddGitHubEnterpriseServerProvider(IAMCTX, &management.AddGitHubEnterpriseServerProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + AuthorizationEndpoint: "authoizationEndpoint", + TokenEndpoint: "tokenEndpoint", + UserEndpoint: "userEndpoint", + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for github enterprise + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + githubEnterprise, err := idpRepo.GetGithubEnterprise(IAMCTX, pool, idpRepo.IDCondition(addGithubEnterprise.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.idp.github_enterprise.added + // idp + assert.Equal(t, instanceID, githubEnterprise.InstanceID) + assert.Equal(t, orgID, *githubEnterprise.OrgID) + assert.Equal(t, addGithubEnterprise.Id, githubEnterprise.ID) + assert.Equal(t, name, githubEnterprise.Name) + assert.Equal(t, domain.IDPTypeGitHubEnterprise, domain.IDPType(*githubEnterprise.Type)) + assert.Equal(t, false, githubEnterprise.AllowLinking) + assert.Equal(t, false, githubEnterprise.AllowCreation) + assert.Equal(t, false, githubEnterprise.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*githubEnterprise.AutoLinkingField)) + assert.WithinRange(t, githubEnterprise.CreatedAt, before, after) + assert.WithinRange(t, githubEnterprise.UpdatedAt, before, after) + + // github enterprise + assert.Equal(t, "clientId", githubEnterprise.ClientID) + assert.NotNil(t, githubEnterprise.ClientSecret) + assert.Equal(t, "authoizationEndpoint", githubEnterprise.AuthorizationEndpoint) + assert.Equal(t, "tokenEndpoint", githubEnterprise.TokenEndpoint) + assert.Equal(t, "userEndpoint", githubEnterprise.UserEndpoint) + assert.Equal(t, []string{"scope"}, githubEnterprise.Scopes) + }, retryDuration, tick) + }) + + t.Run("test org idp github enterprise changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add github enterprise + addGithubEnterprise, err := MgmtClient.AddGitHubEnterpriseServerProvider(IAMCTX, &management.AddGitHubEnterpriseServerProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + AuthorizationEndpoint: "authoizationEndpoint", + TokenEndpoint: "tokenEndpoint", + UserEndpoint: "userEndpoint", + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + var githubEnterprise *domain.IDPGithubEnterprise + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + githubEnterprise, err = idpRepo.GetGithubEnterprise(IAMCTX, pool, idpRepo.IDCondition(addGithubEnterprise.Id), instanceID, &orgID) + require.NoError(t, err) + assert.Equal(t, addGithubEnterprise.Id, githubEnterprise.ID) + }, retryDuration, tick) + + name = "new_" + name + // change github enterprise + before := time.Now() + _, err = MgmtClient.UpdateGitHubEnterpriseServerProvider(IAMCTX, &management.UpdateGitHubEnterpriseServerProviderRequest{ + Id: addGithubEnterprise.Id, + Name: name, + ClientId: "new_clientId", + ClientSecret: "new_clientSecret", + AuthorizationEndpoint: "new_authoizationEndpoint", + TokenEndpoint: "new_tokenEndpoint", + UserEndpoint: "new_userEndpoint", + Scopes: []string{"new_scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + after := time.Now() + require.NoError(t, err) + + // check values for azure + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateGithubEnterprise, err := idpRepo.GetGithubEnterprise(IAMCTX, pool, idpRepo.IDCondition(addGithubEnterprise.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.idp.github_enterprise.changed + // idp + assert.Equal(t, instanceID, githubEnterprise.InstanceID) + assert.Equal(t, orgID, *githubEnterprise.OrgID) + assert.Equal(t, addGithubEnterprise.Id, updateGithubEnterprise.ID) + assert.Equal(t, name, updateGithubEnterprise.Name) + assert.Equal(t, domain.IDPTypeGitHubEnterprise, domain.IDPType(*updateGithubEnterprise.Type)) + assert.Equal(t, false, updateGithubEnterprise.AllowLinking) + assert.Equal(t, false, updateGithubEnterprise.AllowCreation) + assert.Equal(t, false, updateGithubEnterprise.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*updateGithubEnterprise.AutoLinkingField)) + assert.WithinRange(t, updateGithubEnterprise.UpdatedAt, before, after) + + // github enterprise + assert.Equal(t, "new_clientId", updateGithubEnterprise.ClientID) + assert.NotNil(t, updateGithubEnterprise.ClientSecret) + assert.Equal(t, "new_authoizationEndpoint", updateGithubEnterprise.AuthorizationEndpoint) + assert.Equal(t, "new_tokenEndpoint", updateGithubEnterprise.TokenEndpoint) + assert.Equal(t, "new_userEndpoint", updateGithubEnterprise.UserEndpoint) + assert.Equal(t, []string{"new_scope"}, updateGithubEnterprise.Scopes) + }, retryDuration, tick) + }) + + t.Run("test org idp gitlab added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add gitlab + before := time.Now() + addGithub, err := MgmtClient.AddGitLabProvider(IAMCTX, &management.AddGitLabProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for gitlab + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + gitlab, err := idpRepo.GetGitlab(IAMCTX, pool, idpRepo.IDCondition(addGithub.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.idp.gitlab.added + // idp + assert.Equal(t, instanceID, gitlab.InstanceID) + assert.Equal(t, orgID, *gitlab.OrgID) + assert.Equal(t, addGithub.Id, gitlab.ID) + assert.Equal(t, name, gitlab.Name) + assert.Equal(t, domain.IDPTypeGitLab, domain.IDPType(*gitlab.Type)) + assert.Equal(t, false, gitlab.AllowLinking) + assert.Equal(t, false, gitlab.AllowCreation) + assert.Equal(t, false, gitlab.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*gitlab.AutoLinkingField)) + assert.WithinRange(t, gitlab.CreatedAt, before, after) + assert.WithinRange(t, gitlab.UpdatedAt, before, after) + + // gitlab + assert.Equal(t, "clientId", gitlab.ClientID) + assert.NotNil(t, gitlab.ClientSecret) + assert.Equal(t, []string{"scope"}, gitlab.Scopes) + }, retryDuration, tick) + }) + + t.Run("test org idp gitlab changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add gitlab + addGitlab, err := MgmtClient.AddGitLabProvider(IAMCTX, &management.AddGitLabProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + var gitlab *domain.IDPGitlab + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + gitlab, err = idpRepo.GetGitlab(IAMCTX, pool, idpRepo.IDCondition(addGitlab.Id), instanceID, &orgID) + require.NoError(t, err) + assert.Equal(t, addGitlab.Id, gitlab.ID) + }, retryDuration, tick) + + name = "new_" + name + // change gitlab + before := time.Now() + _, err = MgmtClient.UpdateGitLabProvider(IAMCTX, &management.UpdateGitLabProviderRequest{ + Id: addGitlab.Id, + Name: name, + ClientId: "new_clientId", + ClientSecret: "new_clientSecret", + Scopes: []string{"new_scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + after := time.Now() + require.NoError(t, err) + + // check values for gitlab + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateGitlab, err := idpRepo.GetGitlab(IAMCTX, pool, idpRepo.IDCondition(addGitlab.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.idp.gitlab.changed + // idp + assert.Equal(t, instanceID, updateGitlab.InstanceID) + assert.Equal(t, orgID, *updateGitlab.OrgID) + assert.Equal(t, addGitlab.Id, updateGitlab.ID) + assert.Equal(t, name, updateGitlab.Name) + assert.Equal(t, true, updateGitlab.AllowLinking) + assert.Equal(t, true, updateGitlab.AllowCreation) + assert.Equal(t, true, updateGitlab.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*updateGitlab.AutoLinkingField)) + assert.WithinRange(t, updateGitlab.UpdatedAt, before, after) + + // gitlab + assert.Equal(t, "new_clientId", updateGitlab.ClientID) + assert.NotEqual(t, gitlab.ClientSecret, updateGitlab.ClientSecret) + assert.Equal(t, domain.IDPTypeGitLab, domain.IDPType(*updateGitlab.Type)) + assert.Equal(t, []string{"new_scope"}, updateGitlab.Scopes) + }, retryDuration, tick) + }) + + t.Run("test org idp gitlab self hosted added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add gitlab self hosted + before := time.Now() + addGitlabSelfHosted, err := MgmtClient.AddGitLabSelfHostedProvider(IAMCTX, &management.AddGitLabSelfHostedProviderRequest{ + Name: name, + Issuer: "issuer", + ClientId: "clientId", + ClientSecret: "clientSecret", + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for gitlab self hosted + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + gitlabSelfHosted, err := idpRepo.GetGitlabSelfHosting(IAMCTX, pool, idpRepo.IDCondition(addGitlabSelfHosted.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.idp.gitlab_self_hosted.added + // idp + assert.Equal(t, instanceID, gitlabSelfHosted.InstanceID) + assert.Equal(t, orgID, *gitlabSelfHosted.OrgID) + assert.Equal(t, addGitlabSelfHosted.Id, gitlabSelfHosted.ID) + assert.Equal(t, name, gitlabSelfHosted.Name) + assert.Equal(t, domain.IDPTypeGitLabSelfHosted, domain.IDPType(*gitlabSelfHosted.Type)) + assert.Equal(t, false, gitlabSelfHosted.AllowLinking) + assert.Equal(t, false, gitlabSelfHosted.AllowCreation) + assert.Equal(t, false, gitlabSelfHosted.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*gitlabSelfHosted.AutoLinkingField)) + assert.WithinRange(t, gitlabSelfHosted.CreatedAt, before, after) + assert.WithinRange(t, gitlabSelfHosted.UpdatedAt, before, after) + + // gitlab self hosted + assert.Equal(t, "clientId", gitlabSelfHosted.ClientID) + assert.Equal(t, "issuer", gitlabSelfHosted.Issuer) + assert.NotNil(t, gitlabSelfHosted.ClientSecret) + assert.Equal(t, []string{"scope"}, gitlabSelfHosted.Scopes) + }, retryDuration, tick) + }) + + t.Run("test org idp gitlab self hosted changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add gitlab self hosted + addGitlabSelfHosted, err := MgmtClient.AddGitLabSelfHostedProvider(IAMCTX, &management.AddGitLabSelfHostedProviderRequest{ + Name: name, + Issuer: "issuer", + ClientId: "clientId", + ClientSecret: "clientSecret", + 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() + + var githlabSelfHosted *domain.IDPGitlabSelfHosting + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + githlabSelfHosted, err = idpRepo.GetGitlabSelfHosting(IAMCTX, pool, idpRepo.IDCondition(addGitlabSelfHosted.Id), instanceID, &orgID) + require.NoError(t, err) + assert.Equal(t, addGitlabSelfHosted.Id, githlabSelfHosted.ID) + }, retryDuration, tick) + + name = "new_" + name + // change gitlab self hosted + before := time.Now() + _, err = MgmtClient.UpdateGitLabSelfHostedProvider(IAMCTX, &management.UpdateGitLabSelfHostedProviderRequest{ + Id: addGitlabSelfHosted.Id, + Name: name, + ClientId: "new_clientId", + Issuer: "new_issuer", + ClientSecret: "new_clientSecret", + Scopes: []string{"new_scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + after := time.Now() + require.NoError(t, err) + + // check values for gitlab self hosted + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateGitlabSelfHosted, err := idpRepo.GetGitlabSelfHosting(IAMCTX, pool, idpRepo.IDCondition(addGitlabSelfHosted.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.idp.gitlab_self_hosted.changed + // idp + assert.Equal(t, instanceID, updateGitlabSelfHosted.InstanceID) + assert.Equal(t, orgID, *updateGitlabSelfHosted.OrgID) + assert.Equal(t, addGitlabSelfHosted.Id, updateGitlabSelfHosted.ID) + assert.Equal(t, name, updateGitlabSelfHosted.Name) + assert.Equal(t, domain.IDPTypeGitLabSelfHosted, domain.IDPType(*updateGitlabSelfHosted.Type)) + assert.Equal(t, true, updateGitlabSelfHosted.AllowLinking) + assert.Equal(t, true, updateGitlabSelfHosted.AllowCreation) + assert.Equal(t, true, updateGitlabSelfHosted.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*updateGitlabSelfHosted.AutoLinkingField)) + assert.WithinRange(t, updateGitlabSelfHosted.UpdatedAt, before, after) + + // gitlab self hosted + assert.Equal(t, "new_clientId", updateGitlabSelfHosted.ClientID) + assert.Equal(t, "new_issuer", updateGitlabSelfHosted.Issuer) + assert.NotEqual(t, githlabSelfHosted.ClientSecret, updateGitlabSelfHosted.ClientSecret) + assert.Equal(t, []string{"new_scope"}, updateGitlabSelfHosted.Scopes) + }, retryDuration, tick) + }) + + t.Run("test org idp google added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add google + before := time.Now() + addGoogle, err := MgmtClient.AddGoogleProvider(IAMCTX, &management.AddGoogleProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + Scopes: []string{"scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check values for google + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + google, err := idpRepo.GetGoogle(IAMCTX, pool, idpRepo.IDCondition(addGoogle.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.idp.google.added + // idp + assert.Equal(t, instanceID, google.InstanceID) + assert.Equal(t, orgID, *google.OrgID) + assert.Equal(t, addGoogle.Id, google.ID) + assert.Equal(t, name, google.Name) + assert.Equal(t, domain.IDPTypeGoogle, domain.IDPType(*google.Type)) + assert.Equal(t, false, google.AllowLinking) + assert.Equal(t, false, google.AllowCreation) + assert.Equal(t, false, google.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*google.AutoLinkingField)) + assert.WithinRange(t, google.CreatedAt, before, after) + assert.WithinRange(t, google.UpdatedAt, before, after) + + // google + assert.Equal(t, "clientId", google.ClientID) + assert.NotNil(t, google.ClientSecret) + assert.Equal(t, []string{"scope"}, google.Scopes) + }, retryDuration, tick) + }) + + t.Run("test org idp google changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add google + addGoogle, err := MgmtClient.AddGoogleProvider(IAMCTX, &management.AddGoogleProviderRequest{ + Name: name, + ClientId: "clientId", + ClientSecret: "clientSecret", + 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() + + var google *domain.IDPGoogle + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + google, err = idpRepo.GetGoogle(IAMCTX, pool, idpRepo.IDCondition(addGoogle.Id), instanceID, &orgID) + require.NoError(t, err) + assert.Equal(t, addGoogle.Id, google.ID) + }, retryDuration, tick) + + name = "new_" + name + // change google + before := time.Now() + _, err = MgmtClient.UpdateGoogleProvider(IAMCTX, &management.UpdateGoogleProviderRequest{ + Id: addGoogle.Id, + Name: name, + ClientId: "new_clientId", + ClientSecret: "new_clientSecret", + Scopes: []string{"new_scope"}, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + after := time.Now() + require.NoError(t, err) + + // check values for google + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateGoogle, err := idpRepo.GetGoogle(IAMCTX, pool, idpRepo.IDCondition(addGoogle.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.idp.google.changed + // idp + assert.Equal(t, instanceID, updateGoogle.InstanceID) + assert.Equal(t, orgID, *updateGoogle.OrgID) + assert.Equal(t, addGoogle.Id, updateGoogle.ID) + assert.Equal(t, name, updateGoogle.Name) + assert.Equal(t, domain.IDPTypeGoogle, domain.IDPType(*updateGoogle.Type)) + assert.Equal(t, true, updateGoogle.AllowLinking) + assert.Equal(t, true, updateGoogle.AllowCreation) + assert.Equal(t, true, updateGoogle.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*updateGoogle.AutoLinkingField)) + assert.WithinRange(t, updateGoogle.UpdatedAt, before, after) + + // google + assert.Equal(t, "new_clientId", updateGoogle.ClientID) + assert.NotEqual(t, google.ClientSecret, updateGoogle.ClientSecret) + assert.Equal(t, []string{"new_scope"}, updateGoogle.Scopes) + }, retryDuration, tick) + }) + + t.Run("test org ldap added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add ldap + before := time.Now() + addLdap, err := MgmtClient.AddLDAPProvider(IAMCTX, &management.AddLDAPProviderRequest{ + Name: name, + Servers: []string{"servers"}, + StartTls: true, + BaseDn: "baseDN", + BindDn: "bindND", + BindPassword: "bindPassword", + UserBase: "userBase", + UserObjectClasses: []string{"userOhjectClasses"}, + UserFilters: []string{"userFilters"}, + Timeout: durationpb.New(time.Minute), + Attributes: &idp_grpc.LDAPAttributes{ + IdAttribute: "idAttribute", + FirstNameAttribute: "firstNameAttribute", + LastNameAttribute: "lastNameAttribute", + DisplayNameAttribute: "displayNameAttribute", + NickNameAttribute: "nickNameAttribute", + PreferredUsernameAttribute: "preferredUsernameAttribute", + EmailAttribute: "emailAttribute", + EmailVerifiedAttribute: "emailVerifiedAttribute", + PhoneAttribute: "phoneAttribute", + PhoneVerifiedAttribute: "phoneVerifiedAttribute", + PreferredLanguageAttribute: "preferredLanguageAttribute", + AvatarUrlAttribute: "avatarUrlAttribute", + ProfileAttribute: "profileAttribute", + }, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: false, + IsCreationAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + ldap, err := idpRepo.GetLDAP(IAMCTX, pool, idpRepo.IDCondition(addLdap.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.idp.ldap.v2.added + // idp + assert.Equal(t, instanceID, ldap.InstanceID) + assert.Equal(t, orgID, *ldap.OrgID) + assert.Equal(t, addLdap.Id, ldap.ID) + assert.Equal(t, name, ldap.Name) + assert.Equal(t, domain.IDPTypeLDAP, domain.IDPType(*ldap.Type)) + assert.Equal(t, false, ldap.AllowLinking) + assert.Equal(t, false, ldap.AllowCreation) + assert.Equal(t, false, ldap.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*ldap.AutoLinkingField)) + assert.WithinRange(t, ldap.CreatedAt, before, after) + assert.WithinRange(t, ldap.UpdatedAt, before, after) + + // ldap + assert.Equal(t, []string{"servers"}, ldap.Servers) + assert.Equal(t, true, ldap.StartTLS) + assert.Equal(t, "baseDN", ldap.BaseDN) + assert.Equal(t, "bindND", ldap.BindDN) + assert.NotNil(t, ldap.BindPassword) + assert.Equal(t, "userBase", ldap.UserBase) + assert.Equal(t, []string{"userOhjectClasses"}, ldap.UserObjectClasses) + assert.Equal(t, []string{"userFilters"}, ldap.UserFilters) + assert.Equal(t, time.Minute, ldap.Timeout) + assert.Equal(t, "idAttribute", ldap.IDAttribute) + assert.Equal(t, "firstNameAttribute", ldap.FirstNameAttribute) + assert.Equal(t, "lastNameAttribute", ldap.LastNameAttribute) + assert.Equal(t, "displayNameAttribute", ldap.DisplayNameAttribute) + assert.Equal(t, "nickNameAttribute", ldap.NickNameAttribute) + assert.Equal(t, "preferredUsernameAttribute", ldap.PreferredUsernameAttribute) + assert.Equal(t, "emailAttribute", ldap.EmailAttribute) + assert.Equal(t, "emailVerifiedAttribute", ldap.EmailVerifiedAttribute) + assert.Equal(t, "phoneAttribute", ldap.PhoneAttribute) + assert.Equal(t, "phoneVerifiedAttribute", ldap.PhoneVerifiedAttribute) + assert.Equal(t, "preferredLanguageAttribute", ldap.PreferredLanguageAttribute) + assert.Equal(t, "avatarUrlAttribute", ldap.AvatarURLAttribute) + assert.Equal(t, "profileAttribute", ldap.ProfileAttribute) + }, retryDuration, tick) + }) + + t.Run("test org ldap changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add ldap + addLdap, err := MgmtClient.AddLDAPProvider(IAMCTX, &management.AddLDAPProviderRequest{ + Name: name, + Servers: []string{"servers"}, + StartTls: true, + BaseDn: "baseDN", + BindDn: "bindND", + BindPassword: "bindPassword", + UserBase: "userBase", + UserObjectClasses: []string{"userOhjectClasses"}, + UserFilters: []string{"userFilters"}, + Timeout: durationpb.New(time.Minute), + Attributes: &idp_grpc.LDAPAttributes{ + IdAttribute: "idAttribute", + FirstNameAttribute: "firstNameAttribute", + LastNameAttribute: "lastNameAttribute", + DisplayNameAttribute: "displayNameAttribute", + NickNameAttribute: "nickNameAttribute", + PreferredUsernameAttribute: "preferredUsernameAttribute", + EmailAttribute: "emailAttribute", + EmailVerifiedAttribute: "emailVerifiedAttribute", + PhoneAttribute: "phoneAttribute", + PhoneVerifiedAttribute: "phoneVerifiedAttribute", + PreferredLanguageAttribute: "preferredLanguageAttribute", + AvatarUrlAttribute: "avatarUrlAttribute", + ProfileAttribute: "profileAttribute", + }, + 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() + + var ldap *domain.IDPLDAP + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + ldap, err = idpRepo.GetLDAP(IAMCTX, pool, idpRepo.IDCondition(addLdap.Id), instanceID, &orgID) + require.NoError(t, err) + assert.Equal(t, addLdap.Id, ldap.ID) + }, retryDuration, tick) + + name = "new_" + name + // change ldap + before := time.Now() + _, err = MgmtClient.UpdateLDAPProvider(IAMCTX, &management.UpdateLDAPProviderRequest{ + Id: addLdap.Id, + Name: name, + Servers: []string{"new_servers"}, + StartTls: false, + BaseDn: "new_baseDN", + BindDn: "new_bindND", + BindPassword: "new_bindPassword", + UserBase: "new_userBase", + UserObjectClasses: []string{"new_userOhjectClasses"}, + UserFilters: []string{"new_userFilters"}, + Timeout: durationpb.New(time.Second), + Attributes: &idp_grpc.LDAPAttributes{ + IdAttribute: "new_idAttribute", + FirstNameAttribute: "new_firstNameAttribute", + LastNameAttribute: "new_lastNameAttribute", + DisplayNameAttribute: "new_displayNameAttribute", + NickNameAttribute: "new_nickNameAttribute", + PreferredUsernameAttribute: "new_preferredUsernameAttribute", + EmailAttribute: "new_emailAttribute", + EmailVerifiedAttribute: "new_emailVerifiedAttribute", + PhoneAttribute: "new_phoneAttribute", + PhoneVerifiedAttribute: "new_phoneVerifiedAttribute", + PreferredLanguageAttribute: "new_preferredLanguageAttribute", + AvatarUrlAttribute: "new_avatarUrlAttribute", + ProfileAttribute: "new_profileAttribute", + }, + ProviderOptions: &idp_grpc.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, + }) + after := time.Now() + require.NoError(t, err) + + // check values for ldap + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateLdap, err := idpRepo.GetLDAP(IAMCTX, pool, idpRepo.IDCondition(addLdap.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.idp.ldap.v2.changed + // idp + assert.Equal(t, instanceID, updateLdap.InstanceID) + assert.Equal(t, orgID, *updateLdap.OrgID) + assert.Equal(t, addLdap.Id, updateLdap.ID) + assert.Equal(t, name, updateLdap.Name) + assert.Equal(t, domain.IDPTypeLDAP, domain.IDPType(*updateLdap.Type)) + assert.Equal(t, true, updateLdap.AllowLinking) + assert.Equal(t, true, updateLdap.AllowCreation) + assert.Equal(t, true, updateLdap.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*updateLdap.AutoLinkingField)) + assert.WithinRange(t, updateLdap.UpdatedAt, before, after) + + // ldap + assert.Equal(t, []string{"new_servers"}, updateLdap.Servers) + assert.Equal(t, false, updateLdap.StartTLS) + assert.Equal(t, "new_baseDN", updateLdap.BaseDN) + assert.Equal(t, "new_bindND", updateLdap.BindDN) + assert.NotEqual(t, ldap.BindPassword, updateLdap.BindPassword) + assert.Equal(t, "new_userBase", updateLdap.UserBase) + assert.Equal(t, []string{"new_userOhjectClasses"}, updateLdap.UserObjectClasses) + assert.Equal(t, []string{"new_userFilters"}, updateLdap.UserFilters) + assert.Equal(t, time.Second, updateLdap.Timeout) + assert.Equal(t, "new_idAttribute", updateLdap.IDAttribute) + assert.Equal(t, "new_firstNameAttribute", updateLdap.FirstNameAttribute) + assert.Equal(t, "new_lastNameAttribute", updateLdap.LastNameAttribute) + assert.Equal(t, "new_displayNameAttribute", updateLdap.DisplayNameAttribute) + assert.Equal(t, "new_nickNameAttribute", updateLdap.NickNameAttribute) + assert.Equal(t, "new_preferredUsernameAttribute", updateLdap.PreferredUsernameAttribute) + assert.Equal(t, "new_emailAttribute", updateLdap.EmailAttribute) + assert.Equal(t, "new_emailVerifiedAttribute", updateLdap.EmailVerifiedAttribute) + assert.Equal(t, "new_phoneAttribute", updateLdap.PhoneAttribute) + assert.Equal(t, "new_phoneVerifiedAttribute", updateLdap.PhoneVerifiedAttribute) + assert.Equal(t, "new_preferredLanguageAttribute", updateLdap.PreferredLanguageAttribute) + assert.Equal(t, "new_avatarUrlAttribute", updateLdap.AvatarURLAttribute) + assert.Equal(t, "new_profileAttribute", updateLdap.ProfileAttribute) + }, retryDuration, tick) + }) + + t.Run("test org apple added reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add apple + before := time.Now() + addApple, err := MgmtClient.AddAppleProvider(IAMCTX, &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, + }, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + apple, err := idpRepo.GetApple(IAMCTX, pool, idpRepo.IDCondition(addApple.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.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, domain.IDPType(*apple.Type)) + assert.Equal(t, false, apple.AllowLinking) + assert.Equal(t, false, apple.AllowCreation) + assert.Equal(t, false, apple.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*apple.AutoLinkingField)) + assert.WithinRange(t, apple.CreatedAt, before, after) + assert.WithinRange(t, apple.UpdatedAt, before, after) + + // 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 org apple changed reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add apple + addApple, err := MgmtClient.AddAppleProvider(IAMCTX, &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() + + var apple *domain.IDPApple + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + apple, err = idpRepo.GetApple(IAMCTX, pool, idpRepo.IDCondition(addApple.Id), instanceID, &orgID) + require.NoError(t, err) + assert.Equal(t, addApple.Id, apple.ID) + }, retryDuration, tick) + + name = "new_" + name + // change apple + before := time.Now() + _, err = MgmtClient.UpdateAppleProvider(IAMCTX, &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, + }, + }) + after := time.Now() + require.NoError(t, err) + + // check values for apple + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateApple, err := idpRepo.GetApple(IAMCTX, pool, 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, domain.IDPType(*updateApple.Type)) + assert.Equal(t, true, updateApple.AllowLinking) + assert.Equal(t, true, updateApple.AllowCreation) + assert.Equal(t, true, updateApple.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*updateApple.AutoLinkingField)) + assert.WithinRange(t, updateApple.UpdatedAt, before, after) + + // 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 org saml added reduces", func(t *testing.T) { + name := gofakeit.Name() + federatedLogoutEnabled := false + + // add saml + before := time.Now() + addSAML, err := MgmtClient.AddSAMLProvider(IAMCTX, &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, + }, + SignatureAlgorithm: idp.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA1, + }) + after := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + saml, err := idpRepo.GetSAML(IAMCTX, pool, idpRepo.IDCondition(addSAML.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.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, domain.IDPType(*saml.Type)) + assert.Equal(t, false, saml.AllowLinking) + assert.Equal(t, false, saml.AllowCreation) + assert.Equal(t, false, saml.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldEmail, domain.IDPAutoLinkingField(*saml.AutoLinkingField)) + assert.WithinRange(t, saml.CreatedAt, before, after) + assert.WithinRange(t, saml.UpdatedAt, before, after) + + // 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) + assert.Equal(t, "http://www.w3.org/2000/09/xmldsig#rsa-sha1", saml.SignatureAlgorithm) + }, retryDuration, tick) + }) + + t.Run("test org saml changed reduces", func(t *testing.T) { + name := gofakeit.Name() + federatedLogoutEnabled := false + + // add saml + addSAML, err := MgmtClient.AddSAMLProvider(IAMCTX, &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, + }, + SignatureAlgorithm: idp.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA1, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + var saml *domain.IDPSAML + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + saml, err = idpRepo.GetSAML(IAMCTX, pool, 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 + before := time.Now() + _, err = MgmtClient.UpdateSAMLProvider(IAMCTX, &management.UpdateSAMLProviderRequest{ + Id: addSAML.Id, + Name: name, + 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, + }, + SignatureAlgorithm: idp.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA256, + }) + after := time.Now() + require.NoError(t, err) + + // check values for apple + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateSAML, err := idpRepo.GetSAML(IAMCTX, pool, idpRepo.IDCondition(addSAML.Id), instanceID, &orgID) + require.NoError(t, err) + + // event org.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, domain.IDPType(*updateSAML.Type)) + assert.Equal(t, true, updateSAML.AllowLinking) + assert.Equal(t, true, updateSAML.AllowCreation) + assert.Equal(t, true, updateSAML.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingFieldUserName, domain.IDPAutoLinkingField(*updateSAML.AutoLinkingField)) + assert.WithinRange(t, updateSAML.UpdatedAt, before, after) + + // saml + assert.Equal(t, validSAMLMetadata2, updateSAML.Metadata) + assert.NotNil(t, updateSAML.Key) + assert.NotNil(t, updateSAML.Certificate) + 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) + assert.Equal(t, "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", updateSAML.SignatureAlgorithm) + }, retryDuration, tick) + }) + + t.Run("test org iam remove reduces", func(t *testing.T) { + name := gofakeit.Name() + + // add idp + addOIDC, err := MgmtClient.AddOrgOIDCIDP(IAMCTX, &management.AddOrgOIDCIDPRequest{ + Name: name, + StylingType: idp_grpc.IDPStylingType_STYLING_TYPE_GOOGLE, + ClientId: "clientID", + ClientSecret: "clientSecret", + Issuer: "issuer", + Scopes: []string{"scope"}, + DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL, + AutoRegister: true, + }) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + // check idp exists + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + _, err := idpRepo.Get(IAMCTX, pool, + idpRepo.IDCondition(addOIDC.IdpId), + instanceID, + &orgID, + ) + require.NoError(t, err) + }, retryDuration, tick) + + // remove idp + _, err = MgmtClient.DeleteProvider(IAMCTX, &management.DeleteProviderRequest{ + Id: addOIDC.IdpId, + }) + require.NoError(t, err) + + // check idp is removed + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Minute) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + _, err := idpRepo.Get(IAMCTX, pool, + idpRepo.IDCondition(addOIDC.IdpId), + instanceID, + &orgID, + ) + require.ErrorIs(t, &database.NoRowFoundError{}, err) + }, retryDuration, tick) + }) +} diff --git a/backend/v3/storage/database/repository/id_provider.go b/backend/v3/storage/database/repository/id_provider.go new file mode 100644 index 00000000000..9f92de35ec5 --- /dev/null +++ b/backend/v3/storage/database/repository/id_provider.go @@ -0,0 +1,638 @@ +package repository + +import ( + "context" + "encoding/json" + "time" + + "github.com/zitadel/zitadel/backend/v3/domain" + "github.com/zitadel/zitadel/backend/v3/storage/database" +) + +var _ domain.IDProviderRepository = (*idProvider)(nil) + +type idProvider struct{} + +func IDProviderRepository() domain.IDProviderRepository { + return new(idProvider) +} + +const queryIDProviderStmt = `SELECT instance_id, org_id, id, state, name, type, auto_register, allow_creation, allow_auto_creation,` + + ` allow_auto_update, allow_linking, auto_linking_field, styling_type, payload, created_at, updated_at` + + ` FROM zitadel.identity_providers` + +func (i *idProvider) Get(ctx context.Context, client database.QueryExecutor, id domain.IDPIdentifierCondition, instanceID string, orgID *string) (*domain.IdentityProvider, error) { + builder := database.StatementBuilder{} + + builder.WriteString(queryIDProviderStmt) + + conditions := []database.Condition{id, i.InstanceIDCondition(instanceID), i.OrgIDCondition(orgID)} + + writeCondition(&builder, database.And(conditions...)) + + return scanIDProvider(ctx, client, &builder) +} + +func (i *idProvider) List(ctx context.Context, client database.QueryExecutor, conditions ...database.Condition) ([]*domain.IdentityProvider, error) { + builder := database.StatementBuilder{} + + builder.WriteString(queryIDProviderStmt) + + if conditions != nil { + writeCondition(&builder, database.And(conditions...)) + } + + orderBy := database.OrderBy(i.CreatedAtColumn()) + orderBy.Write(&builder) + + return scanIDProviders(ctx, client, &builder) +} + +const createIDProviderStmtStart = `INSERT INTO zitadel.identity_providers` + + ` (instance_id, org_id, id, state, name, type, allow_creation, allow_auto_creation,` + + ` allow_auto_update, allow_linking, styling_type, payload) VALUES (` + +const createIDProviderStmtEnd = `) RETURNING created_at, updated_at` + +func (i *idProvider) Create(ctx context.Context, client database.QueryExecutor, idp *domain.IdentityProvider) error { + builder := database.StatementBuilder{} + + builder.WriteString(createIDProviderStmtStart) + + builder.WriteArgs( + idp.InstanceID, + idp.OrgID, + idp.ID, + idp.State, + idp.Name, + idp.Type, + idp.AllowCreation, + idp.AllowAutoCreation, + idp.AllowAutoUpdate, + idp.AllowLinking, + idp.StylingType, + string(idp.Payload)) + + builder.WriteString(createIDProviderStmtEnd) + + err := client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(&idp.CreatedAt, &idp.UpdatedAt) + return err +} + +func (i *idProvider) Update(ctx context.Context, client database.QueryExecutor, id domain.IDPIdentifierCondition, instanceID string, orgID *string, changes ...database.Change) (int64, error) { + if changes == nil { + return 0, database.ErrNoChanges + } + changes = append(changes, i.SetUpdatedAt(nil)) + builder := database.StatementBuilder{} + builder.WriteString(`UPDATE zitadel.identity_providers SET `) + + conditions := []database.Condition{ + id, + i.InstanceIDCondition(instanceID), + i.OrgIDCondition(orgID), + } + database.Changes(changes).Write(&builder) + writeCondition(&builder, database.And(conditions...)) + + stmt := builder.String() + + return client.Exec(ctx, stmt, builder.Args()...) +} + +func (i *idProvider) Delete(ctx context.Context, client database.QueryExecutor, id domain.IDPIdentifierCondition, instanceID string, orgID *string) (int64, error) { + builder := database.StatementBuilder{} + + builder.WriteString(`DELETE FROM zitadel.identity_providers`) + + conditions := []database.Condition{ + id, + i.InstanceIDCondition(instanceID), + i.OrgIDCondition(orgID), + } + writeCondition(&builder, database.And(conditions...)) + + return client.Exec(ctx, builder.String(), builder.Args()...) +} + +func (i *idProvider) GetOIDC(ctx context.Context, client database.QueryExecutor, id domain.IDPIdentifierCondition, instanceID string, orgID *string) (*domain.IDPOIDC, error) { + idpOIDC := &domain.IDPOIDC{} + var err error + + idpOIDC.IdentityProvider, err = i.Get(ctx, client, id, instanceID, orgID) + if err != nil { + return nil, err + } + + var idpType domain.IDPType + if idpOIDC.Type != nil { + idpType = *idpOIDC.Type + } + + if idpType != domain.IDPTypeOIDC { + return nil, domain.NewIDPWrongTypeError(domain.IDPTypeOIDC, idpType) + } + + err = json.Unmarshal(idpOIDC.Payload, idpOIDC) + if err != nil { + return nil, err + } + + return idpOIDC, nil +} + +func (i *idProvider) GetJWT(ctx context.Context, client database.QueryExecutor, id domain.IDPIdentifierCondition, instanceID string, orgID *string) (*domain.IDPJWT, error) { + idpJWT := &domain.IDPJWT{} + var err error + + idpJWT.IdentityProvider, err = i.Get(ctx, client, id, instanceID, orgID) + if err != nil { + return nil, err + } + + var idpType domain.IDPType + if idpJWT.Type != nil { + idpType = *idpJWT.Type + } + + if idpType != domain.IDPTypeJWT { + return nil, domain.NewIDPWrongTypeError(domain.IDPTypeJWT, idpType) + } + + err = json.Unmarshal(idpJWT.Payload, idpJWT) + if err != nil { + return nil, err + } + + return idpJWT, nil +} + +func (i *idProvider) GetOAuth(ctx context.Context, client database.QueryExecutor, id domain.IDPIdentifierCondition, instanceID string, orgID *string) (*domain.IDPOAuth, error) { + idpOAuth := &domain.IDPOAuth{} + var err error + + idpOAuth.IdentityProvider, err = i.Get(ctx, client, id, instanceID, orgID) + if err != nil { + return nil, err + } + + var idpType domain.IDPType + if idpOAuth.Type != nil { + idpType = *idpOAuth.Type + } + + if idpType != domain.IDPTypeOAuth { + return nil, domain.NewIDPWrongTypeError(domain.IDPTypeOAuth, idpType) + } + + err = json.Unmarshal(idpOAuth.Payload, idpOAuth) + if err != nil { + return nil, err + } + + return idpOAuth, nil +} + +func (i *idProvider) GetAzureAD(ctx context.Context, client database.QueryExecutor, id domain.IDPIdentifierCondition, instanceID string, orgID *string) (*domain.IDPAzureAD, error) { + idpAzure := &domain.IDPAzureAD{} + var err error + + idpAzure.IdentityProvider, err = i.Get(ctx, client, id, instanceID, orgID) + if err != nil { + return nil, err + } + + var idpType domain.IDPType + if idpAzure.Type != nil { + idpType = *idpAzure.Type + } + + if idpType != domain.IDPTypeAzure { + return nil, domain.NewIDPWrongTypeError(domain.IDPTypeAzure, idpType) + } + + err = json.Unmarshal(idpAzure.Payload, idpAzure) + if err != nil { + return nil, err + } + + return idpAzure, nil +} + +func (i *idProvider) GetGoogle(ctx context.Context, client database.QueryExecutor, id domain.IDPIdentifierCondition, instanceID string, orgID *string) (*domain.IDPGoogle, error) { + idpGoogle := &domain.IDPGoogle{} + var err error + + idpGoogle.IdentityProvider, err = i.Get(ctx, client, id, instanceID, orgID) + if err != nil { + return nil, err + } + + var idpType domain.IDPType + if idpGoogle.Type != nil { + idpType = *idpGoogle.Type + } + + if idpType != domain.IDPTypeGoogle { + return nil, domain.NewIDPWrongTypeError(domain.IDPTypeGoogle, idpType) + } + + err = json.Unmarshal(idpGoogle.Payload, idpGoogle) + if err != nil { + return nil, err + } + + return idpGoogle, nil +} + +func (i *idProvider) GetGithub(ctx context.Context, client database.QueryExecutor, id domain.IDPIdentifierCondition, instanceID string, orgID *string) (*domain.IDPGithub, error) { + idpGithub := &domain.IDPGithub{} + var err error + + idpGithub.IdentityProvider, err = i.Get(ctx, client, id, instanceID, orgID) + if err != nil { + return nil, err + } + + var idpType domain.IDPType + if idpGithub.Type != nil { + idpType = *idpGithub.Type + } + + if idpType != domain.IDPTypeGitHub { + return nil, domain.NewIDPWrongTypeError(domain.IDPTypeGitHub, idpType) + } + + err = json.Unmarshal(idpGithub.Payload, idpGithub) + if err != nil { + return nil, err + } + + return idpGithub, nil +} + +func (i *idProvider) GetGithubEnterprise(ctx context.Context, client database.QueryExecutor, id domain.IDPIdentifierCondition, instanceID string, orgID *string) (*domain.IDPGithubEnterprise, error) { + idpGithubEnterprise := &domain.IDPGithubEnterprise{} + var err error + + idpGithubEnterprise.IdentityProvider, err = i.Get(ctx, client, id, instanceID, orgID) + if err != nil { + return nil, err + } + + var idpType domain.IDPType + if idpGithubEnterprise.Type != nil { + idpType = *idpGithubEnterprise.Type + } + + if idpType != domain.IDPTypeGitHubEnterprise { + return nil, domain.NewIDPWrongTypeError(domain.IDPTypeGitHubEnterprise, idpType) + } + + err = json.Unmarshal(idpGithubEnterprise.Payload, idpGithubEnterprise) + if err != nil { + return nil, err + } + + return idpGithubEnterprise, nil +} + +func (i *idProvider) GetGitlab(ctx context.Context, client database.QueryExecutor, id domain.IDPIdentifierCondition, instanceID string, orgID *string) (*domain.IDPGitlab, error) { + idpGitlab := &domain.IDPGitlab{} + var err error + + idpGitlab.IdentityProvider, err = i.Get(ctx, client, id, instanceID, orgID) + if err != nil { + return nil, err + } + + var idpType domain.IDPType + if idpGitlab.Type != nil { + idpType = *idpGitlab.Type + } + + if idpType != domain.IDPTypeGitLab { + return nil, domain.NewIDPWrongTypeError(domain.IDPTypeGitLab, idpType) + } + + err = json.Unmarshal(idpGitlab.Payload, idpGitlab) + if err != nil { + return nil, err + } + + return idpGitlab, nil +} + +func (i *idProvider) GetGitlabSelfHosting(ctx context.Context, client database.QueryExecutor, id domain.IDPIdentifierCondition, instanceID string, orgID *string) (*domain.IDPGitlabSelfHosting, error) { + idpGitlabSelfHosting := &domain.IDPGitlabSelfHosting{} + var err error + + idpGitlabSelfHosting.IdentityProvider, err = i.Get(ctx, client, id, instanceID, orgID) + if err != nil { + return nil, err + } + + var idpType domain.IDPType + if idpGitlabSelfHosting.Type != nil { + idpType = *idpGitlabSelfHosting.Type + } + + if idpType != domain.IDPTypeGitLabSelfHosted { + return nil, domain.NewIDPWrongTypeError(domain.IDPTypeGitLabSelfHosted, idpType) + } + + err = json.Unmarshal(idpGitlabSelfHosting.Payload, idpGitlabSelfHosting) + if err != nil { + return nil, err + } + + return idpGitlabSelfHosting, nil +} + +func (i *idProvider) GetLDAP(ctx context.Context, client database.QueryExecutor, id domain.IDPIdentifierCondition, instanceID string, orgID *string) (*domain.IDPLDAP, error) { + ldap := &domain.IDPLDAP{} + var err error + + ldap.IdentityProvider, err = i.Get(ctx, client, id, instanceID, orgID) + if err != nil { + return nil, err + } + + var idpType domain.IDPType + if ldap.Type != nil { + idpType = *ldap.Type + } + + if idpType != domain.IDPTypeLDAP { + return nil, domain.NewIDPWrongTypeError(domain.IDPTypeLDAP, idpType) + } + + err = json.Unmarshal(ldap.Payload, ldap) + if err != nil { + return nil, err + } + + return ldap, nil +} + +func (i *idProvider) GetApple(ctx context.Context, client database.QueryExecutor, id domain.IDPIdentifierCondition, instanceID string, orgID *string) (*domain.IDPApple, error) { + apple := &domain.IDPApple{} + var err error + + apple.IdentityProvider, err = i.Get(ctx, client, id, instanceID, orgID) + if err != nil { + return nil, err + } + + var idpType domain.IDPType + if apple.Type != nil { + idpType = *apple.Type + } + + if idpType != domain.IDPTypeApple { + return nil, domain.NewIDPWrongTypeError(domain.IDPTypeApple, idpType) + } + + err = json.Unmarshal(apple.Payload, apple) + if err != nil { + return nil, err + } + + return apple, nil +} + +func (i *idProvider) GetSAML(ctx context.Context, client database.QueryExecutor, id domain.IDPIdentifierCondition, instanceID string, orgID *string) (*domain.IDPSAML, error) { + saml := &domain.IDPSAML{} + var err error + + saml.IdentityProvider, err = i.Get(ctx, client, id, instanceID, orgID) + if err != nil { + return nil, err + } + + var idpType domain.IDPType + if saml.Type != nil { + idpType = *saml.Type + } + + if idpType != domain.IDPTypeSAML { + return nil, domain.NewIDPWrongTypeError(domain.IDPTypeSAML, idpType) + } + + err = json.Unmarshal(saml.Payload, saml) + if err != nil { + return nil, err + } + + return saml, nil +} + +// ------------------------------------------------------------- +// columns +// ------------------------------------------------------------- + +func (idProvider) InstanceIDColumn() database.Column { + return database.NewColumn("identity_providers", "instance_id") +} + +func (idProvider) OrgIDColumn() database.Column { + return database.NewColumn("identity_providers", "org_id") +} + +func (idProvider) IDColumn() database.Column { + return database.NewColumn("identity_providers", "id") +} + +func (idProvider) StateColumn() database.Column { + return database.NewColumn("identity_providers", "state") +} + +func (idProvider) NameColumn() database.Column { + return database.NewColumn("identity_providers", "name") +} + +func (idProvider) TypeColumn() database.Column { + return database.NewColumn("identity_providers", "type") +} + +func (idProvider) AutoRegisterColumn() database.Column { + return database.NewColumn("identity_providers", "auto_register") +} + +func (idProvider) AllowCreationColumn() database.Column { + return database.NewColumn("identity_providers", "allow_creation") +} + +func (idProvider) AllowAutoCreationColumn() database.Column { + return database.NewColumn("identity_providers", "allow_auto_creation") +} + +func (idProvider) AllowAutoUpdateColumn() database.Column { + return database.NewColumn("identity_providers", "allow_auto_update") +} + +func (idProvider) AllowLinkingColumn() database.Column { + return database.NewColumn("identity_providers", "allow_linking") +} + +func (idProvider) AllowAutoLinkingColumn() database.Column { + return database.NewColumn("identity_providers", "auto_linking_field") +} + +func (idProvider) StylingTypeColumn() database.Column { + return database.NewColumn("identity_providers", "styling_type") +} + +func (idProvider) PayloadColumn() database.Column { + return database.NewColumn("identity_providers", "payload") +} + +func (idProvider) CreatedAtColumn() database.Column { + return database.NewColumn("identity_providers", "created_at") +} + +func (idProvider) UpdatedAtColumn() database.Column { + return database.NewColumn("identity_providers", "updated_at") +} + +// ------------------------------------------------------------- +// conditions +// ------------------------------------------------------------- + +func (i idProvider) InstanceIDCondition(id string) database.Condition { + return database.NewTextCondition(i.InstanceIDColumn(), database.TextOperationEqual, id) +} + +func (i idProvider) OrgIDCondition(id *string) database.Condition { + if id == nil { + return database.IsNull(i.OrgIDColumn()) + } + return database.NewTextCondition(i.OrgIDColumn(), database.TextOperationEqual, *id) +} + +func (i idProvider) IDCondition(id string) domain.IDPIdentifierCondition { + return database.NewTextCondition(i.IDColumn(), database.TextOperationEqual, id) +} + +func (i idProvider) StateCondition(state domain.IDPState) database.Condition { + return database.NewTextCondition(i.StateColumn(), database.TextOperationEqual, state.String()) +} + +func (i idProvider) NameCondition(name string) domain.IDPIdentifierCondition { + return database.NewTextCondition(i.NameColumn(), database.TextOperationEqual, name) +} + +func (i idProvider) TypeCondition(typ domain.IDPType) database.Condition { + return database.NewNumberCondition(i.TypeColumn(), database.NumberOperationEqual, typ) +} + +func (i idProvider) AutoRegisterCondition(allow bool) database.Condition { + return database.NewBooleanCondition(i.AutoRegisterColumn(), allow) +} + +func (i idProvider) AllowCreationCondition(allow bool) database.Condition { + return database.NewBooleanCondition(i.AllowCreationColumn(), allow) +} + +func (i idProvider) AllowAutoCreationCondition(allow bool) database.Condition { + return database.NewBooleanCondition(i.AllowAutoCreationColumn(), allow) +} + +func (i idProvider) AllowAutoUpdateCondition(allow bool) database.Condition { + return database.NewBooleanCondition(i.AllowAutoUpdateColumn(), allow) +} + +func (i idProvider) AllowLinkingCondition(allow bool) database.Condition { + return database.NewBooleanCondition(i.AllowLinkingColumn(), allow) +} + +func (i idProvider) AllowAutoLinkingCondition(linkingType domain.IDPAutoLinkingField) database.Condition { + return database.NewTextCondition(i.AllowAutoLinkingColumn(), database.TextOperationEqual, linkingType.String()) +} + +func (i idProvider) StylingTypeCondition(style int16) database.Condition { + return database.NewNumberCondition(i.StylingTypeColumn(), database.NumberOperationEqual, style) +} + +func (i idProvider) PayloadCondition(payload string) database.Condition { + return database.NewTextCondition(i.PayloadColumn(), database.TextOperationEqual, payload) +} + +// ------------------------------------------------------------- +// changes +// ------------------------------------------------------------- + +func (i idProvider) SetName(name string) database.Change { + return database.NewChange(i.NameColumn(), name) +} + +func (i idProvider) SetState(state domain.IDPState) database.Change { + return database.NewChange(i.StateColumn(), state) +} + +func (i idProvider) SetAllowCreation(allow bool) database.Change { + return database.NewChange(i.AllowCreationColumn(), allow) +} + +func (i idProvider) SetAutoRegister(allow bool) database.Change { + return database.NewChange(i.AutoRegisterColumn(), allow) +} + +func (i idProvider) SetAllowAutoCreation(allow bool) database.Change { + return database.NewChange(i.AllowAutoCreationColumn(), allow) +} + +func (i idProvider) SetAllowAutoUpdate(allow bool) database.Change { + return database.NewChange(i.AllowAutoUpdateColumn(), allow) +} + +func (i idProvider) SetAllowLinking(allow bool) database.Change { + return database.NewChange(i.AllowLinkingColumn(), allow) +} + +func (i idProvider) SetAutoAllowLinking(allow bool) database.Change { + return database.NewChange(i.AllowAutoLinkingColumn(), allow) +} + +func (i idProvider) SetStylingType(stylingType int16) database.Change { + return database.NewChange(i.StylingTypeColumn(), stylingType) +} + +func (i idProvider) SetPayload(payload string) database.Change { + return database.NewChange(i.PayloadColumn(), payload) +} + +func (i idProvider) SetUpdatedAt(updatedAt *time.Time) database.Change { + return database.NewChangePtr(i.UpdatedAtColumn(), updatedAt) +} + +func scanIDProvider(ctx context.Context, querier database.Querier, builder *database.StatementBuilder) (*domain.IdentityProvider, error) { + idp := &domain.IdentityProvider{} + rows, err := querier.Query(ctx, builder.String(), builder.Args()...) + if err != nil { + return nil, err + } + + err = rows.(database.CollectableRows).CollectExactlyOneRow(idp) + if err != nil { + return nil, err + } + + return idp, err +} + +func scanIDProviders(ctx context.Context, querier database.Querier, builder *database.StatementBuilder) ([]*domain.IdentityProvider, error) { + idps := []*domain.IdentityProvider{} + + rows, err := querier.Query(ctx, builder.String(), builder.Args()...) + if err != nil { + return nil, err + } + + err = rows.(database.CollectableRows).Collect(&idps) + if err != nil { + return nil, err + } + + return idps, nil +} diff --git a/backend/v3/storage/database/repository/id_provider_test.go b/backend/v3/storage/database/repository/id_provider_test.go new file mode 100644 index 00000000000..bab64f8e461 --- /dev/null +++ b/backend/v3/storage/database/repository/id_provider_test.go @@ -0,0 +1,1954 @@ +package repository_test + +import ( + "testing" + "time" + + "github.com/brianvoe/gofakeit/v6" + "github.com/muhlemmer/gu" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/zitadel/zitadel/backend/v3/domain" + "github.com/zitadel/zitadel/backend/v3/storage/database" + "github.com/zitadel/zitadel/backend/v3/storage/database/repository" +) + +var stylingType int16 = 1 + +func TestCreateIDProvider(t *testing.T) { + beforeCreate := time.Now() + tx, err := pool.Begin(t.Context(), nil) + require.NoError(t, err) + defer func() { + err := tx.Rollback(t.Context()) + if err != nil { + t.Errorf("error rolling back transaction: %v", err) + } + }() + + // create instance + instanceId := gofakeit.Name() + instance := domain.Instance{ + ID: instanceId, + Name: gofakeit.Name(), + DefaultOrgID: "defaultOrgId", + IAMProjectID: "iamProject", + ConsoleClientID: "consoleCLient", + ConsoleAppID: "consoleApp", + DefaultLanguage: "defaultLanguage", + } + instanceRepo := repository.InstanceRepository() + err = instanceRepo.Create(t.Context(), tx, &instance) + require.NoError(t, err) + + // create org + orgId := gofakeit.Name() + org := domain.Organization{ + ID: orgId, + Name: gofakeit.Name(), + InstanceID: instanceId, + State: domain.OrgStateActive, + } + organizationRepo := repository.OrganizationRepository() + err = organizationRepo.Create(t.Context(), tx, &org) + require.NoError(t, err) + + type test struct { + name string + testFunc func(t *testing.T, tx database.QueryExecutor) *domain.IdentityProvider + idp domain.IdentityProvider + err error + } + + // TESTS + tests := []test{ + { + name: "happy path", + idp: domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + }, + }, + { + name: "create idp without name", + idp: domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + // Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + }, + err: new(database.CheckError), + }, + { + name: "adding idp with same id twice", + testFunc: func(t *testing.T, tx database.QueryExecutor) *domain.IdentityProvider { + idpRepo := repository.IDProviderRepository() + + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + // change the name to make sure same only the id clashes + org.Name = gofakeit.Name() + return &idp + }, + err: new(database.UniqueError), + }, + { + name: "adding idp with same name twice", + testFunc: func(t *testing.T, tx database.QueryExecutor) *domain.IdentityProvider { + idpRepo := repository.IDProviderRepository() + + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + // change the id to make sure same name causes an error + idp.ID = gofakeit.Name() + return &idp + }, + err: new(database.UniqueError), + }, + func() test { + id := gofakeit.Name() + name := gofakeit.Name() + return test{ + name: "adding idp with same name, id, different instance", + testFunc: func(t *testing.T, tx database.QueryExecutor) *domain.IdentityProvider { + // create instance + newInstId := gofakeit.Name() + instance := domain.Instance{ + ID: newInstId, + Name: gofakeit.Name(), + DefaultOrgID: "defaultOrgId", + IAMProjectID: "iamProject", + ConsoleClientID: "consoleCLient", + ConsoleAppID: "consoleApp", + DefaultLanguage: "defaultLanguage", + } + instanceRepo := repository.InstanceRepository() + err := instanceRepo.Create(t.Context(), tx, &instance) + assert.Nil(t, err) + + // create org + newOrgId := gofakeit.Name() + org := domain.Organization{ + ID: newOrgId, + Name: gofakeit.Name(), + InstanceID: newInstId, + State: domain.OrgStateActive, + } + organizationRepo := repository.OrganizationRepository() + err = organizationRepo.Create(t.Context(), tx, &org) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + idp := domain.IdentityProvider{ + InstanceID: newInstId, + OrgID: &newOrgId, + ID: id, + State: domain.IDPStateActive, + Name: name, + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err = idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + // change the instanceID to a different instance + idp.InstanceID = instanceId + // change the OrgId to a different organization + idp.OrgID = &orgId + return &idp + }, + idp: domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: id, + State: domain.IDPStateActive, + Name: name, + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + }, + } + }(), + { + name: "adding idp with no id", + idp: domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + // ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + }, + err: new(database.CheckError), + }, + { + name: "adding idp with no name", + idp: domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + // Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + }, + err: new(database.CheckError), + }, + { + name: "adding idp with no instance id", + idp: domain.IdentityProvider{ + // InstanceID: instanceId, + OrgID: &orgId, + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + }, + err: new(database.IntegrityViolationError), + }, + { + name: "adding idp with non existent instance id", + idp: domain.IdentityProvider{ + InstanceID: gofakeit.Name(), + OrgID: &orgId, + State: domain.IDPStateActive, + ID: gofakeit.Name(), + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + }, + err: new(database.ForeignKeyError), + }, + { + name: "adding idp with non existent org id", + idp: domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: gu.Ptr(gofakeit.Name()), + State: domain.IDPStateActive, + ID: gofakeit.Name(), + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + }, + err: new(database.ForeignKeyError), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tx, err := tx.Begin(t.Context()) + require.NoError(t, err) + defer func() { + err := tx.Rollback(t.Context()) + if err != nil { + t.Errorf("error rolling back savepoint: %v", err) + } + }() + + var idp *domain.IdentityProvider + if tt.testFunc != nil { + idp = tt.testFunc(t, tx) + } else { + idp = &tt.idp + } + idpRepo := repository.IDProviderRepository() + + // create idp + + err = idpRepo.Create(t.Context(), tx, idp) + assert.ErrorIs(t, err, tt.err) + if err != nil { + return + } + afterCreate := time.Now() + + // check organization values + idp, err = idpRepo.Get(t.Context(), tx, + idpRepo.IDCondition(idp.ID), + idp.InstanceID, + idp.OrgID, + ) + require.NoError(t, err) + + assert.Equal(t, tt.idp.InstanceID, idp.InstanceID) + assert.Equal(t, tt.idp.OrgID, idp.OrgID) + assert.Equal(t, tt.idp.State, idp.State) + assert.Equal(t, tt.idp.ID, idp.ID) + assert.Equal(t, tt.idp.Name, idp.Name) + assert.Equal(t, tt.idp.Type, idp.Type) + assert.Equal(t, tt.idp.AllowCreation, idp.AllowCreation) + assert.Equal(t, tt.idp.AllowAutoCreation, idp.AllowAutoCreation) + assert.Equal(t, tt.idp.AllowAutoUpdate, idp.AllowAutoUpdate) + assert.Equal(t, tt.idp.AllowLinking, idp.AllowLinking) + assert.Equal(t, tt.idp.StylingType, idp.StylingType) + assert.Equal(t, tt.idp.Payload, idp.Payload) + assert.WithinRange(t, idp.CreatedAt, beforeCreate, afterCreate) + assert.WithinRange(t, idp.UpdatedAt, beforeCreate, afterCreate) + }) + } +} + +func TestUpdateIDProvider(t *testing.T) { + beforeUpdate := time.Now() + tx, err := pool.Begin(t.Context(), nil) + require.NoError(t, err) + defer func() { + err := tx.Rollback(t.Context()) + if err != nil { + t.Errorf("error rolling back transaction: %v", err) + } + }() + + // create instance + instanceId := gofakeit.Name() + instance := domain.Instance{ + ID: instanceId, + Name: gofakeit.Name(), + DefaultOrgID: "defaultOrgId", + IAMProjectID: "iamProject", + ConsoleClientID: "consoleCLient", + ConsoleAppID: "consoleApp", + DefaultLanguage: "defaultLanguage", + } + instanceRepo := repository.InstanceRepository() + err = instanceRepo.Create(t.Context(), tx, &instance) + require.NoError(t, err) + + // create org + orgId := gofakeit.Name() + org := domain.Organization{ + ID: orgId, + Name: gofakeit.Name(), + InstanceID: instanceId, + State: domain.OrgStateActive, + } + organizationRepo := repository.OrganizationRepository() + err = organizationRepo.Create(t.Context(), tx, &org) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + tests := []struct { + name string + testFunc func(t *testing.T, tx database.QueryExecutor) *domain.IdentityProvider + update []database.Change + rowsAffected int64 + }{ + { + name: "happy path update name", + testFunc: func(t *testing.T, tx database.QueryExecutor) *domain.IdentityProvider { + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + idp.Name = "new_name" + return &idp + }, + update: []database.Change{idpRepo.SetName("new_name")}, + rowsAffected: 1, + }, + { + name: "happy path update state", + testFunc: func(t *testing.T, tx database.QueryExecutor) *domain.IdentityProvider { + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + idp.State = domain.IDPStateInactive + return &idp + }, + update: []database.Change{idpRepo.SetState(domain.IDPStateInactive)}, + rowsAffected: 1, + }, + { + name: "happy path update AllowCreation", + testFunc: func(t *testing.T, tx database.QueryExecutor) *domain.IdentityProvider { + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + idp.AllowCreation = false + return &idp + }, + update: []database.Change{idpRepo.SetAllowCreation(false)}, + rowsAffected: 1, + }, + { + name: "happy path update AllowAutoCreation", + testFunc: func(t *testing.T, tx database.QueryExecutor) *domain.IdentityProvider { + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + idp.AllowAutoCreation = false + return &idp + }, + update: []database.Change{idpRepo.SetAllowAutoCreation(false)}, + rowsAffected: 1, + }, + { + name: "happy path update AllowLinking", + testFunc: func(t *testing.T, tx database.QueryExecutor) *domain.IdentityProvider { + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + idp.AllowLinking = false + return &idp + }, + update: []database.Change{idpRepo.SetAllowLinking(false)}, + rowsAffected: 1, + }, + { + name: "happy path update StylingType", + testFunc: func(t *testing.T, tx database.QueryExecutor) *domain.IdentityProvider { + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + newStyleType := int16(2) + idp.StylingType = &newStyleType + return &idp + }, + update: []database.Change{idpRepo.SetStylingType(2)}, + rowsAffected: 1, + }, + { + name: "happy path update Payload", + testFunc: func(t *testing.T, tx database.QueryExecutor) *domain.IdentityProvider { + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + idp.Payload = []byte(`{"json": {}}`) + return &idp + }, + // update: []database.Change{idpRepo.SetPayload("{{}}")}, + update: []database.Change{idpRepo.SetPayload(`{"json": {}}`)}, + rowsAffected: 1, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tx, err := tx.Begin(t.Context()) + require.NoError(t, err) + defer func() { + err := tx.Rollback(t.Context()) + if err != nil { + t.Errorf("error rolling back savepoint: %v", err) + } + }() + // organizationRepo := repository.OrganizationRepository() + idpRepo := repository.IDProviderRepository() + + createdIDP := tt.testFunc(t, tx) + + // update idp + rowsAffected, err := idpRepo.Update(t.Context(), tx, + idpRepo.IDCondition(createdIDP.ID), + createdIDP.InstanceID, + createdIDP.OrgID, + tt.update..., + ) + afterUpdate := time.Now() + require.NoError(t, err) + + assert.Equal(t, tt.rowsAffected, rowsAffected) + + if rowsAffected == 0 { + return + } + + // check idp values + idp, err := idpRepo.Get(t.Context(), tx, + idpRepo.IDCondition(createdIDP.ID), + createdIDP.InstanceID, + createdIDP.OrgID, + ) + require.NoError(t, err) + + assert.Equal(t, createdIDP.InstanceID, idp.InstanceID) + assert.Equal(t, createdIDP.OrgID, idp.OrgID) + assert.Equal(t, createdIDP.State, idp.State) + assert.Equal(t, createdIDP.ID, idp.ID) + assert.Equal(t, createdIDP.Name, idp.Name) + assert.Equal(t, createdIDP.Type, idp.Type) + assert.Equal(t, createdIDP.AllowCreation, idp.AllowCreation) + assert.Equal(t, createdIDP.AllowAutoCreation, idp.AllowAutoCreation) + assert.Equal(t, createdIDP.AllowAutoUpdate, idp.AllowAutoUpdate) + assert.Equal(t, createdIDP.AllowLinking, idp.AllowLinking) + assert.Equal(t, createdIDP.StylingType, idp.StylingType) + assert.Equal(t, createdIDP.Payload, idp.Payload) + assert.WithinRange(t, idp.UpdatedAt, beforeUpdate, afterUpdate) + }) + } +} + +func TestGetIDProvider(t *testing.T) { + tx, err := pool.Begin(t.Context(), nil) + require.NoError(t, err) + defer func() { + err := tx.Rollback(t.Context()) + if err != nil { + t.Errorf("error rolling back transaction: %v", err) + } + }() + + // create instance + instanceId := gofakeit.Name() + instance := domain.Instance{ + ID: instanceId, + Name: gofakeit.Name(), + DefaultOrgID: "defaultOrgId", + IAMProjectID: "iamProject", + ConsoleClientID: "consoleCLient", + ConsoleAppID: "consoleApp", + DefaultLanguage: "defaultLanguage", + } + instanceRepo := repository.InstanceRepository() + err = instanceRepo.Create(t.Context(), tx, &instance) + require.NoError(t, err) + + // create org + orgId := gofakeit.Name() + org := domain.Organization{ + ID: orgId, + Name: gofakeit.Name(), + InstanceID: instanceId, + State: domain.OrgStateActive, + } + organizationRepo := repository.OrganizationRepository() + err = organizationRepo.Create(t.Context(), tx, &org) + require.NoError(t, err) + + // create organization + // this org is created as an additional org which should NOT + // be returned in the results of the tests + preexistingOrg := domain.Organization{ + ID: gofakeit.Name(), + Name: gofakeit.Name(), + InstanceID: instanceId, + State: domain.OrgStateActive, + } + err = organizationRepo.Create(t.Context(), tx, &preexistingOrg) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + type test struct { + name string + testFunc func(t *testing.T) *domain.IdentityProvider + idpIdentifierCondition database.Condition + err error + } + + tests := []test{ + func() test { + id := gofakeit.Name() + return test{ + name: "happy path get using id", + testFunc: func(t *testing.T) *domain.IdentityProvider { + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: id, + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + return &idp + }, + idpIdentifierCondition: idpRepo.IDCondition(id), + } + }(), + func() test { + name := gofakeit.Name() + return test{ + name: "happy path get using name", + testFunc: func(t *testing.T) *domain.IdentityProvider { + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: name, + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + return &idp + }, + idpIdentifierCondition: idpRepo.NameCondition(name), + } + }(), + { + name: "happy path using styling type", + testFunc: func(t *testing.T) *domain.IdentityProvider { + stylingType := 2 + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: gu.Ptr(int16(stylingType)), + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + return &idp + }, + idpIdentifierCondition: idpRepo.StylingTypeCondition(2), + }, + { + name: "get using non existent id", + testFunc: func(t *testing.T) *domain.IdentityProvider { + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + return &idp + }, + idpIdentifierCondition: idpRepo.IDCondition("non-existent-id"), + err: new(database.NoRowFoundError), + }, + { + name: "get using non existent name", + testFunc: func(t *testing.T) *domain.IdentityProvider { + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + return &idp + }, + idpIdentifierCondition: idpRepo.NameCondition("non-existent-name"), + err: new(database.NoRowFoundError), + }, + func() test { + id := gofakeit.Name() + return test{ + name: "non existent orgID", + testFunc: func(t *testing.T) *domain.IdentityProvider { + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: id, + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + idp.OrgID = gu.Ptr("non-existent-orgID") + return &idp + }, + idpIdentifierCondition: idpRepo.IDCondition(id), + err: new(database.NoRowFoundError), + } + }(), + func() test { + name := gofakeit.Name() + return test{ + name: "non existent instanceID", + testFunc: func(t *testing.T) *domain.IdentityProvider { + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: name, + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + idp.InstanceID = "non-existent-instanceID" + return &idp + }, + idpIdentifierCondition: idpRepo.NameCondition(name), + err: new(database.NoRowFoundError), + } + }(), + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + var idp *domain.IdentityProvider + if tt.testFunc != nil { + idp = tt.testFunc(t) + } + + // get idp + returnedIDP, err := idpRepo.Get(t.Context(), tx, + tt.idpIdentifierCondition, + idp.InstanceID, + idp.OrgID, + ) + if err != nil { + require.ErrorIs(t, tt.err, err) + return + } + + assert.Equal(t, returnedIDP.InstanceID, idp.InstanceID) + assert.Equal(t, returnedIDP.OrgID, idp.OrgID) + assert.Equal(t, returnedIDP.State, idp.State) + assert.Equal(t, returnedIDP.ID, idp.ID) + assert.Equal(t, returnedIDP.Name, idp.Name) + assert.Equal(t, returnedIDP.Type, idp.Type) + assert.Equal(t, returnedIDP.AllowCreation, idp.AllowCreation) + assert.Equal(t, returnedIDP.AllowAutoCreation, idp.AllowAutoCreation) + assert.Equal(t, returnedIDP.AllowAutoUpdate, idp.AllowAutoUpdate) + assert.Equal(t, returnedIDP.AllowLinking, idp.AllowLinking) + assert.Equal(t, returnedIDP.StylingType, idp.StylingType) + assert.Equal(t, returnedIDP.Payload, idp.Payload) + }) + } +} + +// gocognit linting fails due to number of test cases +// and the fact that each test case has a testFunc() +// +//nolint:gocognit +func TestListIDProvider(t *testing.T) { + tx, err := pool.Begin(t.Context(), nil) + require.NoError(t, err) + defer func() { + err := tx.Rollback(t.Context()) + if err != nil { + t.Errorf("error rolling back transaction: %v", err) + } + }() + + // create instance + instanceId := gofakeit.Name() + instance := domain.Instance{ + ID: instanceId, + Name: gofakeit.Name(), + DefaultOrgID: "defaultOrgId", + IAMProjectID: "iamProject", + ConsoleClientID: "consoleCLient", + ConsoleAppID: "consoleApp", + DefaultLanguage: "defaultLanguage", + } + instanceRepo := repository.InstanceRepository() + err = instanceRepo.Create(t.Context(), tx, &instance) + require.NoError(t, err) + + // create org + orgId := gofakeit.Name() + org := domain.Organization{ + ID: orgId, + Name: gofakeit.Name(), + InstanceID: instanceId, + State: domain.OrgStateActive, + } + organizationRepo := repository.OrganizationRepository() + err = organizationRepo.Create(t.Context(), tx, &org) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + type test struct { + name string + testFunc func(t *testing.T) []*domain.IdentityProvider + conditionClauses []database.Condition + noIDPsReturned bool + } + tests := []test{ + { + name: "multiple idps filter on instance", + testFunc: func(t *testing.T) []*domain.IdentityProvider { + // create instance + newInstanceId := gofakeit.Name() + instance := domain.Instance{ + ID: newInstanceId, + Name: gofakeit.Name(), + DefaultOrgID: "defaultOrgId", + IAMProjectID: "iamProject", + ConsoleClientID: "consoleCLient", + ConsoleAppID: "consoleApp", + DefaultLanguage: "defaultLanguage", + } + err = instanceRepo.Create(t.Context(), tx, &instance) + require.NoError(t, err) + + // create org + newOrgId := gofakeit.Name() + org := domain.Organization{ + ID: newOrgId, + Name: gofakeit.Name(), + InstanceID: newInstanceId, + State: domain.OrgStateActive, + } + organizationRepo := repository.OrganizationRepository() + err = organizationRepo.Create(t.Context(), tx, &org) + require.NoError(t, err) + + // create idp + // this idp is created as an additional idp which should NOT + // be returned in the results of this test case + idp := domain.IdentityProvider{ + InstanceID: newInstanceId, + OrgID: &newOrgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + noOfIDPs := 5 + idps := make([]*domain.IdentityProvider, noOfIDPs) + for i := range noOfIDPs { + + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + idps[i] = &idp + } + + return idps + }, + conditionClauses: []database.Condition{idpRepo.InstanceIDCondition(instanceId)}, + }, + { + name: "multiple idps filter on org", + testFunc: func(t *testing.T) []*domain.IdentityProvider { + // create org + newOrgId := gofakeit.Name() + org := domain.Organization{ + ID: newOrgId, + Name: gofakeit.Name(), + InstanceID: instanceId, + State: domain.OrgStateActive, + } + organizationRepo := repository.OrganizationRepository() + err = organizationRepo.Create(t.Context(), tx, &org) + require.NoError(t, err) + + // create idp + // this idp is created as an additional idp which should NOT + // be returned in the results of this test case + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &newOrgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + noOfIDPs := 5 + idps := make([]*domain.IdentityProvider, noOfIDPs) + for i := range noOfIDPs { + + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + idps[i] = &idp + } + + return idps + }, + conditionClauses: []database.Condition{idpRepo.OrgIDCondition(&orgId)}, + }, + { + name: "happy path single idp no filter", + testFunc: func(t *testing.T) []*domain.IdentityProvider { + noOfIDPs := 1 + idps := make([]*domain.IdentityProvider, noOfIDPs) + for i := range noOfIDPs { + + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + idps[i] = &idp + } + + return idps + }, + }, + { + name: "happy path multiple idps no filter", + testFunc: func(t *testing.T) []*domain.IdentityProvider { + noOfIDPs := 5 + idps := make([]*domain.IdentityProvider, noOfIDPs) + for i := range noOfIDPs { + + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + idps[i] = &idp + } + + return idps + }, + }, + func() test { + id := gofakeit.Name() + return test{ + name: "idp filter on id", + testFunc: func(t *testing.T) []*domain.IdentityProvider { + // create idp + // this idp is created as an additional idp which should NOT + // be returned in the results of this test case + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + noOfIDPs := 1 + idps := make([]*domain.IdentityProvider, noOfIDPs) + for i := range noOfIDPs { + + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: id, + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + idps[i] = &idp + } + + return idps + }, + conditionClauses: []database.Condition{idpRepo.IDCondition(id)}, + } + }(), + { + name: "multiple idps filter on state", + testFunc: func(t *testing.T) []*domain.IdentityProvider { + // create idp + // this idp is created as an additional idp which should NOT + // be returned in the results of this test case + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + // state inactive + State: domain.IDPStateInactive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + noOfIDPs := 5 + idps := make([]*domain.IdentityProvider, noOfIDPs) + for i := range noOfIDPs { + + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + // state active + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + idps[i] = &idp + } + + return idps + }, + conditionClauses: []database.Condition{idpRepo.StateCondition(domain.IDPStateActive)}, + }, + func() test { + name := gofakeit.Name() + return test{ + name: "multiple idps filter on name", + testFunc: func(t *testing.T) []*domain.IdentityProvider { + // create idp + // this idp is created as an additional idp which should NOT + // be returned in the results of this test case + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + noOfIDPs := 1 + idps := make([]*domain.IdentityProvider, noOfIDPs) + for i := range noOfIDPs { + + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: name, + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + idps[i] = &idp + } + + return idps + }, + conditionClauses: []database.Condition{idpRepo.NameCondition(name)}, + } + }(), + { + name: "multiple idps filter on type", + testFunc: func(t *testing.T) []*domain.IdentityProvider { + // create idp + // this idp is created as an additional idp which should NOT + // be returned in the results of this test case + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateInactive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + noOfIDPs := 5 + idps := make([]*domain.IdentityProvider, noOfIDPs) + for i := range noOfIDPs { + + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + // type LDAP + Type: gu.Ptr(domain.IDPTypeLDAP), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + idps[i] = &idp + } + + return idps + }, + conditionClauses: []database.Condition{idpRepo.TypeCondition(domain.IDPTypeLDAP)}, + }, + { + name: "multiple idps filter on AllowCreation", + testFunc: func(t *testing.T) []*domain.IdentityProvider { + // create idp + // this idp is created as an additional idp which should NOT + // be returned in the results of this test case + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateInactive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + noOfIDPs := 5 + idps := make([]*domain.IdentityProvider, noOfIDPs) + for i := range noOfIDPs { + + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeLDAP), + // AllowCreation set to false + AllowCreation: false, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + idps[i] = &idp + } + + return idps + }, + conditionClauses: []database.Condition{idpRepo.AllowCreationCondition(false)}, + }, + { + name: "multiple idps filter on AllowAutoCreation", + testFunc: func(t *testing.T) []*domain.IdentityProvider { + // create idp + // this idp is created as an additional idp which should NOT + // be returned in the results of this test case + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateInactive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + noOfIDPs := 5 + idps := make([]*domain.IdentityProvider, noOfIDPs) + for i := range noOfIDPs { + + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeLDAP), + AllowCreation: true, + // AllowAutoCreation set to false + AllowAutoCreation: false, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + idps[i] = &idp + } + + return idps + }, + conditionClauses: []database.Condition{idpRepo.AllowAutoCreationCondition(false)}, + }, + { + name: "multiple idps filter on AllowAutoUpdate", + testFunc: func(t *testing.T) []*domain.IdentityProvider { + // create idp + // this idp is created as an additional idp which should NOT + // be returned in the results of this test case + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + // state inactive + State: domain.IDPStateInactive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + noOfIDPs := 5 + idps := make([]*domain.IdentityProvider, noOfIDPs) + for i := range noOfIDPs { + + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeLDAP), + AllowCreation: true, + AllowAutoCreation: true, + // AllowAutoUpdate set to false + AllowAutoUpdate: false, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + idps[i] = &idp + } + + return idps + }, + conditionClauses: []database.Condition{idpRepo.AllowAutoUpdateCondition(false)}, + }, + { + name: "multiple idps filter on AllowLinking", + testFunc: func(t *testing.T) []*domain.IdentityProvider { + // create idp + // this idp is created as an additional idp which should NOT + // be returned in the results of this test case + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + // state inactive + State: domain.IDPStateInactive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + noOfIDPs := 5 + idps := make([]*domain.IdentityProvider, noOfIDPs) + for i := range noOfIDPs { + + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeLDAP), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + // AllowLinking set to false + AllowLinking: false, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + idps[i] = &idp + } + + return idps + }, + conditionClauses: []database.Condition{idpRepo.AllowLinkingCondition(false)}, + }, + { + name: "multiple idps filter on StylingType", + testFunc: func(t *testing.T) []*domain.IdentityProvider { + // create idp + // this idp is created as an additional idp which should NOT + // be returned in the results of this test case + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + var sytlingType int16 = 4 + noOfIDPs := 1 + idps := make([]*domain.IdentityProvider, noOfIDPs) + for i := range noOfIDPs { + + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + // StylingType set to 4 + StylingType: &sytlingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + idps[i] = &idp + } + + return idps + }, + conditionClauses: []database.Condition{idpRepo.StylingTypeCondition(4)}, + }, + func() test { + payload := []byte(`{"json": {}}`) + return test{ + name: "multiple idps filter on Payload", + testFunc: func(t *testing.T) []*domain.IdentityProvider { + // create idp + // this idp is created as an additional idp which should NOT + // be returned in the results of this test case + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + noOfIDPs := 1 + idps := make([]*domain.IdentityProvider, noOfIDPs) + for i := range noOfIDPs { + + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: payload, + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + idps[i] = &idp + } + + return idps + }, + conditionClauses: []database.Condition{idpRepo.PayloadCondition(string(payload))}, + } + }(), + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer func() { + _, err := tx.Exec(t.Context(), "DELETE FROM zitadel.identity_providers") + require.NoError(t, err) + }() + + idps := tt.testFunc(t) + + // check idp values + returnedIDPs, err := idpRepo.List(t.Context(), tx, + tt.conditionClauses..., + ) + require.NoError(t, err) + if tt.noIDPsReturned { + assert.Nil(t, returnedIDPs) + return + } + + assert.Equal(t, len(idps), len(returnedIDPs)) + for i, idp := range idps { + + assert.Equal(t, returnedIDPs[i].InstanceID, idp.InstanceID) + assert.Equal(t, returnedIDPs[i].OrgID, idp.OrgID) + assert.Equal(t, returnedIDPs[i].State, idp.State) + assert.Equal(t, returnedIDPs[i].ID, idp.ID) + assert.Equal(t, returnedIDPs[i].Name, idp.Name) + assert.Equal(t, returnedIDPs[i].Type, idp.Type) + assert.Equal(t, returnedIDPs[i].AllowCreation, idp.AllowCreation) + assert.Equal(t, returnedIDPs[i].AllowAutoCreation, idp.AllowAutoCreation) + assert.Equal(t, returnedIDPs[i].AllowAutoUpdate, idp.AllowAutoUpdate) + assert.Equal(t, returnedIDPs[i].AllowLinking, idp.AllowLinking) + assert.Equal(t, returnedIDPs[i].StylingType, idp.StylingType) + assert.Equal(t, returnedIDPs[i].Payload, idp.Payload) + } + }) + } +} + +func TestDeleteIDProvider(t *testing.T) { + tx, err := pool.Begin(t.Context(), nil) + require.NoError(t, err) + defer func() { + err := tx.Rollback(t.Context()) + if err != nil { + t.Errorf("error rolling back transaction: %v", err) + } + }() + + // create instance + instanceId := gofakeit.Name() + instance := domain.Instance{ + ID: instanceId, + Name: gofakeit.Name(), + DefaultOrgID: "defaultOrgId", + IAMProjectID: "iamProject", + ConsoleClientID: "consoleCLient", + ConsoleAppID: "consoleApp", + DefaultLanguage: "defaultLanguage", + } + instanceRepo := repository.InstanceRepository() + err = instanceRepo.Create(t.Context(), tx, &instance) + require.NoError(t, err) + + // create org + orgId := gofakeit.Name() + org := domain.Organization{ + ID: orgId, + Name: gofakeit.Name(), + InstanceID: instanceId, + State: domain.OrgStateActive, + } + organizationRepo := repository.OrganizationRepository() + err = organizationRepo.Create(t.Context(), tx, &org) + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository() + + type test struct { + name string + testFunc func(t *testing.T) + idpIdentifierCondition domain.IDPIdentifierCondition + noOfDeletedRows int64 + } + tests := []test{ + func() test { + id := gofakeit.Name() + var noOfIDPs int64 = 1 + return test{ + name: "happy path delete idp filter id", + testFunc: func(t *testing.T) { + for range noOfIDPs { + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: id, + State: domain.IDPStateActive, + Name: gofakeit.Name(), + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + } + }, + idpIdentifierCondition: idpRepo.IDCondition(id), + noOfDeletedRows: noOfIDPs, + } + }(), + func() test { + name := gofakeit.Name() + var noOfIDPs int64 = 1 + return test{ + name: "happy path delete idp filter name", + testFunc: func(t *testing.T) { + for range noOfIDPs { + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: name, + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + + } + }, + idpIdentifierCondition: idpRepo.NameCondition(name), + noOfDeletedRows: noOfIDPs, + } + }(), + { + name: "delete non existent idp", + idpIdentifierCondition: idpRepo.NameCondition(gofakeit.Name()), + }, + func() test { + name := gofakeit.Name() + return test{ + name: "deleted already deleted idp", + testFunc: func(t *testing.T) { + noOfIDPs := 1 + for range noOfIDPs { + idp := domain.IdentityProvider{ + InstanceID: instanceId, + OrgID: &orgId, + ID: gofakeit.Name(), + State: domain.IDPStateActive, + Name: name, + Type: gu.Ptr(domain.IDPTypeOIDC), + AllowCreation: true, + AllowAutoCreation: true, + AllowAutoUpdate: true, + AllowLinking: true, + StylingType: &stylingType, + Payload: []byte("{}"), + } + + err := idpRepo.Create(t.Context(), tx, &idp) + require.NoError(t, err) + } + + // delete organization + affectedRows, err := idpRepo.Delete(t.Context(), tx, + idpRepo.NameCondition(name), + instanceId, + &orgId, + ) + assert.Equal(t, int64(1), affectedRows) + require.NoError(t, err) + }, + idpIdentifierCondition: idpRepo.NameCondition(name), + // this test should return 0 affected rows as the idp was already deleted + noOfDeletedRows: 0, + } + }(), + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.testFunc != nil { + tt.testFunc(t) + } + + // delete idp + noOfDeletedRows, err := idpRepo.Delete(t.Context(), tx, + tt.idpIdentifierCondition, + instanceId, + &orgId, + ) + require.NoError(t, err) + assert.Equal(t, noOfDeletedRows, tt.noOfDeletedRows) + + // check idp was deleted + organization, err := idpRepo.Get(t.Context(), tx, + tt.idpIdentifierCondition, + instanceId, + &orgId, + ) + require.ErrorIs(t, err, new(database.NoRowFoundError)) + assert.Nil(t, organization) + }) + } +} diff --git a/backend/v3/storage/database/repository/org_test.go b/backend/v3/storage/database/repository/org_test.go index c624ffe6c1c..7e785834b5e 100644 --- a/backend/v3/storage/database/repository/org_test.go +++ b/backend/v3/storage/database/repository/org_test.go @@ -609,11 +609,6 @@ func TestGetOrganization(t *testing.T) { } require.NoError(t, err) - if org.Name == "non existent org" { - assert.Nil(t, returnedOrg) - return - } - assert.Equal(t, returnedOrg.ID, org.ID) assert.Equal(t, returnedOrg.Name, org.Name) assert.Equal(t, returnedOrg.InstanceID, org.InstanceID) @@ -931,9 +926,7 @@ func TestDeleteOrganization(t *testing.T) { return test{ name: "happy path delete organization filter id", testFunc: func(t *testing.T) { - organizations := make([]*domain.Organization, noOfOrganizations) - for i := range noOfOrganizations { - + for range noOfOrganizations { org := domain.Organization{ ID: organizationId, Name: gofakeit.Name(), @@ -945,7 +938,6 @@ func TestDeleteOrganization(t *testing.T) { err := organizationRepo.Create(t.Context(), tx, &org) require.NoError(t, err) - organizations[i] = &org } }, orgIdentifierCondition: organizationRepo.IDCondition(organizationId), @@ -958,9 +950,7 @@ func TestDeleteOrganization(t *testing.T) { return test{ name: "happy path delete organization filter name", testFunc: func(t *testing.T) { - organizations := make([]*domain.Organization, noOfOrganizations) - for i := range noOfOrganizations { - + for range noOfOrganizations { org := domain.Organization{ ID: gofakeit.Name(), Name: organizationName, @@ -972,7 +962,6 @@ func TestDeleteOrganization(t *testing.T) { err := organizationRepo.Create(t.Context(), tx, &org) require.NoError(t, err) - organizations[i] = &org } }, orgIdentifierCondition: organizationRepo.NameCondition(database.TextOperationEqual, organizationName), @@ -991,28 +980,21 @@ func TestDeleteOrganization(t *testing.T) { return test{ name: "deleted already deleted organization", testFunc: func(t *testing.T) { - noOfOrganizations := 1 - organizations := make([]*domain.Organization, noOfOrganizations) - for i := range noOfOrganizations { - - org := domain.Organization{ - ID: gofakeit.Name(), - Name: organizationName, - InstanceID: instanceId, - State: domain.OrgStateActive, - } - - // create organization - err := organizationRepo.Create(t.Context(), tx, &org) - require.NoError(t, err) - - organizations[i] = &org + org := domain.Organization{ + ID: gofakeit.Name(), + Name: organizationName, + InstanceID: instanceId, + State: domain.OrgStateActive, } + // create organization + err := organizationRepo.Create(t.Context(), tx, &org) + require.NoError(t, err) + // delete organization affectedRows, err := organizationRepo.Delete(t.Context(), tx, database.And( - organizationRepo.InstanceIDCondition(organizations[0].InstanceID), + organizationRepo.InstanceIDCondition(org.InstanceID), organizationRepo.NameCondition(database.TextOperationEqual, organizationName), ), ) diff --git a/internal/api/grpc/admin/instance.go b/internal/api/grpc/admin/instance.go index a7b0316ec43..38fc9ec4d8a 100644 --- a/internal/api/grpc/admin/instance.go +++ b/internal/api/grpc/admin/instance.go @@ -29,7 +29,7 @@ func (s *Server) ListInstanceDomains(ctx context.Context, req *admin_pb.ListInst return nil, err } return &admin_pb.ListInstanceDomainsResponse{ - Result: instance_grpc.DomainsToPb(domains.Domains), + Result: instance_grpc.DomainsToPb(domains.Domains), SortingColumn: req.SortingColumn, Details: object.ToListDetails( domains.Count, @@ -49,7 +49,7 @@ func (s *Server) ListInstanceTrustedDomains(ctx context.Context, req *admin_pb.L return nil, err } return &admin_pb.ListInstanceTrustedDomainsResponse{ - Result: instance_grpc.TrustedDomainsToPb(domains.Domains), + Result: instance_grpc.TrustedDomainsToPb(domains.Domains), SortingColumn: req.SortingColumn, Details: object.ToListDetails( domains.Count, diff --git a/internal/query/projection/idp_template_relational.go b/internal/query/projection/idp_template_relational.go new file mode 100644 index 00000000000..79d058fce6f --- /dev/null +++ b/internal/query/projection/idp_template_relational.go @@ -0,0 +1,2523 @@ +package projection + +import ( + "context" + "database/sql" + "encoding/json" + + "github.com/zitadel/zitadel/backend/v3/domain" + v3_sql "github.com/zitadel/zitadel/backend/v3/storage/database/dialect/sql" + "github.com/zitadel/zitadel/backend/v3/storage/database/repository" + internal_domain "github.com/zitadel/zitadel/internal/domain" + "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/idpconfig" + "github.com/zitadel/zitadel/internal/repository/instance" + "github.com/zitadel/zitadel/internal/repository/org" + "github.com/zitadel/zitadel/internal/zerrors" +) + +const ( + IDPRelationalTable = "zitadel.identity_providers" + IDPRelationalOrgIdCol = "org_id" + IDPRelationalAutoRegisterCol = "auto_register" + IDPRelationalPayloadCol = "payload" + IDPRelationalOrgId = "org_id" + IDPRelationalAllowCreationCol = "allow_creation" + IDPRelationalAllowLinkingCol = "allow_linking" + IDPRelationalAllowAutoCreationCol = "allow_auto_creation" + IDPRelationalAllowAutoUpdateCol = "allow_auto_update" + IDPRelationalAllowAutoLinkingCol = "auto_linking_field" +) + +type idpTemplateRelationalProjection struct { + idpRepo domain.IDProviderRepository +} + +func newIDPTemplateRelationalProjection(ctx context.Context, config handler.Config) *handler.Handler { + idpRepo := repository.IDProviderRepository() + return handler.NewHandler(ctx, &config, &idpTemplateRelationalProjection{ + idpRepo: idpRepo, + }) +} + +func (*idpTemplateRelationalProjection) Name() string { + return IDPRelationalTable +} + +func (p *idpTemplateRelationalProjection) Reducers() []handler.AggregateReducer { + return []handler.AggregateReducer{ + { + Aggregate: instance.AggregateType, + EventReducers: []handler.EventReducer{ + { + Event: instance.IDPConfigAddedEventType, + Reduce: p.reduceIDPRelationalAdded, + }, + { + Event: instance.IDPConfigChangedEventType, + Reduce: p.reduceIDPRelationalChanged, + }, + { + Event: instance.IDPConfigDeactivatedEventType, + Reduce: p.reduceIDRelationalPDeactivated, + }, + { + Event: instance.IDPConfigReactivatedEventType, + Reduce: p.reduceIDPRelationalReactivated, + }, + { + Event: instance.IDPConfigRemovedEventType, + Reduce: p.reduceIDPRelationalRemoved, + }, + { + Event: instance.IDPOIDCConfigAddedEventType, + Reduce: p.reduceOIDCRelationalConfigAdded, + }, + { + Event: instance.IDPOIDCConfigChangedEventType, + Reduce: p.reduceOIDCRelationalConfigChanged, + }, + { + Event: instance.IDPJWTConfigAddedEventType, + Reduce: p.reduceJWTRelationalConfigAdded, + }, + { + Event: instance.IDPJWTConfigChangedEventType, + Reduce: p.reduceJWTRelationalConfigChanged, + }, + { + Event: instance.OAuthIDPAddedEventType, + Reduce: p.reduceOAuthIDPRelationalAdded, + }, + { + Event: instance.OAuthIDPChangedEventType, + Reduce: p.reduceOAuthIDPRelationalChanged, + }, + { + Event: instance.OIDCIDPAddedEventType, + Reduce: p.reduceOIDCIDPRelationalAdded, + }, + { + Event: instance.OIDCIDPChangedEventType, + Reduce: p.reduceOIDCIDPRelationalChanged, + }, + { + Event: instance.OIDCIDPMigratedAzureADEventType, + Reduce: p.reduceOIDCIDPRelationalMigratedAzureAD, + }, + { + Event: instance.OIDCIDPMigratedGoogleEventType, + Reduce: p.reduceOIDCIDPRelationalMigratedGoogle, + }, + { + Event: instance.JWTIDPAddedEventType, + Reduce: p.reduceJWTIDPRelationalAdded, + }, + { + Event: instance.JWTIDPChangedEventType, + Reduce: p.reduceJWTIDPRelationalChanged, + }, + { + Event: instance.AzureADIDPAddedEventType, + Reduce: p.reduceAzureADIDPRelationalAdded, + }, + { + Event: instance.AzureADIDPChangedEventType, + Reduce: p.reduceAzureADIDPRelationalChanged, + }, + { + Event: instance.GitHubIDPAddedEventType, + Reduce: p.reduceGitHubIDPRelationalAdded, + }, + { + Event: instance.GitHubIDPChangedEventType, + Reduce: p.reduceGitHubIDPRelationalChanged, + }, + { + Event: instance.GitHubEnterpriseIDPAddedEventType, + Reduce: p.reduceGitHubEnterpriseIDPRelationalAdded, + }, + { + Event: instance.GitHubEnterpriseIDPChangedEventType, + Reduce: p.reduceGitHubEnterpriseIDPRelationalChanged, + }, + { + Event: instance.GitLabIDPAddedEventType, + Reduce: p.reduceGitLabIDPRelationalAdded, + }, + { + Event: instance.GitLabIDPChangedEventType, + Reduce: p.reduceGitLabIDPRelationalChanged, + }, + { + Event: instance.GitLabSelfHostedIDPAddedEventType, + Reduce: p.reduceGitLabSelfHostedIDPRelationalAdded, + }, + { + Event: instance.GitLabSelfHostedIDPChangedEventType, + Reduce: p.reduceGitLabSelfHostedIDPRelationalChanged, + }, + { + Event: instance.GoogleIDPAddedEventType, + Reduce: p.reduceGoogleIDPRelationalAdded, + }, + { + Event: instance.GoogleIDPChangedEventType, + Reduce: p.reduceGoogleIDPRelationalChanged, + }, + { + Event: instance.LDAPIDPAddedEventType, + Reduce: p.reduceLDAPIDPAdded, + }, + { + 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.IDPRemovedEventType, + Reduce: p.reduceIDPRemoved, + }, + }, + }, + { + Aggregate: org.AggregateType, + EventReducers: []handler.EventReducer{ + { + Event: org.IDPConfigAddedEventType, + Reduce: p.reduceIDPRelationalAdded, + }, + { + Event: org.IDPConfigChangedEventType, + Reduce: p.reduceIDPRelationalChanged, + }, + { + Event: org.IDPConfigDeactivatedEventType, + Reduce: p.reduceIDRelationalPDeactivated, + }, + { + Event: org.IDPConfigReactivatedEventType, + Reduce: p.reduceIDPRelationalReactivated, + }, + { + Event: org.IDPConfigRemovedEventType, + Reduce: p.reduceIDPRelationalRemoved, + }, + { + Event: org.IDPOIDCConfigAddedEventType, + Reduce: p.reduceOIDCRelationalConfigAdded, + }, + { + Event: org.IDPOIDCConfigChangedEventType, + Reduce: p.reduceOIDCRelationalConfigChanged, + }, + { + Event: org.IDPJWTConfigAddedEventType, + Reduce: p.reduceJWTRelationalConfigAdded, + }, + { + Event: org.IDPJWTConfigChangedEventType, + Reduce: p.reduceJWTRelationalConfigChanged, + }, + { + Event: org.OAuthIDPAddedEventType, + Reduce: p.reduceOAuthIDPRelationalAdded, + }, + { + Event: org.OAuthIDPChangedEventType, + Reduce: p.reduceOAuthIDPRelationalChanged, + }, + { + Event: org.OIDCIDPAddedEventType, + Reduce: p.reduceOIDCIDPRelationalAdded, + }, + { + Event: org.OIDCIDPChangedEventType, + Reduce: p.reduceOIDCIDPRelationalChanged, + }, + { + Event: org.OIDCIDPMigratedAzureADEventType, + Reduce: p.reduceOIDCIDPRelationalMigratedAzureAD, + }, + { + Event: org.OIDCIDPMigratedGoogleEventType, + Reduce: p.reduceOIDCIDPRelationalMigratedGoogle, + }, + { + Event: org.JWTIDPAddedEventType, + Reduce: p.reduceJWTIDPRelationalAdded, + }, + { + Event: org.JWTIDPChangedEventType, + Reduce: p.reduceJWTIDPRelationalChanged, + }, + { + Event: org.AzureADIDPAddedEventType, + Reduce: p.reduceAzureADIDPRelationalAdded, + }, + { + Event: org.AzureADIDPChangedEventType, + Reduce: p.reduceAzureADIDPRelationalChanged, + }, + { + Event: org.GitHubIDPAddedEventType, + Reduce: p.reduceGitHubIDPRelationalAdded, + }, + { + Event: org.GitHubIDPChangedEventType, + Reduce: p.reduceGitHubIDPRelationalChanged, + }, + { + Event: org.GitHubEnterpriseIDPAddedEventType, + Reduce: p.reduceGitHubEnterpriseIDPRelationalAdded, + }, + { + Event: org.GitHubEnterpriseIDPChangedEventType, + Reduce: p.reduceGitHubEnterpriseIDPRelationalChanged, + }, + { + Event: org.GitLabIDPAddedEventType, + Reduce: p.reduceGitLabIDPRelationalAdded, + }, + { + Event: org.GitLabIDPChangedEventType, + Reduce: p.reduceGitLabIDPRelationalChanged, + }, + { + Event: org.GitLabSelfHostedIDPAddedEventType, + Reduce: p.reduceGitLabSelfHostedIDPRelationalAdded, + }, + { + Event: org.GitLabSelfHostedIDPChangedEventType, + Reduce: p.reduceGitLabSelfHostedIDPRelationalChanged, + }, + { + Event: org.GoogleIDPAddedEventType, + Reduce: p.reduceGoogleIDPRelationalAdded, + }, + { + Event: org.GoogleIDPChangedEventType, + Reduce: p.reduceGoogleIDPRelationalChanged, + }, + { + Event: org.LDAPIDPAddedEventType, + Reduce: p.reduceLDAPIDPAdded, + }, + { + 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.IDPRemovedEventType, + Reduce: p.reduceIDPRemoved, + }, + }, + }, + } +} + +func (p *idpTemplateRelationalProjection) reduceIDPRelationalAdded(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var idpEvent idpconfig.IDPConfigAddedEvent + switch e := event.(type) { + case *org.IDPConfigAddedEvent: + idpEvent = e.IDPConfigAddedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + case *instance.IDPConfigAddedEvent: + idpEvent = e.IDPConfigAddedEvent + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-YcUdQ", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPConfigAddedEventType, instance.IDPConfigAddedEventType}) + } + + return handler.NewCreateStatement( + &idpEvent, + []handler.Column{ + handler.NewCol(IDPInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCol(IDPRelationalOrgIdCol, orgId), + handler.NewCol(IDPIDCol, idpEvent.ConfigID), + handler.NewCol(IDPStateCol, domain.IDPStateActive.String()), + handler.NewCol(IDPNameCol, idpEvent.Name), + handler.NewCol(IDPTemplateTypeCol, nil), + handler.NewCol(IDPRelationalAutoRegisterCol, idpEvent.AutoRegister), + handler.NewCol(IDPRelationalAllowCreationCol, true), + handler.NewCol(IDPRelationalAllowAutoUpdateCol, false), + handler.NewCol(IDPRelationalAllowLinkingCol, true), + handler.NewCol(IDPRelationalAllowAutoLinkingCol, nil), + handler.NewCol(IDPStylingTypeCol, idpEvent.StylingType), + handler.NewCol(CreatedAt, idpEvent.CreationDate()), + handler.NewCol(UpdatedAt, idpEvent.CreationDate()), + }, + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceIDPRelationalChanged(event eventstore.Event) (*handler.Statement, error) { + var orgCond handler.Condition + var idpEvent idpconfig.IDPConfigChangedEvent + switch e := event.(type) { + case *org.IDPConfigChangedEvent: + idpEvent = e.IDPConfigChangedEvent + orgCond = handler.NewCond(IDPRelationalOrgId, idpEvent.Aggregate().ResourceOwner) + case *instance.IDPConfigChangedEvent: + idpEvent = e.IDPConfigChangedEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-YVvJD", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPConfigChangedEventType, instance.IDPConfigChangedEventType}) + } + + columns := make([]handler.Column, 0, 4) + if idpEvent.Name != nil { + columns = append(columns, handler.NewCol(IDPNameCol, *idpEvent.Name)) + } + if idpEvent.StylingType != nil { + columns = append(columns, handler.NewCol(IDPStylingTypeCol, *idpEvent.StylingType)) + } + if idpEvent.AutoRegister != nil { + columns = append(columns, handler.NewCol(IDPRelationalAutoRegisterCol, *idpEvent.AutoRegister)) + } + if len(columns) == 0 { + return handler.NewNoOpStatement(&idpEvent), nil + } + + columns = append(columns, handler.NewCol(UpdatedAt, idpEvent.CreationDate())) + + return handler.NewUpdateStatement( + &idpEvent, + columns, + []handler.Condition{ + handler.NewCond(IDPIDCol, idpEvent.ConfigID), + handler.NewCond(IDPInstanceIDCol, idpEvent.Aggregate().InstanceID), + orgCond, + }, + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceIDRelationalPDeactivated(event eventstore.Event) (*handler.Statement, error) { + var orgCond handler.Condition + var idpEvent idpconfig.IDPConfigDeactivatedEvent + switch e := event.(type) { + case *org.IDPConfigDeactivatedEvent: + idpEvent = e.IDPConfigDeactivatedEvent + orgCond = handler.NewCond(IDPRelationalOrgId, idpEvent.Aggregate().ResourceOwner) + case *instance.IDPConfigDeactivatedEvent: + idpEvent = e.IDPConfigDeactivatedEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Y4O5l", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPConfigDeactivatedEventType, instance.IDPConfigDeactivatedEventType}) + } + + return handler.NewUpdateStatement( + &idpEvent, + []handler.Column{ + handler.NewCol(IDPStateCol, domain.IDPStateInactive), + handler.NewCol(UpdatedAt, idpEvent.CreationDate()), + }, + []handler.Condition{ + handler.NewCond(IDPIDCol, idpEvent.ConfigID), + handler.NewCond(IDPInstanceIDCol, idpEvent.Aggregate().InstanceID), + orgCond, + }, + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceIDPRelationalReactivated(event eventstore.Event) (*handler.Statement, error) { + var orgCond handler.Condition + var idpEvent idpconfig.IDPConfigReactivatedEvent + switch e := event.(type) { + case *org.IDPConfigReactivatedEvent: + idpEvent = e.IDPConfigReactivatedEvent + orgCond = handler.NewCond(IDPRelationalOrgId, idpEvent.Aggregate().ResourceOwner) + case *instance.IDPConfigReactivatedEvent: + idpEvent = e.IDPConfigReactivatedEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Y8QyS", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPConfigReactivatedEventType, instance.IDPConfigReactivatedEventType}) + } + + return handler.NewUpdateStatement( + &idpEvent, + []handler.Column{ + handler.NewCol(IDPStateCol, domain.IDPStateActive.String()), + handler.NewCol(UpdatedAt, idpEvent.CreationDate()), + }, + []handler.Condition{ + handler.NewCond(IDPIDCol, idpEvent.ConfigID), + handler.NewCond(IDPInstanceIDCol, idpEvent.Aggregate().InstanceID), + orgCond, + }, + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceIDPRelationalRemoved(event eventstore.Event) (*handler.Statement, error) { + var orgCond handler.Condition + var idpEvent idpconfig.IDPConfigRemovedEvent + switch e := event.(type) { + case *org.IDPConfigRemovedEvent: + idpEvent = e.IDPConfigRemovedEvent + orgCond = handler.NewCond(IDPRelationalOrgId, idpEvent.Aggregate().ResourceOwner) + case *instance.IDPConfigRemovedEvent: + idpEvent = e.IDPConfigRemovedEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Y4zy8", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPConfigRemovedEventType, instance.IDPConfigRemovedEventType}) + } + + return handler.NewDeleteStatement( + &idpEvent, + []handler.Condition{ + handler.NewCond(IDPIDCol, idpEvent.ConfigID), + handler.NewCond(IDPInstanceIDCol, idpEvent.Aggregate().InstanceID), + orgCond, + }, + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceOIDCRelationalConfigAdded(event eventstore.Event) (*handler.Statement, error) { + var orgCond handler.Condition + var idpEvent idpconfig.OIDCConfigAddedEvent + switch e := event.(type) { + case *org.IDPOIDCConfigAddedEvent: + idpEvent = e.OIDCConfigAddedEvent + orgCond = handler.NewCond(IDPRelationalOrgId, idpEvent.Aggregate().ResourceOwner) + case *instance.IDPOIDCConfigAddedEvent: + idpEvent = e.OIDCConfigAddedEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-YFuAA", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPOIDCConfigAddedEventType, instance.IDPOIDCConfigAddedEventType}) + } + + payloadJSON, err := json.Marshal(idpEvent) + if err != nil { + return nil, err + } + + return handler.NewUpdateStatement( + &idpEvent, + []handler.Column{ + handler.NewCol(IDPRelationalPayloadCol, payloadJSON), + handler.NewCol(IDPTypeCol, domain.IDPTypeOIDC), + handler.NewCol(UpdatedAt, idpEvent.CreatedAt()), + }, + []handler.Condition{ + handler.NewCond(IDPIDCol, idpEvent.IDPConfigID), + handler.NewCond(IDPInstanceIDCol, idpEvent.Aggregate().InstanceID), + orgCond, + }, + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceOIDCRelationalConfigChanged(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var orgCond handler.Condition + var idpEvent idpconfig.OIDCConfigChangedEvent + switch e := event.(type) { + case *org.IDPOIDCConfigChangedEvent: + idpEvent = e.OIDCConfigChangedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + orgCond = handler.NewCond(IDPRelationalOrgId, orgId) + case *instance.IDPOIDCConfigChangedEvent: + idpEvent = e.OIDCConfigChangedEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Y2IVI", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPOIDCConfigChangedEventType, instance.IDPOIDCConfigChangedEventType}) + } + + return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInternal(nil, "HANDL-sh6Lp", "unable to cast to tx executer") + } + + oidc, err := p.idpRepo.GetOIDC(ctx, v3_sql.SQLTx(tx), p.idpRepo.IDCondition(idpEvent.IDPConfigID), idpEvent.Agg.InstanceID, orgId) + if err != nil { + return err + } + + if idpEvent.ClientID != nil { + oidc.ClientID = *idpEvent.ClientID + } + if idpEvent.ClientSecret != nil { + oidc.ClientSecret = idpEvent.ClientSecret + } + if idpEvent.Issuer != nil { + oidc.Issuer = *idpEvent.Issuer + } + if idpEvent.AuthorizationEndpoint != nil { + oidc.AuthorizationEndpoint = *idpEvent.AuthorizationEndpoint + } + if idpEvent.TokenEndpoint != nil { + oidc.TokenEndpoint = *idpEvent.TokenEndpoint + } + if idpEvent.Scopes != nil { + oidc.Scopes = idpEvent.Scopes + } + if idpEvent.IDPDisplayNameMapping != nil { + oidc.IDPDisplayNameMapping = domain.OIDCMappingField(*idpEvent.IDPDisplayNameMapping) + } + if idpEvent.UserNameMapping != nil { + oidc.UserNameMapping = domain.OIDCMappingField(*idpEvent.UserNameMapping) + } + + payloadJSON, err := json.Marshal(idpEvent) + if err != nil { + return err + } + + return handler.NewUpdateStatement( + &idpEvent, + []handler.Column{ + handler.NewCol(IDPRelationalPayloadCol, payloadJSON), + handler.NewCol(IDPTypeCol, domain.IDPTypeOIDC), + handler.NewCol(UpdatedAt, idpEvent.CreationDate()), + }, + []handler.Condition{ + handler.NewCond(IDPIDCol, idpEvent.IDPConfigID), + handler.NewCond(IDPInstanceIDCol, idpEvent.Aggregate().InstanceID), + orgCond, + }, + ).Execute(ctx, ex, projectionName) + }), nil +} + +func (p *idpTemplateRelationalProjection) reduceJWTRelationalConfigAdded(event eventstore.Event) (*handler.Statement, error) { + var orgCond handler.Condition + var idpEvent idpconfig.JWTConfigAddedEvent + switch e := event.(type) { + case *org.IDPJWTConfigAddedEvent: + idpEvent = e.JWTConfigAddedEvent + orgCond = handler.NewCond(IDPRelationalOrgId, idpEvent.Aggregate().ResourceOwner) + case *instance.IDPJWTConfigAddedEvent: + idpEvent = e.JWTConfigAddedEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-YvPdb", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPJWTConfigAddedEventType, instance.IDPJWTConfigAddedEventType}) + } + + payloadJSON, err := json.Marshal(idpEvent) + if err != nil { + return nil, err + } + + return handler.NewUpdateStatement( + &idpEvent, + []handler.Column{ + handler.NewCol(IDPRelationalPayloadCol, payloadJSON), + handler.NewCol(IDPTypeCol, domain.IDPTypeJWT), + handler.NewCol(UpdatedAt, idpEvent.CreatedAt()), + }, + []handler.Condition{ + handler.NewCond(IDPIDCol, idpEvent.IDPConfigID), + handler.NewCond(IDPInstanceIDCol, idpEvent.Aggregate().InstanceID), + orgCond, + }, + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceJWTRelationalConfigChanged(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var orgCond handler.Condition + var idpEvent idpconfig.JWTConfigChangedEvent + switch e := event.(type) { + case *org.IDPJWTConfigChangedEvent: + idpEvent = e.JWTConfigChangedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + orgCond = handler.NewCond(IDPRelationalOrgId, orgId) + case *instance.IDPJWTConfigChangedEvent: + idpEvent = e.JWTConfigChangedEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Y2IVI", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPJWTConfigChangedEventType, instance.IDPJWTConfigChangedEventType}) + } + + return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInternal(nil, "HANDL-sh6Lp", "unable to cast to tx executer") + } + + jwt, err := p.idpRepo.GetJWT(ctx, v3_sql.SQLTx(tx), p.idpRepo.IDCondition(idpEvent.IDPConfigID), idpEvent.Agg.InstanceID, orgId) + if err != nil { + return err + } + + if idpEvent.JWTEndpoint != nil { + jwt.JWTEndpoint = *idpEvent.JWTEndpoint + } + if idpEvent.Issuer != nil { + jwt.Issuer = *idpEvent.Issuer + } + if idpEvent.KeysEndpoint != nil { + jwt.KeysEndpoint = *idpEvent.KeysEndpoint + } + if idpEvent.HeaderName != nil { + jwt.HeaderName = *idpEvent.HeaderName + } + + payloadJSON, err := json.Marshal(idpEvent) + if err != nil { + return err + } + + return handler.NewUpdateStatement( + &idpEvent, + []handler.Column{ + handler.NewCol(IDPRelationalPayloadCol, payloadJSON), + handler.NewCol(IDPTypeCol, domain.IDPTypeJWT), + handler.NewCol(UpdatedAt, idpEvent.CreationDate()), + }, + []handler.Condition{ + handler.NewCond(IDPIDCol, idpEvent.IDPConfigID), + handler.NewCond(IDPInstanceIDCol, idpEvent.Aggregate().InstanceID), + orgCond, + }, + ).Execute(ctx, ex, projectionName) + }), nil +} + +func (p *idpTemplateRelationalProjection) reduceOAuthIDPRelationalAdded(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var idpEvent idp.OAuthIDPAddedEvent + switch e := event.(type) { + case *org.OAuthIDPAddedEvent: + idpEvent = e.OAuthIDPAddedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + case *instance.OAuthIDPAddedEvent: + idpEvent = e.OAuthIDPAddedEvent + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Yap9ihb", "reduce.wrong.event.type %v", []eventstore.EventType{org.OAuthIDPAddedEventType, instance.OAuthIDPAddedEventType}) + } + + oauth := domain.OAuth{ + ClientID: idpEvent.ClientID, + ClientSecret: idpEvent.ClientSecret, + AuthorizationEndpoint: idpEvent.AuthorizationEndpoint, + TokenEndpoint: idpEvent.TokenEndpoint, + UserEndpoint: idpEvent.UserEndpoint, + Scopes: idpEvent.Scopes, + IDAttribute: idpEvent.IDAttribute, + UsePKCE: idpEvent.UsePKCE, + } + + payloadJSON, err := json.Marshal(oauth) + if err != nil { + return nil, err + } + + return handler.NewCreateStatement( + &idpEvent, + []handler.Column{ + handler.NewCol(IDPTemplateIDCol, idpEvent.ID), + handler.NewCol(IDPRelationalOrgId, orgId), + handler.NewCol(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCol(IDPTemplateStateCol, domain.IDPStateActive.String()), + handler.NewCol(IDPTemplateNameCol, idpEvent.Name), + handler.NewCol(IDPTemplateTypeCol, domain.IDPTypeOAuth), + handler.NewCol(IDPRelationalAllowCreationCol, idpEvent.IsCreationAllowed), + handler.NewCol(IDPRelationalAllowLinkingCol, idpEvent.IsLinkingAllowed), + handler.NewCol(IDPRelationalAllowAutoCreationCol, idpEvent.IsAutoCreation), + handler.NewCol(IDPRelationalAllowAutoUpdateCol, idpEvent.IsAutoUpdate), + handler.NewCol(IDPRelationalAllowAutoLinkingCol, func() any { + if idpEvent.AutoLinkingOption == internal_domain.AutoLinkingOptionUnspecified { + return nil + } + return domain.IDPAutoLinkingField(idpEvent.AutoLinkingOption) + }()), + handler.NewCol(IDPRelationalPayloadCol, payloadJSON), + handler.NewCol(CreatedAt, idpEvent.CreationDate()), + handler.NewCol(UpdatedAt, idpEvent.CreationDate()), + }, + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceOAuthIDPRelationalChanged(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var orgCond handler.Condition + var idpEvent idp.OAuthIDPChangedEvent + switch e := event.(type) { + case *org.OAuthIDPChangedEvent: + idpEvent = e.OAuthIDPChangedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + orgCond = handler.NewCond(IDPRelationalOrgId, orgId) + case *instance.OAuthIDPChangedEvent: + idpEvent = e.OAuthIDPChangedEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-K1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.OAuthIDPChangedEventType, instance.OAuthIDPChangedEventType}) + } + + return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInternal(nil, "HANDL-HX6ed", "unable to cast to tx executer") + } + oauth, err := p.idpRepo.GetOAuth(ctx, v3_sql.SQLTx(tx), p.idpRepo.IDCondition(idpEvent.ID), idpEvent.Agg.InstanceID, orgId) + if err != nil { + return err + } + + columns := reduceIDPRelationalChangedTemplateColumns(idpEvent.Name, idpEvent.OptionChanges) + + payload := &oauth.OAuth + payloadChanged := reduceOAuthIDPRelationalChangedColumns(payload, &idpEvent) + if payloadChanged { + payloadJSON, err := json.Marshal(payload) + if err != nil { + return err + } + columns = append(columns, handler.NewCol(IDPRelationalPayloadCol, payloadJSON)) + } + + columns = append(columns, handler.NewCol(UpdatedAt, idpEvent.CreationDate())) + + return handler.NewUpdateStatement( + &idpEvent, + columns, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + orgCond, + }, + ).Execute(ctx, ex, projectionName) + + }), nil +} + +func (p *idpTemplateRelationalProjection) reduceOIDCIDPRelationalAdded(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var idpEvent idp.OIDCIDPAddedEvent + switch e := event.(type) { + case *org.OIDCIDPAddedEvent: + idpEvent = e.OIDCIDPAddedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + case *instance.OIDCIDPAddedEvent: + idpEvent = e.OIDCIDPAddedEvent + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Ys02m1", "reduce.wrong.event.type %v", []eventstore.EventType{org.OIDCIDPAddedEventType, instance.OIDCIDPAddedEventType}) + } + + payloadJSON, err := json.Marshal(idpEvent) + if err != nil { + return nil, err + } + + return handler.NewCreateStatement( + &idpEvent, + []handler.Column{ + handler.NewCol(IDPTemplateIDCol, idpEvent.ID), + handler.NewCol(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCol(IDPRelationalOrgId, orgId), + handler.NewCol(IDPTemplateStateCol, domain.IDPStateActive), + handler.NewCol(IDPTemplateNameCol, idpEvent.Name), + handler.NewCol(IDPTemplateTypeCol, domain.IDPTypeOIDC), + handler.NewCol(IDPRelationalAllowCreationCol, idpEvent.IsCreationAllowed), + handler.NewCol(IDPRelationalAllowLinkingCol, idpEvent.IsLinkingAllowed), + handler.NewCol(IDPRelationalAllowAutoCreationCol, idpEvent.IsAutoCreation), + handler.NewCol(IDPRelationalAllowAutoUpdateCol, idpEvent.IsAutoUpdate), + handler.NewCol(IDPRelationalAllowAutoLinkingCol, func() any { + if idpEvent.AutoLinkingOption == internal_domain.AutoLinkingOptionUnspecified { + return nil + } + return domain.IDPAutoLinkingField(idpEvent.AutoLinkingOption) + }()), + handler.NewCol(IDPRelationalPayloadCol, payloadJSON), + handler.NewCol(CreatedAt, idpEvent.CreationDate()), + handler.NewCol(UpdatedAt, idpEvent.CreationDate()), + }, + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceOIDCIDPRelationalChanged(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var orgCond handler.Condition + var idpEvent idp.OIDCIDPChangedEvent + switch e := event.(type) { + case *org.OIDCIDPChangedEvent: + idpEvent = e.OIDCIDPChangedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + orgCond = handler.NewCond(IDPRelationalOrgId, orgId) + case *instance.OIDCIDPChangedEvent: + idpEvent = e.OIDCIDPChangedEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Y1K82ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.OIDCIDPChangedEventType, instance.OIDCIDPChangedEventType}) + } + + return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInternal(nil, "HANDL-L8CQt", "unable to cast to tx executer") + } + oidc, err := p.idpRepo.GetOIDC(ctx, v3_sql.SQLTx(tx), p.idpRepo.IDCondition(idpEvent.ID), idpEvent.Agg.InstanceID, orgId) + if err != nil { + return err + } + + columns := reduceIDPRelationalChangedTemplateColumns(idpEvent.Name, idpEvent.OptionChanges) + + payload := &oidc.OIDC + payloadChanged := reduceOIDCIDPRelationalChangedColumns(payload, &idpEvent) + if payloadChanged { + payloadJSON, err := json.Marshal(payload) + if err != nil { + return err + } + columns = append(columns, handler.NewCol(IDPRelationalPayloadCol, payloadJSON)) + } + + columns = append(columns, handler.NewCol(UpdatedAt, idpEvent.CreationDate())) + + return handler.NewUpdateStatement( + &idpEvent, + columns, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + orgCond, + }, + ).Execute(ctx, ex, projectionName) + }), nil +} + +func (p *idpTemplateRelationalProjection) reduceOIDCIDPRelationalMigratedAzureAD(event eventstore.Event) (*handler.Statement, error) { + var orgCond handler.Condition + var idpEvent idp.OIDCIDPMigratedAzureADEvent + switch e := event.(type) { + case *org.OIDCIDPMigratedAzureADEvent: + idpEvent = e.OIDCIDPMigratedAzureADEvent + orgCond = handler.NewCond(IDPRelationalOrgId, idpEvent.Aggregate().ResourceOwner) + case *instance.OIDCIDPMigratedAzureADEvent: + idpEvent = e.OIDCIDPMigratedAzureADEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Yb582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.OIDCIDPMigratedAzureADEventType, instance.OIDCIDPMigratedAzureADEventType}) + } + + azureTenant, err := domain.AzureTenantTypeString(idpEvent.Tenant) + if err != nil { + return nil, err + } + + azure := domain.Azure{ + ClientID: idpEvent.ClientID, + ClientSecret: idpEvent.ClientSecret, + Scopes: idpEvent.Scopes, + Tenant: azureTenant, + IsEmailVerified: idpEvent.IsEmailVerified, + } + + payloadJSON, err := json.Marshal(azure) + if err != nil { + return nil, err + } + + return handler.NewUpdateStatement( + &idpEvent, + []handler.Column{ + handler.NewCol(IDPTemplateNameCol, idpEvent.Name), + handler.NewCol(IDPTemplateTypeCol, domain.IDPTypeAzure), + handler.NewCol(IDPRelationalAllowCreationCol, idpEvent.IsCreationAllowed), + handler.NewCol(IDPRelationalAllowLinkingCol, idpEvent.IsLinkingAllowed), + handler.NewCol(IDPRelationalAllowAutoCreationCol, idpEvent.IsAutoCreation), + handler.NewCol(IDPRelationalAllowAutoUpdateCol, idpEvent.IsAutoUpdate), + handler.NewCol(IDPRelationalAllowAutoLinkingCol, func() any { + if idpEvent.AutoLinkingOption == internal_domain.AutoLinkingOptionUnspecified { + return nil + } + return domain.IDPAutoLinkingField(idpEvent.AutoLinkingOption) + }()), + handler.NewCol(IDPRelationalPayloadCol, payloadJSON), + handler.NewCol(UpdatedAt, idpEvent.CreationDate()), + }, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + orgCond, + }, + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceOIDCIDPRelationalMigratedGoogle(event eventstore.Event) (*handler.Statement, error) { + var orgCond handler.Condition + var idpEvent idp.OIDCIDPMigratedGoogleEvent + switch e := event.(type) { + case *org.OIDCIDPMigratedGoogleEvent: + idpEvent = e.OIDCIDPMigratedGoogleEvent + orgCond = handler.NewCond(IDPRelationalOrgId, idpEvent.Aggregate().ResourceOwner) + case *instance.OIDCIDPMigratedGoogleEvent: + idpEvent = e.OIDCIDPMigratedGoogleEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Y1502hk", "reduce.wrong.event.type %v", []eventstore.EventType{org.OIDCIDPMigratedGoogleEventType, instance.OIDCIDPMigratedGoogleEventType}) + } + + google := domain.Google{ + ClientID: idpEvent.ClientID, + ClientSecret: idpEvent.ClientSecret, + Scopes: idpEvent.Scopes, + } + + payloadJSON, err := json.Marshal(google) + if err != nil { + return nil, err + } + + return handler.NewUpdateStatement( + &idpEvent, + []handler.Column{ + handler.NewCol(IDPTemplateNameCol, idpEvent.Name), + handler.NewCol(IDPTemplateTypeCol, domain.IDPTypeGoogle), + handler.NewCol(IDPRelationalAllowCreationCol, idpEvent.IsCreationAllowed), + handler.NewCol(IDPRelationalAllowLinkingCol, idpEvent.IsLinkingAllowed), + handler.NewCol(IDPRelationalAllowAutoCreationCol, idpEvent.IsAutoCreation), + handler.NewCol(IDPRelationalAllowAutoUpdateCol, idpEvent.IsAutoUpdate), + handler.NewCol(IDPRelationalAllowAutoLinkingCol, func() any { + if idpEvent.AutoLinkingOption == internal_domain.AutoLinkingOptionUnspecified { + return nil + } + return domain.IDPAutoLinkingField(idpEvent.AutoLinkingOption) + }()), + handler.NewCol(IDPRelationalPayloadCol, payloadJSON), + handler.NewCol(UpdatedAt, idpEvent.CreationDate()), + }, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + orgCond, + }, + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceJWTIDPRelationalAdded(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var idpEvent idp.JWTIDPAddedEvent + switch e := event.(type) { + case *org.JWTIDPAddedEvent: + idpEvent = e.JWTIDPAddedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + case *instance.JWTIDPAddedEvent: + idpEvent = e.JWTIDPAddedEvent + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Yopi2s", "reduce.wrong.event.type %v", []eventstore.EventType{org.JWTIDPAddedEventType, instance.JWTIDPAddedEventType}) + } + + jwt := domain.JWT{ + JWTEndpoint: idpEvent.JWTEndpoint, + Issuer: idpEvent.Issuer, + KeysEndpoint: idpEvent.KeysEndpoint, + HeaderName: idpEvent.HeaderName, + } + + payloadJSON, err := json.Marshal(jwt) + if err != nil { + return nil, err + } + + return handler.NewCreateStatement( + &idpEvent, + []handler.Column{ + handler.NewCol(IDPTemplateIDCol, idpEvent.ID), + handler.NewCol(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCol(IDPRelationalOrgId, orgId), + handler.NewCol(IDPTemplateNameCol, idpEvent.Name), + handler.NewCol(IDPTemplateStateCol, domain.IDPStateActive.String()), + handler.NewCol(IDPTemplateTypeCol, domain.IDPTypeJWT), + handler.NewCol(IDPRelationalAllowCreationCol, idpEvent.IsCreationAllowed), + handler.NewCol(IDPRelationalAllowLinkingCol, idpEvent.IsLinkingAllowed), + handler.NewCol(IDPRelationalAllowAutoCreationCol, idpEvent.IsAutoCreation), + handler.NewCol(IDPRelationalAllowAutoUpdateCol, idpEvent.IsAutoUpdate), + handler.NewCol(IDPRelationalAllowAutoLinkingCol, func() any { + if idpEvent.AutoLinkingOption == internal_domain.AutoLinkingOptionUnspecified { + return nil + } + return domain.IDPAutoLinkingField(idpEvent.AutoLinkingOption) + }()), + handler.NewCol(IDPRelationalPayloadCol, payloadJSON), + handler.NewCol(CreatedAt, idpEvent.CreationDate()), + handler.NewCol(UpdatedAt, idpEvent.CreationDate()), + }, + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceJWTIDPRelationalChanged(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var orgCond handler.Condition + var idpEvent idp.JWTIDPChangedEvent + switch e := event.(type) { + case *org.JWTIDPChangedEvent: + idpEvent = e.JWTIDPChangedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + orgCond = handler.NewCond(IDPRelationalOrgId, orgId) + case *instance.JWTIDPChangedEvent: + idpEvent = e.JWTIDPChangedEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-H15j2il", "reduce.wrong.event.type %v", []eventstore.EventType{org.JWTIDPChangedEventType, instance.JWTIDPChangedEventType}) + } + + return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInternal(nil, "HANDL-HX6ed", "unable to cast to tx executer") + } + jwt, err := p.idpRepo.GetJWT(ctx, v3_sql.SQLTx(tx), p.idpRepo.IDCondition(idpEvent.ID), idpEvent.Agg.InstanceID, orgId) + if err != nil { + return err + } + + columns := reduceIDPRelationalChangedTemplateColumns(idpEvent.Name, idpEvent.OptionChanges) + + payload := &jwt.JWT + payloadChanged := reduceJWTIDPRelationalChangedColumns(payload, &idpEvent) + if payloadChanged { + payloadJSON, err := json.Marshal(payload) + if err != nil { + return err + } + columns = append(columns, handler.NewCol(IDPRelationalPayloadCol, payloadJSON)) + } + + columns = append(columns, handler.NewCol(UpdatedAt, idpEvent.CreationDate())) + + return handler.NewUpdateStatement( + &idpEvent, + columns, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + orgCond, + }, + ).Execute(ctx, ex, projectionName) + }), nil +} + +func (p *idpTemplateRelationalProjection) reduceAzureADIDPRelationalAdded(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var idpEvent idp.AzureADIDPAddedEvent + switch e := event.(type) { + case *org.AzureADIDPAddedEvent: + idpEvent = e.AzureADIDPAddedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + case *instance.AzureADIDPAddedEvent: + idpEvent = e.AzureADIDPAddedEvent + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Y9a022b", "reduce.wrong.event.type %v", []eventstore.EventType{org.AzureADIDPAddedEventType, instance.AzureADIDPAddedEventType}) + } + + azureTenant, err := domain.AzureTenantTypeString(idpEvent.Tenant) + if err != nil { + return nil, err + } + + azure := domain.Azure{ + ClientID: idpEvent.ClientID, + ClientSecret: idpEvent.ClientSecret, + Scopes: idpEvent.Scopes, + Tenant: azureTenant, + IsEmailVerified: idpEvent.IsEmailVerified, + } + + payloadJSON, err := json.Marshal(azure) + if err != nil { + return nil, err + } + + return handler.NewCreateStatement( + &idpEvent, + []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, domain.IDPTypeAzure), + handler.NewCol(IDPTemplateStateCol, 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, func() any { + if idpEvent.AutoLinkingOption == internal_domain.AutoLinkingOptionUnspecified { + return nil + } + return domain.IDPAutoLinkingField(idpEvent.AutoLinkingOption) + }()), + handler.NewCol(IDPRelationalPayloadCol, payloadJSON), + handler.NewCol(CreatedAt, idpEvent.CreationDate()), + handler.NewCol(UpdatedAt, idpEvent.CreationDate()), + }, + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceAzureADIDPRelationalChanged(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var orgCond handler.Condition + var idpEvent idp.AzureADIDPChangedEvent + switch e := event.(type) { + case *org.AzureADIDPChangedEvent: + idpEvent = e.AzureADIDPChangedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + orgCond = handler.NewCond(IDPRelationalOrgId, orgId) + case *instance.AzureADIDPChangedEvent: + idpEvent = e.AzureADIDPChangedEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-YZ5x25s", "reduce.wrong.event.type %v", []eventstore.EventType{org.AzureADIDPChangedEventType, instance.AzureADIDPChangedEventType}) + } + + return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInternal(nil, "HANDL-HX6ed", "unable to cast to tx executer") + } + azure, err := p.idpRepo.GetAzureAD(ctx, v3_sql.SQLTx(tx), p.idpRepo.IDCondition(idpEvent.ID), idpEvent.Agg.InstanceID, orgId) + if err != nil { + return err + } + + columns := reduceIDPRelationalChangedTemplateColumns(idpEvent.Name, idpEvent.OptionChanges) + + payload := &azure.Azure + payloadChanged, err := reduceAzureADIDPRelationalChangedColumns(payload, &idpEvent) + if err != nil { + return err + } + + if payloadChanged { + payloadJSON, err := json.Marshal(payload) + if err != nil { + return err + } + columns = append(columns, handler.NewCol(IDPRelationalPayloadCol, payloadJSON)) + } + + columns = append(columns, handler.NewCol(UpdatedAt, idpEvent.CreationDate())) + + return handler.NewUpdateStatement( + &idpEvent, + columns, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + orgCond, + }, + ).Execute(ctx, ex, projectionName) + + }), nil +} + +func (p *idpTemplateRelationalProjection) reduceGitHubIDPRelationalAdded(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.NewCreateStatement( + &idpEvent, + []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, domain.IDPTypeGitHub), + handler.NewCol(IDPTemplateStateCol, 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, func() any { + if idpEvent.AutoLinkingOption == internal_domain.AutoLinkingOptionUnspecified { + return nil + } + return domain.IDPAutoLinkingField(idpEvent.AutoLinkingOption) + }()), + handler.NewCol(IDPRelationalPayloadCol, payloadJSON), + handler.NewCol(CreatedAt, idpEvent.CreationDate()), + handler.NewCol(UpdatedAt, idpEvent.CreationDate()), + }, + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceGitHubIDPRelationalChanged(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var orgCond handler.Condition + var idpEvent idp.GitHubIDPChangedEvent + switch e := event.(type) { + case *org.GitHubIDPChangedEvent: + idpEvent = e.GitHubIDPChangedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + orgCond = handler.NewCond(IDPRelationalOrgId, orgId) + case *instance.GitHubIDPChangedEvent: + idpEvent = e.GitHubIDPChangedEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + 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, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInternal(nil, "HANDL-HX6ed", "unable to cast to tx executer") + } + github, err := p.idpRepo.GetGithub(ctx, v3_sql.SQLTx(tx), p.idpRepo.IDCondition(idpEvent.ID), idpEvent.Agg.InstanceID, orgId) + if err != nil { + return err + } + + columns := reduceIDPRelationalChangedTemplateColumns(idpEvent.Name, idpEvent.OptionChanges) + + payload := &github.Github + payloadChanged := reduceGitHubIDPRelationalChangedColumns(payload, &idpEvent) + if payloadChanged { + payloadJSON, err := json.Marshal(payload) + if err != nil { + return err + } + columns = append(columns, handler.NewCol(IDPRelationalPayloadCol, payloadJSON)) + } + + columns = append(columns, handler.NewCol(UpdatedAt, idpEvent.CreationDate())) + + return handler.NewUpdateStatement( + &idpEvent, + columns, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + orgCond, + }, + ).Execute(ctx, ex, projectionName) + + }), nil +} + +func (p *idpTemplateRelationalProjection) reduceGitHubEnterpriseIDPRelationalAdded(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.NewCreateStatement( + &idpEvent, + []handler.Column{ + handler.NewCol(IDPTemplateIDCol, idpEvent.ID), + handler.NewCol(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCol(IDPRelationalOrgId, orgId), + handler.NewCol(IDPTemplateStateCol, domain.IDPStateActive.String()), + handler.NewCol(IDPTemplateNameCol, idpEvent.Name), + handler.NewCol(IDPTemplateTypeCol, domain.IDPTypeGitHubEnterprise), + handler.NewCol(IDPRelationalAllowCreationCol, idpEvent.IsCreationAllowed), + handler.NewCol(IDPRelationalAllowLinkingCol, idpEvent.IsLinkingAllowed), + handler.NewCol(IDPRelationalAllowAutoCreationCol, idpEvent.IsAutoCreation), + handler.NewCol(IDPRelationalAllowAutoUpdateCol, idpEvent.IsAutoUpdate), + handler.NewCol(IDPRelationalAllowAutoLinkingCol, func() any { + if idpEvent.AutoLinkingOption == internal_domain.AutoLinkingOptionUnspecified { + return nil + } + return domain.IDPAutoLinkingField(idpEvent.AutoLinkingOption) + }()), + handler.NewCol(IDPRelationalPayloadCol, payloadJSON), + handler.NewCol(CreatedAt, idpEvent.CreationDate()), + handler.NewCol(UpdatedAt, idpEvent.CreationDate()), + }, + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceGitHubEnterpriseIDPRelationalChanged(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var orgCond handler.Condition + var idpEvent idp.GitHubEnterpriseIDPChangedEvent + switch e := event.(type) { + case *org.GitHubEnterpriseIDPChangedEvent: + idpEvent = e.GitHubEnterpriseIDPChangedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + orgCond = handler.NewCond(IDPRelationalOrgId, orgId) + case *instance.GitHubEnterpriseIDPChangedEvent: + idpEvent = e.GitHubEnterpriseIDPChangedEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + 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, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInternal(nil, "HANDL-HX6ed", "unable to cast to tx executer") + } + githubEnterprise, err := p.idpRepo.GetGithubEnterprise(ctx, v3_sql.SQLTx(tx), p.idpRepo.IDCondition(idpEvent.ID), idpEvent.Agg.InstanceID, orgId) + if err != nil { + return err + } + + columns := reduceIDPRelationalChangedTemplateColumns(idpEvent.Name, idpEvent.OptionChanges) + + payload := &githubEnterprise.GithubEnterprise + payloadChanged := reduceGitHubEnterpriseIDPRelationalChangedColumns(payload, &idpEvent) + if payloadChanged { + payloadJSON, err := json.Marshal(payload) + if err != nil { + return err + } + columns = append(columns, handler.NewCol(IDPRelationalPayloadCol, payloadJSON)) + } + + columns = append(columns, handler.NewCol(UpdatedAt, idpEvent.CreationDate())) + + return handler.NewUpdateStatement( + &idpEvent, + columns, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + orgCond, + }, + ).Execute(ctx, ex, projectionName) + + }), nil +} + +func (p *idpTemplateRelationalProjection) reduceGitLabIDPRelationalAdded(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var idpEvent idp.GitLabIDPAddedEvent + switch e := event.(type) { + case *org.GitLabIDPAddedEvent: + idpEvent = e.GitLabIDPAddedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + case *instance.GitLabIDPAddedEvent: + idpEvent = e.GitLabIDPAddedEvent + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Y9a022b", "reduce.wrong.event.type %v", []eventstore.EventType{org.GitLabIDPAddedEventType, instance.GitLabIDPAddedEventType}) + } + + gitlab := domain.Gitlab{ + ClientID: idpEvent.ClientID, + ClientSecret: idpEvent.ClientSecret, + Scopes: idpEvent.Scopes, + } + + payloadJSON, err := json.Marshal(gitlab) + if err != nil { + return nil, err + } + + return handler.NewCreateStatement( + &idpEvent, + []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, domain.IDPTypeGitLab), + handler.NewCol(IDPTemplateStateCol, 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, func() any { + if idpEvent.AutoLinkingOption == internal_domain.AutoLinkingOptionUnspecified { + return nil + } + return domain.IDPAutoLinkingField(idpEvent.AutoLinkingOption) + }()), + handler.NewCol(IDPRelationalPayloadCol, payloadJSON), + handler.NewCol(CreatedAt, idpEvent.CreationDate()), + handler.NewCol(UpdatedAt, idpEvent.CreationDate()), + }, + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceGitLabIDPRelationalChanged(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var orgCond handler.Condition + var idpEvent idp.GitLabIDPChangedEvent + switch e := event.(type) { + case *org.GitLabIDPChangedEvent: + idpEvent = e.GitLabIDPChangedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + orgCond = handler.NewCond(IDPRelationalOrgId, orgId) + case *instance.GitLabIDPChangedEvent: + idpEvent = e.GitLabIDPChangedEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-mT5827b", "reduce.wrong.event.type %v", []eventstore.EventType{org.GitLabIDPChangedEventType, instance.GitLabIDPChangedEventType}) + } + + return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInternal(nil, "HANDL-HX6ed", "unable to cast to tx executer") + } + gitlab, err := p.idpRepo.GetGitlab(ctx, v3_sql.SQLTx(tx), p.idpRepo.IDCondition(idpEvent.ID), idpEvent.Agg.InstanceID, orgId) + if err != nil { + return err + } + + columns := reduceIDPRelationalChangedTemplateColumns(idpEvent.Name, idpEvent.OptionChanges) + + payload := &gitlab.Gitlab + payloadChanged := reduceGitLabIDPRelationalChangedColumns(payload, &idpEvent) + if payloadChanged { + payloadJSON, err := json.Marshal(payload) + if err != nil { + return err + } + columns = append(columns, handler.NewCol(IDPRelationalPayloadCol, payloadJSON)) + } + + columns = append(columns, handler.NewCol(UpdatedAt, idpEvent.CreationDate())) + + return handler.NewUpdateStatement( + &idpEvent, + columns, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + orgCond, + }, + ).Execute(ctx, ex, projectionName) + + }), nil +} + +func (p *idpTemplateRelationalProjection) reduceGitLabSelfHostedIDPRelationalAdded(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var idpEvent idp.GitLabSelfHostedIDPAddedEvent + switch e := event.(type) { + case *org.GitLabSelfHostedIDPAddedEvent: + idpEvent = e.GitLabSelfHostedIDPAddedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + case *instance.GitLabSelfHostedIDPAddedEvent: + idpEvent = e.GitLabSelfHostedIDPAddedEvent + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-YAF3gw", "reduce.wrong.event.type %v", []eventstore.EventType{org.GitLabSelfHostedIDPAddedEventType, instance.GitLabSelfHostedIDPAddedEventType}) + } + + gitlabSelfHosting := domain.GitlabSelfHosting{ + Issuer: idpEvent.Issuer, + ClientID: idpEvent.ClientID, + ClientSecret: idpEvent.ClientSecret, + Scopes: idpEvent.Scopes, + } + + payloadJSON, err := json.Marshal(gitlabSelfHosting) + if err != nil { + return nil, err + } + + return handler.NewCreateStatement( + &idpEvent, + []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, domain.IDPTypeGitLabSelfHosted), + handler.NewCol(IDPTemplateStateCol, 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, func() any { + if idpEvent.AutoLinkingOption == internal_domain.AutoLinkingOptionUnspecified { + return nil + } + return domain.IDPAutoLinkingField(idpEvent.AutoLinkingOption) + }()), + handler.NewCol(IDPRelationalPayloadCol, payloadJSON), + handler.NewCol(CreatedAt, idpEvent.CreationDate()), + handler.NewCol(UpdatedAt, idpEvent.CreationDate()), + }, + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceGitLabSelfHostedIDPRelationalChanged(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var orgCond handler.Condition + var idpEvent idp.GitLabSelfHostedIDPChangedEvent + switch e := event.(type) { + case *org.GitLabSelfHostedIDPChangedEvent: + idpEvent = e.GitLabSelfHostedIDPChangedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + orgCond = handler.NewCond(IDPRelationalOrgId, orgId) + case *instance.GitLabSelfHostedIDPChangedEvent: + idpEvent = e.GitLabSelfHostedIDPChangedEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-YAf3g2", "reduce.wrong.event.type %v", []eventstore.EventType{org.GitLabSelfHostedIDPChangedEventType, instance.GitLabSelfHostedIDPChangedEventType}) + } + + return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInternal(nil, "HANDL-HX6ed", "unable to cast to tx executer") + } + gitlabSelfHosted, err := p.idpRepo.GetGitlabSelfHosting(ctx, v3_sql.SQLTx(tx), p.idpRepo.IDCondition(idpEvent.ID), idpEvent.Agg.InstanceID, orgId) + if err != nil { + return err + } + + columns := reduceIDPRelationalChangedTemplateColumns(idpEvent.Name, idpEvent.OptionChanges) + + payload := &gitlabSelfHosted.GitlabSelfHosting + payloadChanged := reduceGitLabSelfHostedIDPRelationalChangedColumns(payload, &idpEvent) + if payloadChanged { + payloadJSON, err := json.Marshal(payload) + if err != nil { + return err + } + columns = append(columns, handler.NewCol(IDPRelationalPayloadCol, payloadJSON)) + } + + columns = append(columns, handler.NewCol(UpdatedAt, idpEvent.CreationDate())) + + return handler.NewUpdateStatement( + &idpEvent, + columns, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + orgCond, + }, + ).Execute(ctx, ex, projectionName) + + }), nil +} + +func (p *idpTemplateRelationalProjection) reduceGoogleIDPRelationalAdded(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var idpEvent idp.GoogleIDPAddedEvent + switch e := event.(type) { + case *org.GoogleIDPAddedEvent: + idpEvent = e.GoogleIDPAddedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + case *instance.GoogleIDPAddedEvent: + idpEvent = e.GoogleIDPAddedEvent + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Yp9ihb", "reduce.wrong.event.type %v", []eventstore.EventType{org.GoogleIDPAddedEventType, instance.GoogleIDPAddedEventType}) + } + + google := domain.Google{ + ClientID: idpEvent.ClientID, + ClientSecret: idpEvent.ClientSecret, + Scopes: idpEvent.Scopes, + } + + payloadJSON, err := json.Marshal(google) + if err != nil { + return nil, err + } + + return handler.NewCreateStatement( + &idpEvent, + []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, domain.IDPTypeGoogle), + handler.NewCol(IDPTemplateStateCol, 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, func() any { + if idpEvent.AutoLinkingOption == internal_domain.AutoLinkingOptionUnspecified { + return nil + } + return domain.IDPAutoLinkingField(idpEvent.AutoLinkingOption) + }()), + handler.NewCol(IDPRelationalPayloadCol, payloadJSON), + handler.NewCol(CreatedAt, idpEvent.CreationDate()), + handler.NewCol(UpdatedAt, idpEvent.CreationDate()), + }, + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceGoogleIDPRelationalChanged(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var orgCond handler.Condition + var idpEvent idp.GoogleIDPChangedEvent + switch e := event.(type) { + case *org.GoogleIDPChangedEvent: + idpEvent = e.GoogleIDPChangedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + orgCond = handler.NewCond(IDPRelationalOrgId, orgId) + case *instance.GoogleIDPChangedEvent: + idpEvent = e.GoogleIDPChangedEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-YN58hml", "reduce.wrong.event.type %v", []eventstore.EventType{org.GoogleIDPChangedEventType, instance.GoogleIDPChangedEventType}) + } + + return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInternal(nil, "HANDL-HX6ed", "unable to cast to tx executer") + } + google, err := p.idpRepo.GetGoogle(ctx, v3_sql.SQLTx(tx), p.idpRepo.IDCondition(idpEvent.ID), idpEvent.Agg.InstanceID, orgId) + if err != nil { + return err + } + + columns := reduceIDPRelationalChangedTemplateColumns(idpEvent.Name, idpEvent.OptionChanges) + + payload := &google.Google + payloadChanged := reduceGoogleIDPRelationalChangedColumns(payload, &idpEvent) + if payloadChanged { + payloadJSON, err := json.Marshal(payload) + if err != nil { + return err + } + columns = append(columns, handler.NewCol(IDPRelationalPayloadCol, payloadJSON)) + } + + columns = append(columns, handler.NewCol(UpdatedAt, idpEvent.CreationDate())) + + return handler.NewUpdateStatement( + &idpEvent, + columns, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + orgCond, + }, + ).Execute(ctx, ex, projectionName) + + }), nil +} + +func (p *idpTemplateRelationalProjection) reduceLDAPIDPAdded(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var idpEvent idp.LDAPIDPAddedEvent + switch e := event.(type) { + case *org.LDAPIDPAddedEvent: + idpEvent = e.LDAPIDPAddedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + case *instance.LDAPIDPAddedEvent: + idpEvent = e.LDAPIDPAddedEvent + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-9s02m1", "reduce.wrong.event.type %v", []eventstore.EventType{org.LDAPIDPAddedEventType, instance.LDAPIDPAddedEventType}) + } + + ldap := domain.LDAP{ + Servers: idpEvent.Servers, + StartTLS: idpEvent.StartTLS, + BaseDN: idpEvent.BaseDN, + BindDN: idpEvent.BindDN, + BindPassword: idpEvent.BindPassword, + UserBase: idpEvent.UserBase, + UserObjectClasses: idpEvent.UserObjectClasses, + UserFilters: idpEvent.UserFilters, + Timeout: idpEvent.Timeout, + LDAPAttributes: domain.LDAPAttributes{ + IDAttribute: idpEvent.IDAttribute, + FirstNameAttribute: idpEvent.FirstNameAttribute, + LastNameAttribute: idpEvent.LastNameAttribute, + DisplayNameAttribute: idpEvent.DisplayNameAttribute, + NickNameAttribute: idpEvent.NickNameAttribute, + PreferredUsernameAttribute: idpEvent.PreferredUsernameAttribute, + EmailAttribute: idpEvent.EmailAttribute, + EmailVerifiedAttribute: idpEvent.EmailVerifiedAttribute, + PhoneAttribute: idpEvent.PhoneAttribute, + PhoneVerifiedAttribute: idpEvent.PhoneVerifiedAttribute, + PreferredLanguageAttribute: idpEvent.PreferredLanguageAttribute, + AvatarURLAttribute: idpEvent.AvatarURLAttribute, + ProfileAttribute: idpEvent.ProfileAttribute, + }, + } + + payloadJSON, err := json.Marshal(ldap) + if err != nil { + return nil, err + } + + return handler.NewCreateStatement( + &idpEvent, + []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, domain.IDPTypeLDAP), + handler.NewCol(IDPTemplateStateCol, 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, func() any { + if idpEvent.AutoLinkingOption == internal_domain.AutoLinkingOptionUnspecified { + return nil + } + return domain.IDPAutoLinkingField(idpEvent.AutoLinkingOption) + }()), + handler.NewCol(IDPRelationalPayloadCol, payloadJSON), + handler.NewCol(CreatedAt, idpEvent.CreationDate()), + handler.NewCol(UpdatedAt, idpEvent.CreationDate()), + }, + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceLDAPIDPChanged(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var orgCond handler.Condition + var idpEvent idp.LDAPIDPChangedEvent + switch e := event.(type) { + case *org.LDAPIDPChangedEvent: + idpEvent = e.LDAPIDPChangedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + orgCond = handler.NewCond(IDPRelationalOrgId, orgId) + case *instance.LDAPIDPChangedEvent: + idpEvent = e.LDAPIDPChangedEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.LDAPIDPChangedEventType, instance.LDAPIDPChangedEventType}) + } + + return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInternal(nil, "HANDL-HX6ed", "unable to cast to tx executer") + } + ldap, err := p.idpRepo.GetLDAP(ctx, v3_sql.SQLTx(tx), p.idpRepo.IDCondition(idpEvent.ID), idpEvent.Agg.InstanceID, orgId) + if err != nil { + return err + } + + columns := reduceIDPRelationalChangedTemplateColumns(idpEvent.Name, idpEvent.OptionChanges) + + payload := &ldap.LDAP + payloadChanged := reduceLDAPIDPRelationalChangedColumns(payload, &idpEvent) + if payloadChanged { + payloadJSON, err := json.Marshal(payload) + if err != nil { + return err + } + columns = append(columns, handler.NewCol(IDPRelationalPayloadCol, payloadJSON)) + } + + columns = append(columns, handler.NewCol(UpdatedAt, idpEvent.CreationDate())) + + return handler.NewUpdateStatement( + &idpEvent, + columns, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + orgCond, + }, + ).Execute(ctx, ex, projectionName) + + }), nil +} + +func (p *idpTemplateRelationalProjection) reduceAppleIDPAdded(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var idpEvent idp.AppleIDPAddedEvent + switch e := event.(type) { + case *org.AppleIDPAddedEvent: + idpEvent = e.AppleIDPAddedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + 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*/}) + } + + apple := domain.Apple{ + ClientID: idpEvent.ClientID, + TeamID: idpEvent.TeamID, + KeyID: idpEvent.KeyID, + PrivateKey: idpEvent.PrivateKey, + Scopes: idpEvent.Scopes, + } + + payloadJSON, err := json.Marshal(apple) + if err != nil { + return nil, err + } + + return handler.NewCreateStatement( + &idpEvent, + []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, domain.IDPTypeApple), + handler.NewCol(IDPTemplateStateCol, 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, func() any { + if idpEvent.AutoLinkingOption == internal_domain.AutoLinkingOptionUnspecified { + return nil + } + return domain.IDPAutoLinkingField(idpEvent.AutoLinkingOption) + }()), + handler.NewCol(IDPRelationalPayloadCol, payloadJSON), + handler.NewCol(CreatedAt, idpEvent.CreationDate()), + handler.NewCol(UpdatedAt, idpEvent.CreationDate()), + }, + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceAppleIDPChanged(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var orgCond handler.Condition + var idpEvent idp.AppleIDPChangedEvent + switch e := event.(type) { + case *org.AppleIDPChangedEvent: + idpEvent = e.AppleIDPChangedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + orgCond = handler.NewCond(IDPRelationalOrgId, orgId) + case *instance.AppleIDPChangedEvent: + idpEvent = e.AppleIDPChangedEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-YBez3", "reduce.wrong.event.type %v", []eventstore.EventType{org.AppleIDPChangedEventType /*, instance.AppleIDPChangedEventType*/}) + } + + return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInternal(nil, "HANDL-HX6ed", "unable to cast to tx executer") + } + apple, err := p.idpRepo.GetApple(ctx, v3_sql.SQLTx(tx), p.idpRepo.IDCondition(idpEvent.ID), idpEvent.Agg.InstanceID, orgId) + if err != nil { + return err + } + + columns := reduceIDPRelationalChangedTemplateColumns(idpEvent.Name, idpEvent.OptionChanges) + + payload := &apple.Apple + payloadChanged := reduceAppleIDPRelationalChangedColumns(payload, &idpEvent) + if payloadChanged { + payloadJSON, err := json.Marshal(payload) + if err != nil { + return err + } + columns = append(columns, handler.NewCol(IDPRelationalPayloadCol, payloadJSON)) + } + + columns = append(columns, handler.NewCol(UpdatedAt, idpEvent.CreationDate())) + + return handler.NewUpdateStatement( + &idpEvent, + columns, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + orgCond, + }, + ).Execute(ctx, ex, projectionName) + + }), nil +} + +func (p *idpTemplateRelationalProjection) reduceSAMLIDPAdded(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var idpEvent idp.SAMLIDPAddedEvent + switch e := event.(type) { + case *org.SAMLIDPAddedEvent: + idpEvent = e.SAMLIDPAddedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + 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}) + } + + saml := domain.SAML{ + Metadata: idpEvent.Metadata, + Key: idpEvent.Key, + Certificate: idpEvent.Certificate, + Binding: idpEvent.Binding, + WithSignedRequest: idpEvent.WithSignedRequest, + NameIDFormat: idpEvent.NameIDFormat, + TransientMappingAttributeName: idpEvent.TransientMappingAttributeName, + SignatureAlgorithm: idpEvent.SignatureAlgorithm, + } + + payloadJSON, err := json.Marshal(saml) + if err != nil { + return nil, err + } + + return handler.NewCreateStatement( + &idpEvent, + []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, domain.IDPTypeSAML), + handler.NewCol(IDPTemplateStateCol, 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, func() any { + if idpEvent.AutoLinkingOption == internal_domain.AutoLinkingOptionUnspecified { + return nil + } + return domain.IDPAutoLinkingField(idpEvent.AutoLinkingOption) + }()), + handler.NewCol(IDPRelationalPayloadCol, payloadJSON), + handler.NewCol(CreatedAt, idpEvent.CreationDate()), + handler.NewCol(UpdatedAt, idpEvent.CreationDate()), + }, + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceSAMLIDPChanged(event eventstore.Event) (*handler.Statement, error) { + var orgId *string + var orgCond handler.Condition + var idpEvent idp.SAMLIDPChangedEvent + switch e := event.(type) { + case *org.SAMLIDPChangedEvent: + idpEvent = e.SAMLIDPChangedEvent + orgId = &idpEvent.Aggregate().ResourceOwner + orgCond = handler.NewCond(IDPRelationalOrgId, orgId) + case *instance.SAMLIDPChangedEvent: + idpEvent = e.SAMLIDPChangedEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Y7c0fii4ad", "reduce.wrong.event.type %v", []eventstore.EventType{org.SAMLIDPChangedEventType, instance.SAMLIDPChangedEventType}) + } + + return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInternal(nil, "HANDL-HX6ed", "unable to cast to tx executer") + } + saml, err := p.idpRepo.GetSAML(ctx, v3_sql.SQLTx(tx), p.idpRepo.IDCondition(idpEvent.ID), idpEvent.Agg.InstanceID, orgId) + if err != nil { + return err + } + + columns := reduceIDPRelationalChangedTemplateColumns(idpEvent.Name, idpEvent.OptionChanges) + + payload := &saml.SAML + payloadChanged := reduceSAMLIDPRelationalChangedColumns(payload, &idpEvent) + if payloadChanged { + payloadJSON, err := json.Marshal(payload) + if err != nil { + return err + } + columns = append(columns, handler.NewCol(IDPRelationalPayloadCol, payloadJSON)) + } + + columns = append(columns, handler.NewCol(UpdatedAt, idpEvent.CreationDate())) + + return handler.NewUpdateStatement( + &idpEvent, + columns, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + orgCond, + }, + ).Execute(ctx, ex, projectionName) + + }), nil +} + +func (p *idpTemplateRelationalProjection) reduceIDPRemoved(event eventstore.Event) (*handler.Statement, error) { + var orgCond handler.Condition + var idpEvent idp.RemovedEvent + switch e := event.(type) { + case *org.IDPRemovedEvent: + idpEvent = e.RemovedEvent + orgCond = handler.NewCond(IDPRelationalOrgId, idpEvent.Aggregate().ResourceOwner) + case *instance.IDPRemovedEvent: + idpEvent = e.RemovedEvent + orgCond = handler.NewIsNullCond((IDPRelationalOrgId)) + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Ybcvwin2", "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), + orgCond, + }, + ), nil +} + +func reduceIDPRelationalChangedTemplateColumns(name *string, optionChanges idp.OptionChanges) []handler.Column { + cols := make([]handler.Column, 0, 8) + if name != nil { + cols = append(cols, handler.NewCol(IDPTemplateNameCol, *name)) + } + if optionChanges.IsCreationAllowed != nil { + cols = append(cols, handler.NewCol(IDPRelationalAllowCreationCol, *optionChanges.IsCreationAllowed)) + } + if optionChanges.IsLinkingAllowed != nil { + cols = append(cols, handler.NewCol(IDPRelationalAllowLinkingCol, *optionChanges.IsLinkingAllowed)) + } + if optionChanges.IsAutoCreation != nil { + cols = append(cols, handler.NewCol(IDPRelationalAllowAutoCreationCol, *optionChanges.IsAutoCreation)) + } + if optionChanges.IsAutoUpdate != nil { + cols = append(cols, handler.NewCol(IDPRelationalAllowAutoUpdateCol, *optionChanges.IsAutoUpdate)) + } + if optionChanges.AutoLinkingOption != nil && *optionChanges.AutoLinkingOption != internal_domain.AutoLinkingOptionUnspecified { + cols = append(cols, handler.NewCol(IDPRelationalAllowAutoLinkingCol, domain.IDPAutoLinkingField(*optionChanges.AutoLinkingOption))) + } + + return cols +} + +func reduceOAuthIDPRelationalChangedColumns(payload *domain.OAuth, idpEvent *idp.OAuthIDPChangedEvent) bool { + payloadChange := false + if idpEvent.ClientID != nil { + payloadChange = true + payload.ClientID = *idpEvent.ClientID + } + if idpEvent.ClientSecret != nil { + payloadChange = true + payload.ClientSecret = idpEvent.ClientSecret + } + if idpEvent.AuthorizationEndpoint != nil { + payloadChange = true + payload.AuthorizationEndpoint = *idpEvent.AuthorizationEndpoint + } + if idpEvent.TokenEndpoint != nil { + payloadChange = true + payload.TokenEndpoint = *idpEvent.TokenEndpoint + } + if idpEvent.UserEndpoint != nil { + payloadChange = true + payload.UserEndpoint = *idpEvent.UserEndpoint + } + if idpEvent.Scopes != nil { + payloadChange = true + payload.Scopes = idpEvent.Scopes + } + if idpEvent.IDAttribute != nil { + payloadChange = true + payload.IDAttribute = *idpEvent.IDAttribute + } + if idpEvent.UsePKCE != nil { + payloadChange = true + payload.UsePKCE = *idpEvent.UsePKCE + } + return payloadChange +} + +func reduceOIDCIDPRelationalChangedColumns(payload *domain.OIDC, idpEvent *idp.OIDCIDPChangedEvent) bool { + payloadChange := false + if idpEvent.ClientID != nil { + payloadChange = true + payload.ClientID = *idpEvent.ClientID + } + if idpEvent.ClientSecret != nil { + payloadChange = true + payload.ClientSecret = idpEvent.ClientSecret + } + if idpEvent.Issuer != nil { + payloadChange = true + payload.Issuer = *idpEvent.Issuer + } + if idpEvent.Scopes != nil { + payloadChange = true + payload.Scopes = idpEvent.Scopes + } + if idpEvent.IsIDTokenMapping != nil { + payloadChange = true + payload.IsIDTokenMapping = *idpEvent.IsIDTokenMapping + } + if idpEvent.UsePKCE != nil { + payloadChange = true + payload.UsePKCE = *idpEvent.UsePKCE + } + return payloadChange +} + +func reduceJWTIDPRelationalChangedColumns(payload *domain.JWT, idpEvent *idp.JWTIDPChangedEvent) bool { + payloadChange := false + if idpEvent.JWTEndpoint != nil { + payloadChange = true + payload.JWTEndpoint = *idpEvent.JWTEndpoint + } + if idpEvent.KeysEndpoint != nil { + payloadChange = true + payload.KeysEndpoint = *idpEvent.KeysEndpoint + } + if idpEvent.HeaderName != nil { + payloadChange = true + payload.HeaderName = *idpEvent.HeaderName + } + if idpEvent.Issuer != nil { + payloadChange = true + payload.Issuer = *idpEvent.Issuer + } + return payloadChange +} + +func reduceAzureADIDPRelationalChangedColumns(payload *domain.Azure, idpEvent *idp.AzureADIDPChangedEvent) (bool, error) { + payloadChange := false + if idpEvent.ClientID != nil { + payloadChange = true + payload.ClientID = *idpEvent.ClientID + } + if idpEvent.ClientSecret != nil { + payloadChange = true + payload.ClientSecret = idpEvent.ClientSecret + } + if idpEvent.Scopes != nil { + payloadChange = true + payload.Scopes = idpEvent.Scopes + } + if idpEvent.Tenant != nil { + payloadChange = true + + azureTenant, err := domain.AzureTenantTypeString(*idpEvent.Tenant) + if err != nil { + return false, err + } + + payload.Tenant = azureTenant + } + if idpEvent.IsEmailVerified != nil { + payloadChange = true + payload.IsEmailVerified = *idpEvent.IsEmailVerified + } + return payloadChange, nil +} + +func reduceGitHubIDPRelationalChangedColumns(payload *domain.Github, idpEvent *idp.GitHubIDPChangedEvent) bool { + payloadChange := false + if idpEvent.ClientID != nil { + payloadChange = true + payload.ClientID = *idpEvent.ClientID + } + if idpEvent.ClientSecret != nil { + payloadChange = true + payload.ClientSecret = idpEvent.ClientSecret + } + if idpEvent.Scopes != nil { + payloadChange = true + payload.Scopes = idpEvent.Scopes + } + return payloadChange +} + +func reduceGitHubEnterpriseIDPRelationalChangedColumns(payload *domain.GithubEnterprise, idpEvent *idp.GitHubEnterpriseIDPChangedEvent) bool { + payloadChange := false + if idpEvent.ClientID != nil { + payloadChange = true + payload.ClientID = *idpEvent.ClientID + } + if idpEvent.ClientSecret != nil { + payloadChange = true + payload.ClientSecret = idpEvent.ClientSecret + } + if idpEvent.AuthorizationEndpoint != nil { + payloadChange = true + payload.AuthorizationEndpoint = *idpEvent.AuthorizationEndpoint + } + if idpEvent.TokenEndpoint != nil { + payloadChange = true + payload.TokenEndpoint = *idpEvent.TokenEndpoint + } + if idpEvent.UserEndpoint != nil { + payloadChange = true + payload.UserEndpoint = *idpEvent.UserEndpoint + } + if idpEvent.Scopes != nil { + payloadChange = true + payload.Scopes = idpEvent.Scopes + } + return payloadChange +} + +func reduceGitLabIDPRelationalChangedColumns(payload *domain.Gitlab, idpEvent *idp.GitLabIDPChangedEvent) bool { + payloadChange := false + if idpEvent.ClientID != nil { + payloadChange = true + payload.ClientID = *idpEvent.ClientID + } + if idpEvent.ClientSecret != nil { + payloadChange = true + payload.ClientSecret = idpEvent.ClientSecret + } + if idpEvent.Scopes != nil { + payloadChange = true + payload.Scopes = idpEvent.Scopes + } + return payloadChange +} + +func reduceGitLabSelfHostedIDPRelationalChangedColumns(payload *domain.GitlabSelfHosting, idpEvent *idp.GitLabSelfHostedIDPChangedEvent) bool { + payloadChange := false + if idpEvent.ClientID != nil { + payloadChange = true + payload.ClientID = *idpEvent.ClientID + } + if idpEvent.ClientSecret != nil { + payloadChange = true + payload.ClientSecret = idpEvent.ClientSecret + } + if idpEvent.Issuer != nil { + payloadChange = true + payload.Issuer = *idpEvent.Issuer + } + if idpEvent.Scopes != nil { + payloadChange = true + payload.Scopes = idpEvent.Scopes + } + return payloadChange +} + +func reduceGoogleIDPRelationalChangedColumns(payload *domain.Google, idpEvent *idp.GoogleIDPChangedEvent) bool { + payloadChange := false + if idpEvent.ClientID != nil { + payloadChange = true + payload.ClientID = *idpEvent.ClientID + } + if idpEvent.ClientSecret != nil { + payloadChange = true + payload.ClientSecret = idpEvent.ClientSecret + } + if idpEvent.Scopes != nil { + payloadChange = true + payload.Scopes = idpEvent.Scopes + } + return payloadChange +} + +func reduceLDAPIDPRelationalChangedColumns(payload *domain.LDAP, idpEvent *idp.LDAPIDPChangedEvent) bool { + payloadChange := false + if idpEvent.Servers != nil { + payloadChange = true + payload.Servers = idpEvent.Servers + } + if idpEvent.StartTLS != nil { + payloadChange = true + payload.StartTLS = *idpEvent.StartTLS + } + if idpEvent.BaseDN != nil { + payloadChange = true + payload.BaseDN = *idpEvent.BaseDN + } + if idpEvent.BindDN != nil { + payloadChange = true + payload.BindDN = *idpEvent.BindDN + } + if idpEvent.BindPassword != nil { + payloadChange = true + payload.BindPassword = idpEvent.BindPassword + } + if idpEvent.UserBase != nil { + payloadChange = true + payload.UserBase = *idpEvent.UserBase + } + if idpEvent.UserObjectClasses != nil { + payloadChange = true + payload.UserObjectClasses = idpEvent.UserObjectClasses + } + if idpEvent.UserFilters != nil { + payloadChange = true + payload.UserFilters = idpEvent.UserFilters + } + if idpEvent.Timeout != nil { + payloadChange = true + payload.Timeout = *idpEvent.Timeout + } + if idpEvent.RootCA != nil { + payloadChange = true + payload.RootCA = idpEvent.RootCA + } + if idpEvent.IDAttribute != nil { + payloadChange = true + payload.IDAttribute = *idpEvent.IDAttribute + } + if idpEvent.FirstNameAttribute != nil { + payloadChange = true + payload.FirstNameAttribute = *idpEvent.FirstNameAttribute + } + if idpEvent.LastNameAttribute != nil { + payloadChange = true + payload.LastNameAttribute = *idpEvent.LastNameAttribute + } + if idpEvent.DisplayNameAttribute != nil { + payloadChange = true + payload.DisplayNameAttribute = *idpEvent.DisplayNameAttribute + } + if idpEvent.NickNameAttribute != nil { + payloadChange = true + payload.NickNameAttribute = *idpEvent.NickNameAttribute + } + if idpEvent.PreferredUsernameAttribute != nil { + payloadChange = true + payload.PreferredUsernameAttribute = *idpEvent.PreferredUsernameAttribute + } + if idpEvent.EmailAttribute != nil { + payloadChange = true + payload.EmailAttribute = *idpEvent.EmailAttribute + } + if idpEvent.EmailVerifiedAttribute != nil { + payloadChange = true + payload.EmailVerifiedAttribute = *idpEvent.EmailVerifiedAttribute + } + if idpEvent.PhoneAttribute != nil { + payloadChange = true + payload.PhoneAttribute = *idpEvent.PhoneAttribute + } + if idpEvent.PhoneVerifiedAttribute != nil { + payloadChange = true + payload.PhoneVerifiedAttribute = *idpEvent.PhoneVerifiedAttribute + } + if idpEvent.PreferredLanguageAttribute != nil { + payloadChange = true + payload.PreferredLanguageAttribute = *idpEvent.PreferredLanguageAttribute + } + if idpEvent.AvatarURLAttribute != nil { + payloadChange = true + payload.AvatarURLAttribute = *idpEvent.AvatarURLAttribute + } + if idpEvent.ProfileAttribute != nil { + payloadChange = true + payload.ProfileAttribute = *idpEvent.ProfileAttribute + } + 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 + } + if idpEvent.Key != nil { + payloadChange = true + payload.Key = idpEvent.Key + } + if idpEvent.Certificate != nil { + payloadChange = true + payload.Certificate = idpEvent.Certificate + } + if idpEvent.Binding != nil { + payloadChange = true + payload.Binding = *idpEvent.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 + } + if idpEvent.SignatureAlgorithm != nil { + payloadChange = true + payload.SignatureAlgorithm = *idpEvent.SignatureAlgorithm + } + return payloadChange +} diff --git a/internal/query/projection/projection.go b/internal/query/projection/projection.go index cddf65114f8..0ee8096b1a8 100644 --- a/internal/query/projection/projection.go +++ b/internal/query/projection/projection.go @@ -93,6 +93,7 @@ var ( OrganizationRelationalProjection *handler.Handler InstanceDomainRelationalProjection *handler.Handler OrganizationDomainRelationalProjection *handler.Handler + IDPTemplateRelationalProjection *handler.Handler ProjectGrantFields *handler.FieldHandler OrgDomainVerifiedFields *handler.FieldHandler @@ -208,6 +209,7 @@ func Create(ctx context.Context, sqlClient *database.DB, es handler.EventStore, OrganizationRelationalProjection = newOrgRelationalProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["organizations_relational"])) InstanceDomainRelationalProjection = newInstanceDomainRelationalProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["instance_domains_relational"])) OrganizationDomainRelationalProjection = newOrgDomainRelationalProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["organization_domains_relational"])) + IDPTemplateRelationalProjection = newIDPTemplateRelationalProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["idp_templates_relational"])) newProjectionsList() newFieldsList() @@ -395,5 +397,6 @@ func newProjectionsList() { OrganizationRelationalProjection, InstanceDomainRelationalProjection, OrganizationDomainRelationalProjection, + IDPTemplateRelationalProjection, } }