diff --git a/backend/v3/domain/id_provider.go b/backend/v3/domain/id_provider.go index 723c3597a1..4e64b84230 100644 --- a/backend/v3/domain/id_provider.go +++ b/backend/v3/domain/id_provider.go @@ -200,6 +200,42 @@ type IDPGitlabSelfHosting struct { 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 +} + // 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) @@ -279,4 +315,5 @@ type IDProviderRepository interface { GetGithubEnterprise(ctx context.Context, id IDPIdentifierCondition, instanceID string, orgID *string) (*IDPGithubEnterprise, error) GetGitlab(ctx context.Context, id IDPIdentifierCondition, instanceID string, orgID *string) (*IDPGitlab, error) GetGitlabSelfHosting(ctx context.Context, id IDPIdentifierCondition, instanceID string, orgID *string) (*IDPGitlabSelfHosting, error) + GetLDAP(ctx context.Context, id IDPIdentifierCondition, instanceID string, orgID *string) (*IDPLDAP, error) } diff --git a/backend/v3/storage/database/events_testing/id_provider_org_test.go b/backend/v3/storage/database/events_testing/id_provider_org_test.go index d8c91ae0d0..8c9951be73 100644 --- a/backend/v3/storage/database/events_testing/id_provider_org_test.go +++ b/backend/v3/storage/database/events_testing/id_provider_org_test.go @@ -9,6 +9,7 @@ import ( "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" @@ -1911,4 +1912,232 @@ func TestServer_TestIDProviderOrgReduces(t *testing.T) { 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 + beforeCreate := time.Now() + // addLdap, err := AdminClient.AddLDAPProvider(CTX, &admin.AddLDAPProviderRequest{ + addLdap, err := MgmtClient.AddLDAPProvider(CTX, &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, + }, + }) + afterCreate := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository(pool) + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, time.Second*5) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + ldap, err := idpRepo.GetLDAP(CTX, idpRepo.IDCondition(addLdap.Id), instanceID, &orgID) + require.NoError(t, err) + + // event instance.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.String(), ldap.Type) + assert.Equal(t, false, ldap.AllowLinking) + assert.Equal(t, false, ldap.AllowCreation) + assert.Equal(t, false, ldap.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingOptionEmail.String(), ldap.AllowAutoLinking) + assert.WithinRange(t, ldap.CreatedAt, beforeCreate, afterCreate) + assert.WithinRange(t, ldap.UpdatedAt, beforeCreate, afterCreate) + + // 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(CTX, &admin.AddLDAPProviderRequest{ + addLdap, err := MgmtClient.AddLDAPProvider(CTX, &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(pool) + + var ldap *domain.IDPLDAP + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, time.Second*5) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + ldap, err = idpRepo.GetLDAP(CTX, idpRepo.IDCondition(addLdap.Id), instanceID, &orgID) + require.NoError(t, err) + assert.Equal(t, addLdap.Id, ldap.ID) + }, retryDuration, tick) + + name = "new_" + name + // change ldap + beforeCreate := time.Now() + // _, err = AdminClient.UpdateLDAPProvider(CTX, &admin.UpdateLDAPProviderRequest{ + _, err = MgmtClient.UpdateLDAPProvider(CTX, &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, + }, + }) + afterCreate := time.Now() + require.NoError(t, err) + + // check values for ldap + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(CTX, time.Second*5) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateLdap, err := idpRepo.GetLDAP(CTX, idpRepo.IDCondition(addLdap.Id), instanceID, &orgID) + require.NoError(t, err) + + // event instance.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.String(), updateLdap.Type) + assert.Equal(t, true, updateLdap.AllowLinking) + assert.Equal(t, true, updateLdap.AllowCreation) + assert.Equal(t, true, updateLdap.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingOptionUserName.String(), updateLdap.AllowAutoLinking) + assert.WithinRange(t, updateLdap.UpdatedAt, beforeCreate, afterCreate) + + // 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) + }) } diff --git a/backend/v3/storage/database/events_testing/id_provider_test.go b/backend/v3/storage/database/events_testing/id_provider_test.go index e613b3227f..150d0439db 100644 --- a/backend/v3/storage/database/events_testing/id_provider_test.go +++ b/backend/v3/storage/database/events_testing/id_provider_test.go @@ -17,6 +17,7 @@ import ( "github.com/zitadel/zitadel/pkg/grpc/admin" "github.com/zitadel/zitadel/pkg/grpc/idp" idp_grpc "github.com/zitadel/zitadel/pkg/grpc/idp" + durationpb "google.golang.org/protobuf/types/known/durationpb" ) func TestServer_TestIDProviderReduces(t *testing.T) { @@ -1876,4 +1877,229 @@ func TestServer_TestIDProviderReduces(t *testing.T) { 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 + beforeCreate := time.Now() + addLdap, err := AdminClient.AddLDAPProvider(CTX, &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, + }, + }) + afterCreate := time.Now() + require.NoError(t, err) + + idpRepo := repository.IDProviderRepository(pool) + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, time.Second*5) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + ldap, err := idpRepo.GetLDAP(CTX, 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.String(), ldap.Type) + assert.Equal(t, false, ldap.AllowLinking) + assert.Equal(t, false, ldap.AllowCreation) + assert.Equal(t, false, ldap.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingOptionEmail.String(), ldap.AllowAutoLinking) + assert.WithinRange(t, ldap.CreatedAt, beforeCreate, afterCreate) + assert.WithinRange(t, ldap.UpdatedAt, beforeCreate, afterCreate) + + // 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(CTX, &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(pool) + + var ldap *domain.IDPLDAP + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, time.Second*5) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + ldap, err = idpRepo.GetLDAP(CTX, idpRepo.IDCondition(addLdap.Id), instanceID, nil) + require.NoError(t, err) + assert.Equal(t, addLdap.Id, ldap.ID) + }, retryDuration, tick) + + name = "new_" + name + // change ldap + beforeCreate := time.Now() + _, err = AdminClient.UpdateLDAPProvider(CTX, &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, + }, + }) + afterCreate := time.Now() + require.NoError(t, err) + + // check values for ldap + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(CTX, time.Second*5) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + updateLdap, err := idpRepo.GetLDAP(CTX, 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.String(), updateLdap.Type) + assert.Equal(t, true, updateLdap.AllowLinking) + assert.Equal(t, true, updateLdap.AllowCreation) + assert.Equal(t, true, updateLdap.AllowAutoUpdate) + assert.Equal(t, domain.IDPAutoLinkingOptionUserName.String(), updateLdap.AllowAutoLinking) + assert.WithinRange(t, updateLdap.UpdatedAt, beforeCreate, afterCreate) + + // 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) + }) } diff --git a/backend/v3/storage/database/repository/id_provider.go b/backend/v3/storage/database/repository/id_provider.go index 31f0634071..54ac2da9a6 100644 --- a/backend/v3/storage/database/repository/id_provider.go +++ b/backend/v3/storage/database/repository/id_provider.go @@ -317,6 +317,28 @@ func (i *idProvider) GetGitlabSelfHosting(ctx context.Context, id domain.IDPIden return idpGitlabSelfHosting, nil } +func (i *idProvider) GetLDAP(ctx context.Context, id domain.IDPIdentifierCondition, instnaceID string, orgID *string) (*domain.IDPLDAP, error) { + ldap := &domain.IDPLDAP{} + var err error + + ldap.IdentityProvider, err = i.Get(ctx, id, instnaceID, orgID) + if err != nil { + return nil, err + } + + if ldap.Type != domain.IDPTypeLDAP.String() { + // TODO + return nil, errors.New("WRONG TYPE") + } + + err = json.Unmarshal([]byte(*ldap.Payload), ldap) + if err != nil { + return nil, err + } + + return ldap, nil +} + // ------------------------------------------------------------- // columns // ------------------------------------------------------------- diff --git a/internal/query/projection/idp_template_relational.go b/internal/query/projection/idp_template_relational.go index c1a8ba7977..be50c8e1f3 100644 --- a/internal/query/projection/idp_template_relational.go +++ b/internal/query/projection/idp_template_relational.go @@ -156,14 +156,14 @@ func (p *idpTemplateRelationalProjection) Reducers() []handler.AggregateReducer Event: instance.GoogleIDPChangedEventType, Reduce: p.reduceGoogleIDPRelationalChanged, }, - // { - // Event: instance.LDAPIDPAddedEventType, - // Reduce: p.reduceLDAPIDPAdded, - // }, - // { - // Event: instance.LDAPIDPChangedEventType, - // Reduce: p.reduceLDAPIDPChanged, - // }, + { + Event: instance.LDAPIDPAddedEventType, + Reduce: p.reduceLDAPIDPAdded, + }, + { + Event: instance.LDAPIDPChangedEventType, + Reduce: p.reduceLDAPIDPChanged, + }, // { // Event: instance.AppleIDPAddedEventType, // Reduce: p.reduceAppleIDPAdded, @@ -301,14 +301,14 @@ func (p *idpTemplateRelationalProjection) Reducers() []handler.AggregateReducer Event: org.GoogleIDPChangedEventType, Reduce: p.reduceGoogleIDPRelationalChanged, }, - // { - // Event: org.LDAPIDPAddedEventType, - // Reduce: p.reduceLDAPIDPAdded, - // }, - // { - // Event: org.LDAPIDPChangedEventType, - // Reduce: p.reduceLDAPIDPChanged, - // }, + { + Event: org.LDAPIDPAddedEventType, + Reduce: p.reduceLDAPIDPAdded, + }, + { + Event: org.LDAPIDPChangedEventType, + Reduce: p.reduceLDAPIDPChanged, + }, // { // Event: org.AppleIDPAddedEventType, // Reduce: p.reduceAppleIDPAdded, @@ -1713,6 +1713,123 @@ func (p *idpTemplateRelationalProjection) reduceGoogleIDPRelationalChanged(event }, ), ), nil +} + +func (p *idpTemplateRelationalProjection) reduceLDAPIDPAdded(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idp.LDAPIDPAddedEvent + switch e := event.(type) { + case *org.LDAPIDPAddedEvent: + idpEvent = e.LDAPIDPAddedEvent + 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 := db_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: db_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, + }, + } + + payload, err := json.Marshal(ldap) + if err != nil { + return nil, err + } + + var orgId *string + if idpEvent.Aggregate().ResourceOwner != idpEvent.Agg.InstanceID { + orgId = &idpEvent.Aggregate().ResourceOwner + } + + return handler.NewMultiStatement( + &idpEvent, + handler.AddCreateStatement( + []handler.Column{ + handler.NewCol(IDPTemplateIDCol, idpEvent.ID), + handler.NewCol(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCol(IDPRelationalOrgId, orgId), + handler.NewCol(IDPTemplateNameCol, idpEvent.Name), + handler.NewCol(IDPTemplateTypeCol, db_domain.IDPTypeLDAP.String()), + handler.NewCol(IDPTemplateStateCol, db_domain.IDPStateActive.String()), + handler.NewCol(IDPRelationalAllowCreationCol, idpEvent.IsCreationAllowed), + handler.NewCol(IDPRelationalAllowLinkingCol, idpEvent.IsLinkingAllowed), + handler.NewCol(IDPRelationalAllowAutoCreationCol, idpEvent.IsAutoCreation), + handler.NewCol(IDPRelationalAllowAutoUpdateCol, idpEvent.IsAutoUpdate), + handler.NewCol(IDPRelationalAllowAutoLinkingCol, db_domain.IDPAutoLinkingOption(idpEvent.AutoLinkingOption).String()), + handler.NewCol(CreatedAt, idpEvent.CreationDate()), + handler.NewCol(IDPRelationalPayloadCol, payload), + }, + ), + ), nil +} + +func (p *idpTemplateRelationalProjection) reduceLDAPIDPChanged(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idp.LDAPIDPChangedEvent + switch e := event.(type) { + case *org.LDAPIDPChangedEvent: + idpEvent = e.LDAPIDPChangedEvent + case *instance.LDAPIDPChangedEvent: + idpEvent = e.LDAPIDPChangedEvent + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.LDAPIDPChangedEventType, instance.LDAPIDPChangedEventType}) + } + + var orgId *string + if idpEvent.Aggregate().ResourceOwner != idpEvent.Agg.InstanceID { + orgId = &idpEvent.Aggregate().ResourceOwner + } + + oauth, err := p.idpRepo.GetLDAP(context.Background(), p.idpRepo.IDCondition(idpEvent.ID), idpEvent.Agg.InstanceID, orgId) + if err != nil { + return nil, err + } + + columns := make([]handler.Column, 0, 7) + reduceIDPRelationalChangedTemplateColumns(idpEvent.Name, idpEvent.OptionChanges, &columns) + + payload := &oauth.LDAP + payloadChanged := reduceLDAPIDPRelationalChangedColumns(payload, &idpEvent) + if payloadChanged { + payload, err := json.Marshal(payload) + if err != nil { + return nil, err + } + columns = append(columns, handler.NewCol(IDPRelationalPayloadCol, payload)) + } + + return handler.NewMultiStatement( + &idpEvent, + handler.AddUpdateStatement( + columns, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCond(IDPRelationalOrgId, orgId), + }, + ), + ), nil // ops := make([]func(eventstore.Event) handler.Exec, 0, 2) // ops = append(ops, @@ -1724,16 +1841,17 @@ func (p *idpTemplateRelationalProjection) reduceGoogleIDPRelationalChanged(event // }, // ), // ) - // googleCols := reduceGoogleIDPRelationalChangedColumns(idpEvent) - // if len(googleCols) > 0 { + + // ldapCols := reduceLDAPIDPChangedColumns(idpEvent) + // if len(ldapCols) > 0 { // ops = append(ops, // handler.AddUpdateStatement( - // googleCols, + // ldapCols, // []handler.Condition{ - // handler.NewCond(GoogleIDCol, idpEvent.ID), - // handler.NewCond(GoogleInstanceIDCol, idpEvent.Aggregate().InstanceID), + // handler.NewCond(LDAPIDCol, idpEvent.ID), + // handler.NewCond(LDAPInstanceIDCol, idpEvent.Aggregate().InstanceID), // }, - // handler.WithTableSuffix(IDPTemplateGoogleSuffix), + // handler.WithTableSuffix(IDPTemplateLDAPSuffix), // ), // ) // } @@ -1744,116 +1862,6 @@ func (p *idpTemplateRelationalProjection) reduceGoogleIDPRelationalChanged(event // ), nil } -// func (p *idpTemplateProjection) reduceLDAPIDPAdded(event eventstore.Event) (*handler.Statement, error) { -// var idpEvent idp.LDAPIDPAddedEvent -// var idpOwnerType domain.IdentityProviderType -// switch e := event.(type) { -// case *org.LDAPIDPAddedEvent: -// idpEvent = e.LDAPIDPAddedEvent -// idpOwnerType = domain.IdentityProviderTypeOrg -// case *instance.LDAPIDPAddedEvent: -// idpEvent = e.LDAPIDPAddedEvent -// idpOwnerType = domain.IdentityProviderTypeSystem -// default: -// return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-9s02m1", "reduce.wrong.event.type %v", []eventstore.EventType{org.LDAPIDPAddedEventType, instance.LDAPIDPAddedEventType}) -// } - -// return handler.NewMultiStatement( -// &idpEvent, -// handler.AddCreateStatement( -// []handler.Column{ -// handler.NewCol(IDPTemplateIDCol, idpEvent.ID), -// handler.NewCol(IDPTemplateCreationDateCol, idpEvent.CreationDate()), -// handler.NewCol(IDPTemplateChangeDateCol, idpEvent.CreationDate()), -// handler.NewCol(IDPTemplateSequenceCol, idpEvent.Sequence()), -// handler.NewCol(IDPTemplateResourceOwnerCol, idpEvent.Aggregate().ResourceOwner), -// handler.NewCol(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), -// handler.NewCol(IDPTemplateStateCol, domain.IDPStateActive), -// handler.NewCol(IDPTemplateNameCol, idpEvent.Name), -// handler.NewCol(IDPTemplateOwnerTypeCol, idpOwnerType), -// handler.NewCol(IDPTemplateTypeCol, domain.IDPTypeLDAP), -// handler.NewCol(IDPTemplateIsCreationAllowedCol, idpEvent.IsCreationAllowed), -// handler.NewCol(IDPTemplateIsLinkingAllowedCol, idpEvent.IsLinkingAllowed), -// handler.NewCol(IDPTemplateIsAutoCreationCol, idpEvent.IsAutoCreation), -// handler.NewCol(IDPTemplateIsAutoUpdateCol, idpEvent.IsAutoUpdate), -// handler.NewCol(IDPTemplateAutoLinkingCol, idpEvent.AutoLinkingOption), -// }, -// ), -// handler.AddCreateStatement( -// []handler.Column{ -// handler.NewCol(LDAPIDCol, idpEvent.ID), -// handler.NewCol(LDAPInstanceIDCol, idpEvent.Aggregate().InstanceID), -// handler.NewCol(LDAPServersCol, database.TextArray[string](idpEvent.Servers)), -// handler.NewCol(LDAPStartTLSCol, idpEvent.StartTLS), -// handler.NewCol(LDAPBaseDNCol, idpEvent.BaseDN), -// handler.NewCol(LDAPBindDNCol, idpEvent.BindDN), -// handler.NewCol(LDAPBindPasswordCol, idpEvent.BindPassword), -// handler.NewCol(LDAPUserBaseCol, idpEvent.UserBase), -// handler.NewCol(LDAPUserObjectClassesCol, database.TextArray[string](idpEvent.UserObjectClasses)), -// handler.NewCol(LDAPUserFiltersCol, database.TextArray[string](idpEvent.UserFilters)), -// handler.NewCol(LDAPTimeoutCol, idpEvent.Timeout), -// handler.NewCol(LDAPRootCACol, idpEvent.RootCA), -// handler.NewCol(LDAPIDAttributeCol, idpEvent.IDAttribute), -// handler.NewCol(LDAPFirstNameAttributeCol, idpEvent.FirstNameAttribute), -// handler.NewCol(LDAPLastNameAttributeCol, idpEvent.LastNameAttribute), -// handler.NewCol(LDAPDisplayNameAttributeCol, idpEvent.DisplayNameAttribute), -// handler.NewCol(LDAPNickNameAttributeCol, idpEvent.NickNameAttribute), -// handler.NewCol(LDAPPreferredUsernameAttributeCol, idpEvent.PreferredUsernameAttribute), -// handler.NewCol(LDAPEmailAttributeCol, idpEvent.EmailAttribute), -// handler.NewCol(LDAPEmailVerifiedAttributeCol, idpEvent.EmailVerifiedAttribute), -// handler.NewCol(LDAPPhoneAttributeCol, idpEvent.PhoneAttribute), -// handler.NewCol(LDAPPhoneVerifiedAttributeCol, idpEvent.PhoneVerifiedAttribute), -// handler.NewCol(LDAPPreferredLanguageAttributeCol, idpEvent.PreferredLanguageAttribute), -// handler.NewCol(LDAPAvatarURLAttributeCol, idpEvent.AvatarURLAttribute), -// handler.NewCol(LDAPProfileAttributeCol, idpEvent.ProfileAttribute), -// }, -// handler.WithTableSuffix(IDPTemplateLDAPSuffix), -// ), -// ), nil -// } - -// func (p *idpTemplateProjection) reduceLDAPIDPChanged(event eventstore.Event) (*handler.Statement, error) { -// var idpEvent idp.LDAPIDPChangedEvent -// switch e := event.(type) { -// case *org.LDAPIDPChangedEvent: -// idpEvent = e.LDAPIDPChangedEvent -// case *instance.LDAPIDPChangedEvent: -// idpEvent = e.LDAPIDPChangedEvent -// default: -// return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.LDAPIDPChangedEventType, instance.LDAPIDPChangedEventType}) -// } - -// ops := make([]func(eventstore.Event) handler.Exec, 0, 2) -// ops = append(ops, -// handler.AddUpdateStatement( -// reduceIDPChangedTemplateColumns(idpEvent.Name, idpEvent.CreationDate(), idpEvent.Sequence(), idpEvent.OptionChanges), -// []handler.Condition{ -// handler.NewCond(IDPTemplateIDCol, idpEvent.ID), -// handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), -// }, -// ), -// ) - -// ldapCols := reduceLDAPIDPChangedColumns(idpEvent) -// if len(ldapCols) > 0 { -// ops = append(ops, -// handler.AddUpdateStatement( -// ldapCols, -// []handler.Condition{ -// handler.NewCond(LDAPIDCol, idpEvent.ID), -// handler.NewCond(LDAPInstanceIDCol, idpEvent.Aggregate().InstanceID), -// }, -// handler.WithTableSuffix(IDPTemplateLDAPSuffix), -// ), -// ) -// } - -// return handler.NewMultiStatement( -// &idpEvent, -// ops..., -// ), nil -// } - // func (p *idpTemplateProjection) reduceSAMLIDPAdded(event eventstore.Event) (*handler.Statement, error) { // var idpEvent idp.SAMLIDPAddedEvent // var idpOwnerType domain.IdentityProviderType @@ -2652,3 +2660,100 @@ func reduceGoogleIDPRelationalChangedColumns(payload *db_domain.Google, idpEvent } return payloadChange } + +func reduceLDAPIDPRelationalChangedColumns(payload *db_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 +}