From 5bbb953ffbd2a20a333704217c7077a78f96dfe5 Mon Sep 17 00:00:00 2001 From: Iraq <66622793+kkrime@users.noreply.github.com> Date: Tue, 18 Feb 2025 10:06:50 +0000 Subject: [PATCH] feat(ldap): adding root ca option to ldap config (#9292) # Which Problems Are Solved Adding ability to add a root CA to LDAP configs # Additional Context - Closes https://github.com/zitadel/zitadel/issues/7888 --------- Co-authored-by: Iraq Jaber --- internal/api/grpc/admin/idp_converter.go | 2 + internal/api/grpc/idp/converter.go | 1 + internal/api/grpc/idp/v2/query.go | 1 + internal/api/grpc/management/idp_converter.go | 2 + .../api/ui/login/external_provider_handler.go | 1 + internal/command/idp.go | 1 + internal/command/idp_model.go | 8 + internal/command/instance_idp.go | 2 + internal/command/instance_idp_model.go | 2 + internal/command/instance_idp_test.go | 8 + internal/command/org_idp.go | 2 + internal/command/org_idp_model.go | 2 + internal/command/org_idp_test.go | 7 + internal/idp/providers/ldap/ldap.go | 3 + internal/idp/providers/ldap/ldap_test.go | 6 + internal/idp/providers/ldap/session.go | 37 +- internal/query/idp_template.go | 13 + internal/query/idp_template_test.go | 122 +++--- internal/query/projection/idp_template.go | 8 +- .../query/projection/idp_template_test.go | 14 +- internal/repository/idp/ldap.go | 10 + internal/repository/instance/idp.go | 2 + internal/repository/org/idp.go | 2 + proto/zitadel/admin.proto | 4 + proto/zitadel/idp.proto | 1 + proto/zitadel/idp/v2/idp.proto | 396 ++++++++++-------- proto/zitadel/management.proto | 4 + 27 files changed, 418 insertions(+), 243 deletions(-) diff --git a/internal/api/grpc/admin/idp_converter.go b/internal/api/grpc/admin/idp_converter.go index 67e40a44a..e18154238 100644 --- a/internal/api/grpc/admin/idp_converter.go +++ b/internal/api/grpc/admin/idp_converter.go @@ -423,6 +423,7 @@ func addLDAPProviderToCommand(req *admin_pb.AddLDAPProviderRequest) command.LDAP UserObjectClasses: req.UserObjectClasses, UserFilters: req.UserFilters, Timeout: req.Timeout.AsDuration(), + RootCA: req.RootCa, LDAPAttributes: idp_grpc.LDAPAttributesToCommand(req.Attributes), IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), } @@ -442,6 +443,7 @@ func updateLDAPProviderToCommand(req *admin_pb.UpdateLDAPProviderRequest) comman Timeout: req.Timeout.AsDuration(), LDAPAttributes: idp_grpc.LDAPAttributesToCommand(req.Attributes), IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + RootCA: req.RootCa, } } diff --git a/internal/api/grpc/idp/converter.go b/internal/api/grpc/idp/converter.go index 9b3e09a10..8edc585fe 100644 --- a/internal/api/grpc/idp/converter.go +++ b/internal/api/grpc/idp/converter.go @@ -620,6 +620,7 @@ func ldapConfigToPb(providerConfig *idp_pb.ProviderConfig, template *query.LDAPI UserObjectClasses: template.UserObjectClasses, UserFilters: template.UserFilters, Timeout: timeout, + RootCa: template.RootCA, Attributes: ldapAttributesToPb(template.LDAPAttributes), }, } diff --git a/internal/api/grpc/idp/v2/query.go b/internal/api/grpc/idp/v2/query.go index 0b66175c1..e53d54eee 100644 --- a/internal/api/grpc/idp/v2/query.go +++ b/internal/api/grpc/idp/v2/query.go @@ -288,6 +288,7 @@ func ldapConfigToPb(idpConfig *idp_pb.IDPConfig, template *query.LDAPIDPTemplate UserObjectClasses: template.UserObjectClasses, UserFilters: template.UserFilters, Timeout: timeout, + RootCa: template.RootCA, Attributes: ldapAttributesToPb(template.LDAPAttributes), }, } diff --git a/internal/api/grpc/management/idp_converter.go b/internal/api/grpc/management/idp_converter.go index ef3914cc9..b2fcb7652 100644 --- a/internal/api/grpc/management/idp_converter.go +++ b/internal/api/grpc/management/idp_converter.go @@ -416,6 +416,7 @@ func addLDAPProviderToCommand(req *mgmt_pb.AddLDAPProviderRequest) command.LDAPP UserObjectClasses: req.UserObjectClasses, UserFilters: req.UserFilters, Timeout: req.Timeout.AsDuration(), + RootCA: req.RootCa, LDAPAttributes: idp_grpc.LDAPAttributesToCommand(req.Attributes), IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), } @@ -435,6 +436,7 @@ func updateLDAPProviderToCommand(req *mgmt_pb.UpdateLDAPProviderRequest) command Timeout: req.Timeout.AsDuration(), LDAPAttributes: idp_grpc.LDAPAttributesToCommand(req.Attributes), IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + RootCA: req.RootCa, } } diff --git a/internal/api/ui/login/external_provider_handler.go b/internal/api/ui/login/external_provider_handler.go index 5481c6aed..5d0e0ede6 100644 --- a/internal/api/ui/login/external_provider_handler.go +++ b/internal/api/ui/login/external_provider_handler.go @@ -976,6 +976,7 @@ func (l *Login) ldapProvider(ctx context.Context, identityProvider *query.IDPTem identityProvider.UserObjectClasses, identityProvider.UserFilters, identityProvider.Timeout, + identityProvider.RootCA, l.baseURL(ctx)+EndpointLDAPLogin+"?"+QueryAuthRequestID+"=", opts..., ), nil diff --git a/internal/command/idp.go b/internal/command/idp.go index 84e6a7ddc..b9185cbc5 100644 --- a/internal/command/idp.go +++ b/internal/command/idp.go @@ -107,6 +107,7 @@ type LDAPProvider struct { UserObjectClasses []string UserFilters []string Timeout time.Duration + RootCA []byte LDAPAttributes idp.LDAPAttributes IDPOptions idp.Options } diff --git a/internal/command/idp_model.go b/internal/command/idp_model.go index fa38b4a07..7a51ea111 100644 --- a/internal/command/idp_model.go +++ b/internal/command/idp_model.go @@ -1,6 +1,7 @@ package command import ( + "bytes" "net/http" "reflect" "slices" @@ -1366,6 +1367,7 @@ type LDAPIDPWriteModel struct { UserObjectClasses []string UserFilters []string Timeout time.Duration + RootCA []byte idp.LDAPAttributes idp.Options @@ -1406,6 +1408,7 @@ func (wm *LDAPIDPWriteModel) reduceAddedEvent(e *idp.LDAPIDPAddedEvent) { wm.UserObjectClasses = e.UserObjectClasses wm.UserFilters = e.UserFilters wm.Timeout = e.Timeout + wm.RootCA = e.RootCA wm.LDAPAttributes = e.LDAPAttributes wm.Options = e.Options wm.State = domain.IDPStateActive @@ -1460,6 +1463,7 @@ func (wm *LDAPIDPWriteModel) NewChanges( userObjectClasses []string, userFilters []string, timeout time.Duration, + rootCA []byte, secretCrypto crypto.EncryptionAlgorithm, attributes idp.LDAPAttributes, options idp.Options, @@ -1501,6 +1505,9 @@ func (wm *LDAPIDPWriteModel) NewChanges( if wm.Timeout != timeout { changes = append(changes, idp.ChangeLDAPTimeout(timeout)) } + if !bytes.Equal(wm.RootCA, rootCA) { + changes = append(changes, idp.ChangeLDAPRootCA(rootCA)) + } attrs := wm.LDAPAttributes.Changes(attributes) if !attrs.IsZero() { changes = append(changes, idp.ChangeLDAPAttributes(attrs)) @@ -1582,6 +1589,7 @@ func (wm *LDAPIDPWriteModel) ToProvider(callbackURL string, idpAlg crypto.Encryp wm.UserObjectClasses, wm.UserFilters, wm.Timeout, + wm.RootCA, callbackURL, opts..., ), nil diff --git a/internal/command/instance_idp.go b/internal/command/instance_idp.go index 99ab50642..46f54cad6 100644 --- a/internal/command/instance_idp.go +++ b/internal/command/instance_idp.go @@ -1556,6 +1556,7 @@ func (c *Commands) prepareAddInstanceLDAPProvider(a *instance.Aggregate, writeMo provider.UserObjectClasses, provider.UserFilters, provider.Timeout, + provider.RootCA, provider.LDAPAttributes, provider.IDPOptions, ), @@ -1616,6 +1617,7 @@ func (c *Commands) prepareUpdateInstanceLDAPProvider(a *instance.Aggregate, writ provider.UserObjectClasses, provider.UserFilters, provider.Timeout, + provider.RootCA, c.idpConfigEncryption, provider.LDAPAttributes, provider.IDPOptions, diff --git a/internal/command/instance_idp_model.go b/internal/command/instance_idp_model.go index 5c895b1fc..68f060dd2 100644 --- a/internal/command/instance_idp_model.go +++ b/internal/command/instance_idp_model.go @@ -768,6 +768,7 @@ func (wm *InstanceLDAPIDPWriteModel) NewChangedEvent( userObjectClasses []string, userFilters []string, timeout time.Duration, + rootCA []byte, secretCrypto crypto.EncryptionAlgorithm, attributes idp.LDAPAttributes, options idp.Options, @@ -784,6 +785,7 @@ func (wm *InstanceLDAPIDPWriteModel) NewChangedEvent( userObjectClasses, userFilters, timeout, + rootCA, secretCrypto, attributes, options, diff --git a/internal/command/instance_idp_test.go b/internal/command/instance_idp_test.go index 22defda53..39eb74815 100644 --- a/internal/command/instance_idp_test.go +++ b/internal/command/instance_idp_test.go @@ -4260,6 +4260,7 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) { []string{"object"}, []string{"filter"}, time.Second*30, + []byte("certificate"), idp.LDAPAttributes{}, idp.Options{}, ), @@ -4281,6 +4282,7 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) { UserObjectClasses: []string{"object"}, UserFilters: []string{"filter"}, Timeout: time.Second * 30, + RootCA: []byte("certificate"), }, }, res: res{ @@ -4311,6 +4313,7 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) { []string{"object"}, []string{"filter"}, time.Second*30, + []byte("certificate"), idp.LDAPAttributes{ IDAttribute: "id", FirstNameAttribute: "firstName", @@ -4351,6 +4354,7 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) { UserObjectClasses: []string{"object"}, UserFilters: []string{"filter"}, Timeout: time.Second * 30, + RootCA: []byte("certificate"), LDAPAttributes: idp.LDAPAttributes{ IDAttribute: "id", FirstNameAttribute: "firstName", @@ -4626,6 +4630,7 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) { []string{"object"}, []string{"filter"}, time.Second*30, + []byte("certificate"), idp.LDAPAttributes{}, idp.Options{}, )), @@ -4645,6 +4650,7 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) { UserObjectClasses: []string{"object"}, UserFilters: []string{"filter"}, Timeout: time.Second * 30, + RootCA: []byte("certificate"), }, }, res: res{ @@ -4674,6 +4680,7 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) { []string{"object"}, []string{"filter"}, time.Second*30, + []byte("certificate"), idp.LDAPAttributes{}, idp.Options{}, )), @@ -4742,6 +4749,7 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) { UserObjectClasses: []string{"new object"}, UserFilters: []string{"new filter"}, Timeout: time.Second * 20, + RootCA: []byte("certificate"), LDAPAttributes: idp.LDAPAttributes{ IDAttribute: "new id", FirstNameAttribute: "new firstName", diff --git a/internal/command/org_idp.go b/internal/command/org_idp.go index 26690b3a6..21d871133 100644 --- a/internal/command/org_idp.go +++ b/internal/command/org_idp.go @@ -1540,6 +1540,7 @@ func (c *Commands) prepareAddOrgLDAPProvider(a *org.Aggregate, writeModel *OrgLD provider.UserObjectClasses, provider.UserFilters, provider.Timeout, + provider.RootCA, provider.LDAPAttributes, provider.IDPOptions, ), @@ -1600,6 +1601,7 @@ func (c *Commands) prepareUpdateOrgLDAPProvider(a *org.Aggregate, writeModel *Or provider.UserObjectClasses, provider.UserFilters, provider.Timeout, + provider.RootCA, c.idpConfigEncryption, provider.LDAPAttributes, provider.IDPOptions, diff --git a/internal/command/org_idp_model.go b/internal/command/org_idp_model.go index df0009609..634eb42ea 100644 --- a/internal/command/org_idp_model.go +++ b/internal/command/org_idp_model.go @@ -778,6 +778,7 @@ func (wm *OrgLDAPIDPWriteModel) NewChangedEvent( userObjectClasses []string, userFilters []string, timeout time.Duration, + rootCA []byte, secretCrypto crypto.EncryptionAlgorithm, attributes idp.LDAPAttributes, options idp.Options, @@ -794,6 +795,7 @@ func (wm *OrgLDAPIDPWriteModel) NewChangedEvent( userObjectClasses, userFilters, timeout, + rootCA, secretCrypto, attributes, options, diff --git a/internal/command/org_idp_test.go b/internal/command/org_idp_test.go index 75321bb60..e2fd68fa9 100644 --- a/internal/command/org_idp_test.go +++ b/internal/command/org_idp_test.go @@ -4328,6 +4328,7 @@ func TestCommandSide_AddOrgLDAPIDP(t *testing.T) { []string{"object"}, []string{"filter"}, time.Second*30, + nil, idp.LDAPAttributes{}, idp.Options{}, ), @@ -4380,6 +4381,7 @@ func TestCommandSide_AddOrgLDAPIDP(t *testing.T) { []string{"object"}, []string{"filter"}, time.Second*30, + []byte("certificate"), idp.LDAPAttributes{ IDAttribute: "id", FirstNameAttribute: "firstName", @@ -4421,6 +4423,7 @@ func TestCommandSide_AddOrgLDAPIDP(t *testing.T) { UserObjectClasses: []string{"object"}, UserFilters: []string{"filter"}, Timeout: time.Second * 30, + RootCA: []byte("certificate"), LDAPAttributes: idp.LDAPAttributes{ IDAttribute: "id", FirstNameAttribute: "firstName", @@ -4706,6 +4709,7 @@ func TestCommandSide_UpdateOrgLDAPIDP(t *testing.T) { []string{"object"}, []string{"filter"}, time.Second*30, + []byte("certificate"), idp.LDAPAttributes{}, idp.Options{}, )), @@ -4725,6 +4729,7 @@ func TestCommandSide_UpdateOrgLDAPIDP(t *testing.T) { UserFilters: []string{"filter"}, UserBase: "user", Timeout: time.Second * 30, + RootCA: []byte("certificate"), }, }, res: res{ @@ -4754,6 +4759,7 @@ func TestCommandSide_UpdateOrgLDAPIDP(t *testing.T) { []string{"object"}, []string{"filter"}, time.Second*30, + []byte("certificate"), idp.LDAPAttributes{}, idp.Options{}, )), @@ -4823,6 +4829,7 @@ func TestCommandSide_UpdateOrgLDAPIDP(t *testing.T) { UserObjectClasses: []string{"new object"}, UserFilters: []string{"new filter"}, Timeout: time.Second * 20, + RootCA: []byte("certificate"), LDAPAttributes: idp.LDAPAttributes{ IDAttribute: "new id", FirstNameAttribute: "new firstName", diff --git a/internal/idp/providers/ldap/ldap.go b/internal/idp/providers/ldap/ldap.go index a42ebf237..567356d41 100644 --- a/internal/idp/providers/ldap/ldap.go +++ b/internal/idp/providers/ldap/ldap.go @@ -23,6 +23,7 @@ type Provider struct { userObjectClasses []string userFilters []string timeout time.Duration + rootCA []byte loginUrl string @@ -185,6 +186,7 @@ func New( userObjectClasses []string, userFilters []string, timeout time.Duration, + rootCA []byte, loginUrl string, options ...ProviderOpts, ) *Provider { @@ -199,6 +201,7 @@ func New( userObjectClasses: userObjectClasses, userFilters: userFilters, timeout: timeout, + rootCA: rootCA, loginUrl: loginUrl, } for _, option := range options { diff --git a/internal/idp/providers/ldap/ldap_test.go b/internal/idp/providers/ldap/ldap_test.go index d00680da1..8b9f08def 100644 --- a/internal/idp/providers/ldap/ldap_test.go +++ b/internal/idp/providers/ldap/ldap_test.go @@ -18,11 +18,13 @@ func TestProvider_Options(t *testing.T) { userObjectClasses []string userFilters []string timeout time.Duration + rootCA []byte loginUrl string opts []ProviderOpts } type want struct { name string + rootCA []byte startTls bool linkingAllowed bool creationAllowed bool @@ -114,6 +116,7 @@ func TestProvider_Options(t *testing.T) { userObjectClasses: []string{"object"}, userFilters: []string{"filter"}, timeout: 30 * time.Second, + rootCA: []byte("certificate"), loginUrl: "url", opts: []ProviderOpts{ WithoutStartTLS(), @@ -138,6 +141,7 @@ func TestProvider_Options(t *testing.T) { }, want: want{ name: "ldap", + rootCA: []byte("certificate"), startTls: false, linkingAllowed: true, creationAllowed: true, @@ -172,11 +176,13 @@ func TestProvider_Options(t *testing.T) { tt.fields.userObjectClasses, tt.fields.userFilters, tt.fields.timeout, + tt.fields.rootCA, tt.fields.loginUrl, tt.fields.opts..., ) a.Equal(tt.want.name, provider.Name()) + a.Equal(tt.want.rootCA, provider.rootCA) a.Equal(tt.want.startTls, provider.startTLS) a.Equal(tt.want.linkingAllowed, provider.IsLinkingAllowed()) a.Equal(tt.want.creationAllowed, provider.IsCreationAllowed()) diff --git a/internal/idp/providers/ldap/session.go b/internal/idp/providers/ldap/session.go index c3ca5c636..4984ad353 100644 --- a/internal/idp/providers/ldap/session.go +++ b/internal/idp/providers/ldap/session.go @@ -3,6 +3,7 @@ package ldap import ( "context" "crypto/tls" + "crypto/x509" "encoding/base64" "errors" "net" @@ -21,6 +22,7 @@ import ( var ErrNoSingleUser = errors.New("user does not exist or too many entries returned") var ErrFailedLogin = errors.New("user failed to login") +var ErrUnableToAppendRootCA = errors.New("unable to append rootCA") var _ idp.Session = (*Session)(nil) @@ -49,7 +51,9 @@ func (s *Session) FetchUser(_ context.Context) (_ idp.User, err error) { s.Provider.userObjectClasses, s.Provider.userFilters, s.User, - s.Password, s.Provider.timeout) + s.Password, + s.Provider.timeout, + s.Provider.rootCA) // If there were invalid credentials or multiple users with the credentials cancel process if err != nil && (errors.Is(err, ErrFailedLogin) || errors.Is(err, ErrNoSingleUser)) { return nil, err @@ -94,8 +98,9 @@ func tryBind( username string, password string, timeout time.Duration, + rootCA []byte, ) (*ldap.Entry, error) { - conn, err := getConnection(server, startTLS, timeout) + conn, err := getConnection(server, startTLS, timeout, rootCA) if err != nil { return nil, err } @@ -114,6 +119,7 @@ func tryBind( username, password, timeout, + rootCA, ) } @@ -121,21 +127,37 @@ func getConnection( server string, startTLS bool, timeout time.Duration, + rootCA []byte, ) (*ldap.Conn, error) { if timeout == 0 { timeout = ldap.DefaultTimeout } - conn, err := ldap.DialURL(server, ldap.DialWithDialer(&net.Dialer{Timeout: timeout})) - if err != nil { - return nil, err - } + dialer := make([]ldap.DialOpt, 1, 2) + dialer[0] = ldap.DialWithDialer(&net.Dialer{Timeout: timeout}) u, err := url.Parse(server) if err != nil { return nil, err } - if u.Scheme == "ldaps" && startTLS { + + if u.Scheme == "ldaps" && len(rootCA) > 0 { + rootCAs := x509.NewCertPool() + if ok := rootCAs.AppendCertsFromPEM(rootCA); !ok { + return nil, ErrUnableToAppendRootCA + } + + dialer = append(dialer, ldap.DialWithTLSConfig(&tls.Config{ + RootCAs: rootCAs, + })) + } + + conn, err := ldap.DialURL(server, dialer...) + if err != nil { + return nil, err + } + + if u.Scheme == "ldap" && startTLS { err = conn.StartTLS(&tls.Config{ServerName: u.Host}) if err != nil { return nil, err @@ -153,6 +175,7 @@ func trySearchAndUserBind( username string, password string, timeout time.Duration, + rootCA []byte, ) (*ldap.Entry, error) { searchQuery := queriesAndToSearchQuery( objectClassesToSearchQuery(objectClasses), diff --git a/internal/query/idp_template.go b/internal/query/idp_template.go index dd36cee19..d75e040ad 100644 --- a/internal/query/idp_template.go +++ b/internal/query/idp_template.go @@ -142,6 +142,7 @@ type LDAPIDPTemplate struct { UserObjectClasses []string UserFilters []string Timeout time.Duration + RootCA []byte idp.LDAPAttributes } @@ -580,6 +581,10 @@ var ( name: projection.LDAPTimeoutCol, table: ldapIdpTemplateTable, } + LDAPRootCACol = Column{ + name: projection.LDAPRootCACol, + table: ldapIdpTemplateTable, + } LDAPIDAttributeCol = Column{ name: projection.LDAPIDAttributeCol, table: ldapIdpTemplateTable, @@ -943,6 +948,7 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se LDAPUserObjectClassesCol.identifier(), LDAPUserFiltersCol.identifier(), LDAPTimeoutCol.identifier(), + LDAPRootCACol.identifier(), LDAPIDAttributeCol.identifier(), LDAPFirstNameAttributeCol.identifier(), LDAPLastNameAttributeCol.identifier(), @@ -1059,6 +1065,7 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se ldapUserObjectClasses := database.TextArray[string]{} ldapUserFilters := database.TextArray[string]{} ldapTimeout := sql.NullInt64{} + var ldapRootCA []byte ldapIDAttribute := sql.NullString{} ldapFirstNameAttribute := sql.NullString{} ldapLastNameAttribute := sql.NullString{} @@ -1173,6 +1180,7 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se &ldapUserObjectClasses, &ldapUserFilters, &ldapTimeout, + &ldapRootCA, &ldapIDAttribute, &ldapFirstNameAttribute, &ldapLastNameAttribute, @@ -1312,6 +1320,7 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se UserObjectClasses: ldapUserObjectClasses, UserFilters: ldapUserFilters, Timeout: time.Duration(ldapTimeout.Int64), + RootCA: ldapRootCA, LDAPAttributes: idp.LDAPAttributes{ IDAttribute: ldapIDAttribute.String, FirstNameAttribute: ldapFirstNameAttribute.String, @@ -1438,6 +1447,7 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec LDAPUserObjectClassesCol.identifier(), LDAPUserFiltersCol.identifier(), LDAPTimeoutCol.identifier(), + LDAPRootCACol.identifier(), LDAPIDAttributeCol.identifier(), LDAPFirstNameAttributeCol.identifier(), LDAPLastNameAttributeCol.identifier(), @@ -1559,6 +1569,7 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec ldapUserObjectClasses := database.TextArray[string]{} ldapUserFilters := database.TextArray[string]{} ldapTimeout := sql.NullInt64{} + var ldapRootCA []byte ldapIDAttribute := sql.NullString{} ldapFirstNameAttribute := sql.NullString{} ldapLastNameAttribute := sql.NullString{} @@ -1673,6 +1684,7 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec &ldapUserObjectClasses, &ldapUserFilters, &ldapTimeout, + &ldapRootCA, &ldapIDAttribute, &ldapFirstNameAttribute, &ldapLastNameAttribute, @@ -1811,6 +1823,7 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec UserObjectClasses: ldapUserObjectClasses, UserFilters: ldapUserFilters, Timeout: time.Duration(ldapTimeout.Int64), + RootCA: ldapRootCA, LDAPAttributes: idp.LDAPAttributes{ IDAttribute: ldapIDAttribute.String, FirstNameAttribute: ldapFirstNameAttribute.String, diff --git a/internal/query/idp_template_test.go b/internal/query/idp_template_test.go index c5bee3a00..1c2310fee 100644 --- a/internal/query/idp_template_test.go +++ b/internal/query/idp_template_test.go @@ -98,29 +98,30 @@ var ( ` projections.idp_templates6_saml.name_id_format,` + ` projections.idp_templates6_saml.transient_mapping_attribute_name,` + // ldap - ` projections.idp_templates6_ldap2.idp_id,` + - ` projections.idp_templates6_ldap2.servers,` + - ` projections.idp_templates6_ldap2.start_tls,` + - ` projections.idp_templates6_ldap2.base_dn,` + - ` projections.idp_templates6_ldap2.bind_dn,` + - ` projections.idp_templates6_ldap2.bind_password,` + - ` projections.idp_templates6_ldap2.user_base,` + - ` projections.idp_templates6_ldap2.user_object_classes,` + - ` projections.idp_templates6_ldap2.user_filters,` + - ` projections.idp_templates6_ldap2.timeout,` + - ` projections.idp_templates6_ldap2.id_attribute,` + - ` projections.idp_templates6_ldap2.first_name_attribute,` + - ` projections.idp_templates6_ldap2.last_name_attribute,` + - ` projections.idp_templates6_ldap2.display_name_attribute,` + - ` projections.idp_templates6_ldap2.nick_name_attribute,` + - ` projections.idp_templates6_ldap2.preferred_username_attribute,` + - ` projections.idp_templates6_ldap2.email_attribute,` + - ` projections.idp_templates6_ldap2.email_verified,` + - ` projections.idp_templates6_ldap2.phone_attribute,` + - ` projections.idp_templates6_ldap2.phone_verified_attribute,` + - ` projections.idp_templates6_ldap2.preferred_language_attribute,` + - ` projections.idp_templates6_ldap2.avatar_url_attribute,` + - ` projections.idp_templates6_ldap2.profile_attribute,` + + ` projections.idp_templates6_ldap3.idp_id,` + + ` projections.idp_templates6_ldap3.servers,` + + ` projections.idp_templates6_ldap3.start_tls,` + + ` projections.idp_templates6_ldap3.base_dn,` + + ` projections.idp_templates6_ldap3.bind_dn,` + + ` projections.idp_templates6_ldap3.bind_password,` + + ` projections.idp_templates6_ldap3.user_base,` + + ` projections.idp_templates6_ldap3.user_object_classes,` + + ` projections.idp_templates6_ldap3.user_filters,` + + ` projections.idp_templates6_ldap3.timeout,` + + ` projections.idp_templates6_ldap3.rootCA,` + + ` projections.idp_templates6_ldap3.id_attribute,` + + ` projections.idp_templates6_ldap3.first_name_attribute,` + + ` projections.idp_templates6_ldap3.last_name_attribute,` + + ` projections.idp_templates6_ldap3.display_name_attribute,` + + ` projections.idp_templates6_ldap3.nick_name_attribute,` + + ` projections.idp_templates6_ldap3.preferred_username_attribute,` + + ` projections.idp_templates6_ldap3.email_attribute,` + + ` projections.idp_templates6_ldap3.email_verified,` + + ` projections.idp_templates6_ldap3.phone_attribute,` + + ` projections.idp_templates6_ldap3.phone_verified_attribute,` + + ` projections.idp_templates6_ldap3.preferred_language_attribute,` + + ` projections.idp_templates6_ldap3.avatar_url_attribute,` + + ` projections.idp_templates6_ldap3.profile_attribute,` + // apple ` projections.idp_templates6_apple.idp_id,` + ` projections.idp_templates6_apple.client_id,` + @@ -139,7 +140,7 @@ var ( ` LEFT JOIN projections.idp_templates6_gitlab_self_hosted ON projections.idp_templates6.id = projections.idp_templates6_gitlab_self_hosted.idp_id AND projections.idp_templates6.instance_id = projections.idp_templates6_gitlab_self_hosted.instance_id` + ` LEFT JOIN projections.idp_templates6_google ON projections.idp_templates6.id = projections.idp_templates6_google.idp_id AND projections.idp_templates6.instance_id = projections.idp_templates6_google.instance_id` + ` LEFT JOIN projections.idp_templates6_saml ON projections.idp_templates6.id = projections.idp_templates6_saml.idp_id AND projections.idp_templates6.instance_id = projections.idp_templates6_saml.instance_id` + - ` LEFT JOIN projections.idp_templates6_ldap2 ON projections.idp_templates6.id = projections.idp_templates6_ldap2.idp_id AND projections.idp_templates6.instance_id = projections.idp_templates6_ldap2.instance_id` + + ` LEFT JOIN projections.idp_templates6_ldap3 ON projections.idp_templates6.id = projections.idp_templates6_ldap3.idp_id AND projections.idp_templates6.instance_id = projections.idp_templates6_ldap3.instance_id` + ` LEFT JOIN projections.idp_templates6_apple ON projections.idp_templates6.id = projections.idp_templates6_apple.idp_id AND projections.idp_templates6.instance_id = projections.idp_templates6_apple.instance_id` + ` AS OF SYSTEM TIME '-1 ms'` idpTemplateCols = []string{ @@ -235,6 +236,7 @@ var ( "user_object_classes", "user_filters", "timeout", + "rootCA", "id_attribute", "first_name_attribute", "last_name_attribute", @@ -338,29 +340,30 @@ var ( ` projections.idp_templates6_saml.name_id_format,` + ` projections.idp_templates6_saml.transient_mapping_attribute_name,` + // ldap - ` projections.idp_templates6_ldap2.idp_id,` + - ` projections.idp_templates6_ldap2.servers,` + - ` projections.idp_templates6_ldap2.start_tls,` + - ` projections.idp_templates6_ldap2.base_dn,` + - ` projections.idp_templates6_ldap2.bind_dn,` + - ` projections.idp_templates6_ldap2.bind_password,` + - ` projections.idp_templates6_ldap2.user_base,` + - ` projections.idp_templates6_ldap2.user_object_classes,` + - ` projections.idp_templates6_ldap2.user_filters,` + - ` projections.idp_templates6_ldap2.timeout,` + - ` projections.idp_templates6_ldap2.id_attribute,` + - ` projections.idp_templates6_ldap2.first_name_attribute,` + - ` projections.idp_templates6_ldap2.last_name_attribute,` + - ` projections.idp_templates6_ldap2.display_name_attribute,` + - ` projections.idp_templates6_ldap2.nick_name_attribute,` + - ` projections.idp_templates6_ldap2.preferred_username_attribute,` + - ` projections.idp_templates6_ldap2.email_attribute,` + - ` projections.idp_templates6_ldap2.email_verified,` + - ` projections.idp_templates6_ldap2.phone_attribute,` + - ` projections.idp_templates6_ldap2.phone_verified_attribute,` + - ` projections.idp_templates6_ldap2.preferred_language_attribute,` + - ` projections.idp_templates6_ldap2.avatar_url_attribute,` + - ` projections.idp_templates6_ldap2.profile_attribute,` + + ` projections.idp_templates6_ldap3.idp_id,` + + ` projections.idp_templates6_ldap3.servers,` + + ` projections.idp_templates6_ldap3.start_tls,` + + ` projections.idp_templates6_ldap3.base_dn,` + + ` projections.idp_templates6_ldap3.bind_dn,` + + ` projections.idp_templates6_ldap3.bind_password,` + + ` projections.idp_templates6_ldap3.user_base,` + + ` projections.idp_templates6_ldap3.user_object_classes,` + + ` projections.idp_templates6_ldap3.user_filters,` + + ` projections.idp_templates6_ldap3.timeout,` + + ` projections.idp_templates6_ldap3.rootCA,` + + ` projections.idp_templates6_ldap3.id_attribute,` + + ` projections.idp_templates6_ldap3.first_name_attribute,` + + ` projections.idp_templates6_ldap3.last_name_attribute,` + + ` projections.idp_templates6_ldap3.display_name_attribute,` + + ` projections.idp_templates6_ldap3.nick_name_attribute,` + + ` projections.idp_templates6_ldap3.preferred_username_attribute,` + + ` projections.idp_templates6_ldap3.email_attribute,` + + ` projections.idp_templates6_ldap3.email_verified,` + + ` projections.idp_templates6_ldap3.phone_attribute,` + + ` projections.idp_templates6_ldap3.phone_verified_attribute,` + + ` projections.idp_templates6_ldap3.preferred_language_attribute,` + + ` projections.idp_templates6_ldap3.avatar_url_attribute,` + + ` projections.idp_templates6_ldap3.profile_attribute,` + // apple ` projections.idp_templates6_apple.idp_id,` + ` projections.idp_templates6_apple.client_id,` + @@ -380,7 +383,7 @@ var ( ` LEFT JOIN projections.idp_templates6_gitlab_self_hosted ON projections.idp_templates6.id = projections.idp_templates6_gitlab_self_hosted.idp_id AND projections.idp_templates6.instance_id = projections.idp_templates6_gitlab_self_hosted.instance_id` + ` LEFT JOIN projections.idp_templates6_google ON projections.idp_templates6.id = projections.idp_templates6_google.idp_id AND projections.idp_templates6.instance_id = projections.idp_templates6_google.instance_id` + ` LEFT JOIN projections.idp_templates6_saml ON projections.idp_templates6.id = projections.idp_templates6_saml.idp_id AND projections.idp_templates6.instance_id = projections.idp_templates6_saml.instance_id` + - ` LEFT JOIN projections.idp_templates6_ldap2 ON projections.idp_templates6.id = projections.idp_templates6_ldap2.idp_id AND projections.idp_templates6.instance_id = projections.idp_templates6_ldap2.instance_id` + + ` LEFT JOIN projections.idp_templates6_ldap3 ON projections.idp_templates6.id = projections.idp_templates6_ldap3.idp_id AND projections.idp_templates6.instance_id = projections.idp_templates6_ldap3.instance_id` + ` LEFT JOIN projections.idp_templates6_apple ON projections.idp_templates6.id = projections.idp_templates6_apple.idp_id AND projections.idp_templates6.instance_id = projections.idp_templates6_apple.instance_id` + ` AS OF SYSTEM TIME '-1 ms'` idpTemplatesCols = []string{ @@ -476,6 +479,7 @@ var ( "user_object_classes", "user_filters", "timeout", + "rootCA", "id_attribute", "first_name_attribute", "last_name_attribute", @@ -642,6 +646,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // apple nil, nil, @@ -792,6 +797,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // apple nil, nil, @@ -940,6 +946,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // apple nil, nil, @@ -1087,6 +1094,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // apple nil, nil, @@ -1233,6 +1241,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // apple nil, nil, @@ -1379,6 +1388,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // apple nil, nil, @@ -1526,6 +1536,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // apple nil, nil, @@ -1672,6 +1683,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // apple nil, nil, @@ -1809,6 +1821,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { database.TextArray[string]{"object"}, database.TextArray[string]{"filter"}, time.Duration(30000000000), + []byte("certificate"), "id", "first", "last", @@ -1857,6 +1870,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { UserObjectClasses: []string{"object"}, UserFilters: []string{"filter"}, Timeout: time.Duration(30000000000), + RootCA: []byte("certificate"), LDAPAttributes: idp.LDAPAttributes{ IDAttribute: "id", FirstNameAttribute: "first", @@ -1988,6 +2002,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // apple "idp-id", "client_id", @@ -2136,6 +2151,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // apple nil, nil, @@ -2299,6 +2315,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { database.TextArray[string]{"object"}, database.TextArray[string]{"filter"}, time.Duration(30000000000), + []byte("certificate"), "id", "first", "last", @@ -2353,6 +2370,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { UserObjectClasses: []string{"object"}, UserFilters: []string{"filter"}, Timeout: time.Duration(30000000000), + RootCA: []byte("certificate"), LDAPAttributes: idp.LDAPAttributes{ IDAttribute: "id", FirstNameAttribute: "first", @@ -2487,6 +2505,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // apple nil, nil, @@ -2623,6 +2642,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { database.TextArray[string]{"object"}, database.TextArray[string]{"filter"}, time.Duration(30000000000), + []byte("certificate"), "id", "first", "last", @@ -2750,6 +2770,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // apple nil, nil, @@ -2864,6 +2885,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // apple nil, nil, @@ -2978,6 +3000,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // apple nil, nil, @@ -3092,6 +3115,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // apple nil, nil, @@ -3206,6 +3230,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // apple nil, nil, @@ -3247,6 +3272,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { UserObjectClasses: []string{"object"}, UserFilters: []string{"filter"}, Timeout: time.Duration(30000000000), + RootCA: []byte("certificate"), LDAPAttributes: idp.LDAPAttributes{ IDAttribute: "id", FirstNameAttribute: "first", diff --git a/internal/query/projection/idp_template.go b/internal/query/projection/idp_template.go index a9d38cfeb..a5edcba16 100644 --- a/internal/query/projection/idp_template.go +++ b/internal/query/projection/idp_template.go @@ -40,7 +40,7 @@ const ( IDPTemplateGitLabSuffix = "gitlab" IDPTemplateGitLabSelfHostedSuffix = "gitlab_self_hosted" IDPTemplateGoogleSuffix = "google" - IDPTemplateLDAPSuffix = "ldap2" + IDPTemplateLDAPSuffix = "ldap3" IDPTemplateAppleSuffix = "apple" IDPTemplateSAMLSuffix = "saml" @@ -139,6 +139,7 @@ const ( LDAPUserObjectClassesCol = "user_object_classes" LDAPUserFiltersCol = "user_filters" LDAPTimeoutCol = "timeout" + LDAPRootCACol = "rootCA" LDAPIDAttributeCol = "id_attribute" LDAPFirstNameAttributeCol = "first_name_attribute" LDAPLastNameAttributeCol = "last_name_attribute" @@ -330,6 +331,7 @@ func (*idpTemplateProjection) Init() *old_handler.Check { handler.NewColumn(LDAPUserObjectClassesCol, handler.ColumnTypeTextArray), handler.NewColumn(LDAPUserFiltersCol, handler.ColumnTypeTextArray), handler.NewColumn(LDAPTimeoutCol, handler.ColumnTypeInt64), + handler.NewColumn(LDAPRootCACol, handler.ColumnTypeBytes, handler.Nullable()), handler.NewColumn(LDAPIDAttributeCol, handler.ColumnTypeText, handler.Nullable()), handler.NewColumn(LDAPFirstNameAttributeCol, handler.ColumnTypeText, handler.Nullable()), handler.NewColumn(LDAPLastNameAttributeCol, handler.ColumnTypeText, handler.Nullable()), @@ -1896,6 +1898,7 @@ func (p *idpTemplateProjection) reduceLDAPIDPAdded(event eventstore.Event) (*han 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), @@ -2421,6 +2424,9 @@ func reduceLDAPIDPChangedColumns(idpEvent idp.LDAPIDPChangedEvent) []handler.Col if idpEvent.Timeout != nil { ldapCols = append(ldapCols, handler.NewCol(LDAPTimeoutCol, *idpEvent.Timeout)) } + if idpEvent.RootCA != nil { + ldapCols = append(ldapCols, handler.NewCol(LDAPRootCACol, idpEvent.RootCA)) + } if idpEvent.IDAttribute != nil { ldapCols = append(ldapCols, handler.NewCol(LDAPIDAttributeCol, *idpEvent.IDAttribute)) } diff --git a/internal/query/projection/idp_template_test.go b/internal/query/projection/idp_template_test.go index 6f096a94c..e8efa82f8 100644 --- a/internal/query/projection/idp_template_test.go +++ b/internal/query/projection/idp_template_test.go @@ -2117,6 +2117,7 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { "userObjectClasses": ["object"], "userFilters": ["filter"], "timeout": 30000000000, + "rootcA": `+stringToJSONByte("certificate")+`, "idAttribute": "id", "firstNameAttribute": "first", "lastNameAttribute": "last", @@ -2165,7 +2166,7 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.idp_templates6_ldap2 (idp_id, instance_id, servers, start_tls, base_dn, bind_dn, bind_password, user_base, user_object_classes, user_filters, timeout, id_attribute, first_name_attribute, last_name_attribute, display_name_attribute, nick_name_attribute, preferred_username_attribute, email_attribute, email_verified, phone_attribute, phone_verified_attribute, preferred_language_attribute, avatar_url_attribute, profile_attribute) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24)", + expectedStmt: "INSERT INTO projections.idp_templates6_ldap3 (idp_id, instance_id, servers, start_tls, base_dn, bind_dn, bind_password, user_base, user_object_classes, user_filters, timeout, rootCA, id_attribute, first_name_attribute, last_name_attribute, display_name_attribute, nick_name_attribute, preferred_username_attribute, email_attribute, email_verified, phone_attribute, phone_verified_attribute, preferred_language_attribute, avatar_url_attribute, profile_attribute) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25)", expectedArgs: []interface{}{ "idp-id", "instance-id", @@ -2178,6 +2179,7 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { database.TextArray[string]{"object"}, database.TextArray[string]{"filter"}, time.Duration(30000000000), + []byte("certificate"), "id", "first", "last", @@ -2220,6 +2222,7 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { "userObjectClasses": ["object"], "userFilters": ["filter"], "timeout": 30000000000, + "rootcA": `+stringToJSONByte("certificate")+`, "idAttribute": "id", "firstNameAttribute": "first", "lastNameAttribute": "last", @@ -2268,7 +2271,7 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.idp_templates6_ldap2 (idp_id, instance_id, servers, start_tls, base_dn, bind_dn, bind_password, user_base, user_object_classes, user_filters, timeout, id_attribute, first_name_attribute, last_name_attribute, display_name_attribute, nick_name_attribute, preferred_username_attribute, email_attribute, email_verified, phone_attribute, phone_verified_attribute, preferred_language_attribute, avatar_url_attribute, profile_attribute) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24)", + expectedStmt: "INSERT INTO projections.idp_templates6_ldap3 (idp_id, instance_id, servers, start_tls, base_dn, bind_dn, bind_password, user_base, user_object_classes, user_filters, timeout, rootCA, id_attribute, first_name_attribute, last_name_attribute, display_name_attribute, nick_name_attribute, preferred_username_attribute, email_attribute, email_verified, phone_attribute, phone_verified_attribute, preferred_language_attribute, avatar_url_attribute, profile_attribute) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25)", expectedArgs: []interface{}{ "idp-id", "instance-id", @@ -2281,6 +2284,7 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { database.TextArray[string]{"object"}, database.TextArray[string]{"filter"}, time.Duration(30000000000), + []byte("certificate"), "id", "first", "last", @@ -2331,7 +2335,7 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.idp_templates6_ldap2 SET base_dn = $1 WHERE (idp_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.idp_templates6_ldap3 SET base_dn = $1 WHERE (idp_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ "basedn", "idp-id", @@ -2365,6 +2369,7 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { "userObjectClasses": ["object"], "userFilters": ["filter"], "timeout": 30000000000, + "rootcA": `+stringToJSONByte("certificate")+`, "idAttribute": "id", "firstNameAttribute": "first", "lastNameAttribute": "last", @@ -2408,7 +2413,7 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.idp_templates6_ldap2 SET (servers, start_tls, base_dn, bind_dn, bind_password, user_base, user_object_classes, user_filters, timeout, id_attribute, first_name_attribute, last_name_attribute, display_name_attribute, nick_name_attribute, preferred_username_attribute, email_attribute, email_verified, phone_attribute, phone_verified_attribute, preferred_language_attribute, avatar_url_attribute, profile_attribute) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22) WHERE (idp_id = $23) AND (instance_id = $24)", + expectedStmt: "UPDATE projections.idp_templates6_ldap3 SET (servers, start_tls, base_dn, bind_dn, bind_password, user_base, user_object_classes, user_filters, timeout, rootCA, id_attribute, first_name_attribute, last_name_attribute, display_name_attribute, nick_name_attribute, preferred_username_attribute, email_attribute, email_verified, phone_attribute, phone_verified_attribute, preferred_language_attribute, avatar_url_attribute, profile_attribute) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23) WHERE (idp_id = $24) AND (instance_id = $25)", expectedArgs: []interface{}{ database.TextArray[string]{"server"}, false, @@ -2419,6 +2424,7 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { database.TextArray[string]{"object"}, database.TextArray[string]{"filter"}, time.Duration(30000000000), + []byte("certificate"), "id", "first", "last", diff --git a/internal/repository/idp/ldap.go b/internal/repository/idp/ldap.go index 5f5bb9ced..56fb18c5b 100644 --- a/internal/repository/idp/ldap.go +++ b/internal/repository/idp/ldap.go @@ -22,6 +22,7 @@ type LDAPIDPAddedEvent struct { UserObjectClasses []string `json:"userObjectClasses"` UserFilters []string `json:"userFilters"` Timeout time.Duration `json:"timeout"` + RootCA []byte `json:"rootCA"` LDAPAttributes Options @@ -142,6 +143,7 @@ func NewLDAPIDPAddedEvent( userObjectClasses []string, userFilters []string, timeout time.Duration, + rootCA []byte, attributes LDAPAttributes, options Options, ) *LDAPIDPAddedEvent { @@ -158,6 +160,7 @@ func NewLDAPIDPAddedEvent( UserObjectClasses: userObjectClasses, UserFilters: userFilters, Timeout: timeout, + RootCA: rootCA, LDAPAttributes: attributes, Options: options, } @@ -198,6 +201,7 @@ type LDAPIDPChangedEvent struct { UserObjectClasses []string `json:"userObjectClasses,omitempty"` UserFilters []string `json:"userFilters,omitempty"` Timeout *time.Duration `json:"timeout,omitempty"` + RootCA []byte `json:"rootCA,omitempty"` LDAPAttributeChanges OptionChanges @@ -315,6 +319,12 @@ func ChangeLDAPTimeout(timeout time.Duration) func(*LDAPIDPChangedEvent) { } } +func ChangeLDAPRootCA(rootCA []byte) func(*LDAPIDPChangedEvent) { + return func(e *LDAPIDPChangedEvent) { + e.RootCA = rootCA + } +} + func ChangeLDAPAttributes(attributes LDAPAttributeChanges) func(*LDAPIDPChangedEvent) { return func(e *LDAPIDPChangedEvent) { e.LDAPAttributeChanges = attributes diff --git a/internal/repository/instance/idp.go b/internal/repository/instance/idp.go index d2f90ab4e..130a8d3d6 100644 --- a/internal/repository/instance/idp.go +++ b/internal/repository/instance/idp.go @@ -852,6 +852,7 @@ func NewLDAPIDPAddedEvent( userObjectClasses []string, userFilters []string, timeout time.Duration, + rootCA []byte, attributes idp.LDAPAttributes, options idp.Options, ) *LDAPIDPAddedEvent { @@ -874,6 +875,7 @@ func NewLDAPIDPAddedEvent( userObjectClasses, userFilters, timeout, + rootCA, attributes, options, ), diff --git a/internal/repository/org/idp.go b/internal/repository/org/idp.go index a2bc5c7ab..9510814e2 100644 --- a/internal/repository/org/idp.go +++ b/internal/repository/org/idp.go @@ -852,6 +852,7 @@ func NewLDAPIDPAddedEvent( userObjectClasses []string, userFilters []string, timeout time.Duration, + rootCA []byte, attributes idp.LDAPAttributes, options idp.Options, ) *LDAPIDPAddedEvent { @@ -874,6 +875,7 @@ func NewLDAPIDPAddedEvent( userObjectClasses, userFilters, timeout, + rootCA, attributes, options, ), diff --git a/proto/zitadel/admin.proto b/proto/zitadel/admin.proto index e1818833e..020fa60e9 100644 --- a/proto/zitadel/admin.proto +++ b/proto/zitadel/admin.proto @@ -6834,6 +6834,8 @@ message AddLDAPProviderRequest { google.protobuf.Duration timeout = 10; zitadel.idp.v1.LDAPAttributes attributes = 11; zitadel.idp.v1.Options provider_options = 12; + // Root_ca is for self signing certificates for TLS connections to LDAP servers it is intended to be filled with a .pem file. + bytes root_ca = 13 [(validate.rules).bytes.max_len = 12000]; } message AddLDAPProviderResponse { @@ -6855,6 +6857,8 @@ message UpdateLDAPProviderRequest { google.protobuf.Duration timeout = 11; zitadel.idp.v1.LDAPAttributes attributes = 12; zitadel.idp.v1.Options provider_options = 13; + // Root_ca is for self signing certificates for TLS connections to LDAP servers it is intended to be filled with a .pem file. + bytes root_ca = 14 [(validate.rules).bytes.max_len = 12000]; } message UpdateLDAPProviderResponse { diff --git a/proto/zitadel/idp.proto b/proto/zitadel/idp.proto index cbb2be9db..eecb9eae9 100644 --- a/proto/zitadel/idp.proto +++ b/proto/zitadel/idp.proto @@ -456,6 +456,7 @@ message LDAPConfig { repeated string user_filters = 7; google.protobuf.Duration timeout = 8; LDAPAttributes attributes = 9; + bytes root_ca = 10; } message SAMLConfig { diff --git a/proto/zitadel/idp/v2/idp.proto b/proto/zitadel/idp/v2/idp.proto index 784e717d3..0c95b742f 100644 --- a/proto/zitadel/idp/v2/idp.proto +++ b/proto/zitadel/idp/v2/idp.proto @@ -10,24 +10,23 @@ import "protoc-gen-openapiv2/options/annotations.proto"; import "validate/validate.proto"; import "google/protobuf/duration.proto"; - option go_package = "github.com/zitadel/zitadel/pkg/grpc/idp/v2;idp"; message IDP { // Unique identifier for the identity provider. - string id = 1 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"69629023906488334\""; - } - ]; + string id = 1 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "\"69629023906488334\""; + } ]; zitadel.object.v2.Details details = 2; // Current state of the identity provider. IDPState state = 3; - string name = 4 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"Google\""; - } - ]; + string name = 4 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "\"Google\""; + } ]; // Type of the identity provider, for example OIDC, JWT, LDAP and SAML. IDPType type = 5; // Configuration for the type of the identity provider. @@ -93,177 +92,189 @@ message IDPConfig { message JWTConfig { // The endpoint where the JWT can be extracted. string jwt_endpoint = 1 [ - (validate.rules).string = {min_len: 1, max_len: 200}, - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"https://accounts.google.com\""; + (validate.rules).string = {min_len : 1, max_len : 200}, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = + { + example: + "\"https://accounts.google.com\""; } ]; // The issuer of the JWT (for validation). string issuer = 2 [ - (validate.rules).string = {min_len: 1, max_len: 200}, - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"https://accounts.google.com\""; + (validate.rules).string = {min_len : 1, max_len : 200}, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = + { + example: + "\"https://accounts.google.com\""; } ]; // The endpoint to the key (JWK) which is used to sign the JWT with. string keys_endpoint = 3 [ - (validate.rules).string = {min_len: 1, max_len: 200}, - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"https://accounts.google.com/keys\""; + (validate.rules).string = {min_len : 1, max_len : 200}, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = + { + example: + "\"https://accounts.google.com/keys\""; } ]; // The name of the header where the JWT is sent in, default is authorization. string header_name = 4 [ - (validate.rules).string = {min_len: 1, max_len: 200}, - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"x-auth-token\""; + (validate.rules).string = {min_len : 1, max_len : 200}, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = + { + example: + "\"x-auth-token\""; } ]; } message OAuthConfig { // Client id generated by the identity provider. - string client_id = 1 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"client-id\""; - } - ]; + string client_id = 1 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "\"client-id\""; + } ]; // The endpoint where ZITADEL send the user to authenticate. - string authorization_endpoint = 2 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"https://accounts.google.com/o/oauth2/v2/auth\""; - } - ]; + string authorization_endpoint = 2 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "\"https://accounts.google.com/o/oauth2/v2/auth\""; + } ]; // The endpoint where ZITADEL can get the token. - string token_endpoint = 3 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"https://oauth2.googleapis.com/token\""; - } - ]; + string token_endpoint = 3 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "\"https://oauth2.googleapis.com/token\""; + } ]; // The endpoint where ZITADEL can get the user information. - string user_endpoint = 4 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"https://openidconnect.googleapis.com/v1/userinfo\""; - } - ]; - // The scopes requested by ZITADEL during the request on the identity provider. - repeated string scopes = 5 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "[\"openid\", \"profile\", \"email\"]"; - } - ]; - // Defines how the attribute is called where ZITADEL can get the id of the user. - string id_attribute = 6 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"user_id\""; - } - ]; + string user_endpoint = 4 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "\"https://openidconnect.googleapis.com/v1/userinfo\""; + } ]; + // The scopes requested by ZITADEL during the request on the identity + // provider. + repeated string scopes = 5 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "[\"openid\", \"profile\", \"email\"]"; + } ]; + // Defines how the attribute is called where ZITADEL can get the id of the + // user. + string id_attribute = 6 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "\"user_id\""; + } ]; } message GenericOIDCConfig { // The OIDC issuer of the identity provider. - string issuer = 1 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"https://accounts.google.com/\""; - } - ]; + string issuer = 1 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "\"https://accounts.google.com/\""; + } ]; // Client id generated by the identity provider. - string client_id = 2 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"client-id\""; - } - ]; - // The scopes requested by ZITADEL during the request on the identity provider. - repeated string scopes = 3 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "[\"openid\", \"profile\", \"email\"]"; - } - ]; - // If true, provider information get mapped from the id token, not from the userinfo endpoint. - bool is_id_token_mapping = 4 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "true"; - } - ]; + string client_id = 2 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "\"client-id\""; + } ]; + // The scopes requested by ZITADEL during the request on the identity + // provider. + repeated string scopes = 3 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "[\"openid\", \"profile\", \"email\"]"; + } ]; + // If true, provider information get mapped from the id token, not from the + // userinfo endpoint. + bool is_id_token_mapping = 4 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "true"; + } ]; } message GitHubConfig { // The client ID of the GitHub App. - string client_id = 1 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"client-id\""; - } - ]; + string client_id = 1 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "\"client-id\""; + } ]; // The scopes requested by ZITADEL during the request to GitHub. - repeated string scopes = 2 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "[\"openid\", \"profile\", \"email\"]"; - } - ]; + repeated string scopes = 2 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "[\"openid\", \"profile\", \"email\"]"; + } ]; } message GitHubEnterpriseServerConfig { // The client ID of the GitHub App. - string client_id = 1 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"client-id\""; - } - ]; + string client_id = 1 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "\"client-id\""; + } ]; string authorization_endpoint = 2; string token_endpoint = 3; string user_endpoint = 4; // The scopes requested by ZITADEL during the request to GitHub. - repeated string scopes = 5 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "[\"openid\", \"profile\", \"email\"]"; - } - ]; + repeated string scopes = 5 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "[\"openid\", \"profile\", \"email\"]"; + } ]; } message GoogleConfig { // Client id of the Google application. - string client_id = 1 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"client-id\""; - } - ]; + string client_id = 1 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "\"client-id\""; + } ]; // The scopes requested by ZITADEL during the request to Google. - repeated string scopes = 2 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "[\"openid\", \"profile\", \"email\"]"; - } - ]; + repeated string scopes = 2 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "[\"openid\", \"profile\", \"email\"]"; + } ]; } message GitLabConfig { // Client id of the GitLab application. - string client_id = 1 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"client-id\""; - } - ]; + string client_id = 1 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "\"client-id\""; + } ]; // The scopes requested by ZITADEL during the request to GitLab. - repeated string scopes = 2 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "[\"openid\", \"profile\", \"email\"]"; - } - ]; + repeated string scopes = 2 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "[\"openid\", \"profile\", \"email\"]"; + } ]; } message GitLabSelfHostedConfig { string issuer = 1; // Client id of the GitLab application. - string client_id = 2 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"client-id\""; - } - ]; + string client_id = 2 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "\"client-id\""; + } ]; // The scopes requested by ZITADEL during the request to GitLab. - repeated string scopes = 3 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "[\"openid\", \"profile\", \"email\"]"; - } - ]; + repeated string scopes = 3 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "[\"openid\", \"profile\", \"email\"]"; + } ]; } message LDAPConfig { @@ -276,6 +287,7 @@ message LDAPConfig { repeated string user_filters = 7; google.protobuf.Duration timeout = 8; LDAPAttributes attributes = 9; + bytes root_ca = 10; } message SAMLConfig { @@ -288,66 +300,84 @@ message SAMLConfig { // `nameid-format` for the SAML Request. SAMLNameIDFormat name_id_format = 4; // Optional name of the attribute, which will be used to map the user - // in case the nameid-format returned is `urn:oasis:names:tc:SAML:2.0:nameid-format:transient`. + // in case the nameid-format returned is + // `urn:oasis:names:tc:SAML:2.0:nameid-format:transient`. optional string transient_mapping_attribute_name = 5; } message AzureADConfig { // Client id of the Azure AD application - string client_id = 1 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"client-id\""; - } - ]; - // Defines what user accounts should be able to login (Personal, Organizational, All). + string client_id = 1 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "\"client-id\""; + } ]; + // Defines what user accounts should be able to login (Personal, + // Organizational, All). AzureADTenant tenant = 2; - // Azure AD doesn't send if the email has been verified. Enable this if the user email should always be added verified in ZITADEL (no verification emails will be sent). + // Azure AD doesn't send if the email has been verified. Enable this if the + // user email should always be added verified in ZITADEL (no verification + // emails will be sent). bool email_verified = 3; // The scopes requested by ZITADEL during the request to Azure AD. - repeated string scopes = 4 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "[\"openid\", \"profile\", \"email\", \"User.Read\"]"; - } - ]; + repeated string scopes = 4 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "[\"openid\", \"profile\", \"email\", \"User.Read\"]"; + } ]; } message Options { - // Enable if users should be able to link an existing ZITADEL user with an external account. + // Enable if users should be able to link an existing ZITADEL user with an + // external account. bool is_linking_allowed = 1; - // Enable if users should be able to create a new account in ZITADEL when using an external account. + // Enable if users should be able to create a new account in ZITADEL when + // using an external account. bool is_creation_allowed = 2; - // Enable if a new account in ZITADEL should be created automatically when login with an external account. + // Enable if a new account in ZITADEL should be created automatically when + // login with an external account. bool is_auto_creation = 3; - // Enable if a the ZITADEL account fields should be updated automatically on each login. + // Enable if a the ZITADEL account fields should be updated automatically on + // each login. bool is_auto_update = 4; - // Enable if users should get prompted to link an existing ZITADEL user to an external account if the selected attribute matches. - AutoLinkingOption auto_linking = 5 ; + // Enable if users should get prompted to link an existing ZITADEL user to an + // external account if the selected attribute matches. + AutoLinkingOption auto_linking = 5; } enum AutoLinkingOption { // AUTO_LINKING_OPTION_UNSPECIFIED disables the auto linking prompt. AUTO_LINKING_OPTION_UNSPECIFIED = 0; - // AUTO_LINKING_OPTION_USERNAME will use the username of the external user to check for a corresponding ZITADEL user. + // AUTO_LINKING_OPTION_USERNAME will use the username of the external user to + // check for a corresponding ZITADEL user. AUTO_LINKING_OPTION_USERNAME = 1; - // AUTO_LINKING_OPTION_EMAIL will use the email of the external user to check for a corresponding ZITADEL user with the same verified email - // Note that in case multiple users match, no prompt will be shown. + // AUTO_LINKING_OPTION_EMAIL will use the email of the external user to check + // for a corresponding ZITADEL user with the same verified email Note that in + // case multiple users match, no prompt will be shown. AUTO_LINKING_OPTION_EMAIL = 2; } message LDAPAttributes { - string id_attribute = 1 [(validate.rules).string = {max_len: 200}]; - string first_name_attribute = 2 [(validate.rules).string = {max_len: 200}]; - string last_name_attribute = 3 [(validate.rules).string = {max_len: 200}]; - string display_name_attribute = 4 [(validate.rules).string = {max_len: 200}]; - string nick_name_attribute = 5 [(validate.rules).string = {max_len: 200}]; - string preferred_username_attribute = 6 [(validate.rules).string = {max_len: 200}]; - string email_attribute = 7 [(validate.rules).string = {max_len: 200}]; - string email_verified_attribute = 8 [(validate.rules).string = {max_len: 200}]; - string phone_attribute = 9 [(validate.rules).string = {max_len: 200}]; - string phone_verified_attribute = 10 [(validate.rules).string = {max_len: 200}]; - string preferred_language_attribute = 11 [(validate.rules).string = {max_len: 200}]; - string avatar_url_attribute = 12 [(validate.rules).string = {max_len: 200}]; - string profile_attribute = 13 [(validate.rules).string = {max_len: 200}]; + string id_attribute = 1 [ (validate.rules).string = {max_len : 200} ]; + string first_name_attribute = 2 [ (validate.rules).string = {max_len : 200} ]; + string last_name_attribute = 3 [ (validate.rules).string = {max_len : 200} ]; + string display_name_attribute = 4 + [ (validate.rules).string = {max_len : 200} ]; + string nick_name_attribute = 5 [ (validate.rules).string = {max_len : 200} ]; + string preferred_username_attribute = 6 + [ (validate.rules).string = {max_len : 200} ]; + string email_attribute = 7 [ (validate.rules).string = {max_len : 200} ]; + string email_verified_attribute = 8 + [ (validate.rules).string = {max_len : 200} ]; + string phone_attribute = 9 [ (validate.rules).string = {max_len : 200} ]; + string phone_verified_attribute = 10 + [ (validate.rules).string = {max_len : 200} ]; + string preferred_language_attribute = 11 + [ (validate.rules).string = {max_len : 200} ]; + string avatar_url_attribute = 12 + [ (validate.rules).string = {max_len : 200} ]; + string profile_attribute = 13 [ (validate.rules).string = {max_len : 200} ]; + string root_ca= 14; } enum AzureADTenantType { @@ -365,27 +395,27 @@ message AzureADTenant { message AppleConfig { // Client id (App ID or Service ID) provided by Apple. - string client_id = 1 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"com.client.id\""; - } - ]; + string client_id = 1 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "\"com.client.id\""; + } ]; // Team ID provided by Apple. - string team_id = 2 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"ALT03JV3OS\""; - } - ]; + string team_id = 2 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "\"ALT03JV3OS\""; + } ]; // ID of the private key generated by Apple. - string key_id = 3 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"OGKDK25KD\""; - } - ]; + string key_id = 3 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "\"OGKDK25KD\""; + } ]; // The scopes requested by ZITADEL during the request to Apple. - repeated string scopes = 4 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "[\"name\", \"email\"]"; - } - ]; -} \ No newline at end of file + repeated string scopes = 4 + [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: + "[\"name\", \"email\"]"; + } ]; +} diff --git a/proto/zitadel/management.proto b/proto/zitadel/management.proto index 544498339..818600efe 100644 --- a/proto/zitadel/management.proto +++ b/proto/zitadel/management.proto @@ -13255,6 +13255,8 @@ message AddLDAPProviderRequest { google.protobuf.Duration timeout = 10; zitadel.idp.v1.LDAPAttributes attributes = 11; zitadel.idp.v1.Options provider_options = 12; + // Root_ca is for self signing certificates for TLS connections to LDAP servers it is intended to be filled with a .pem file. + bytes root_ca = 13 [(validate.rules).bytes.max_len = 12000]; } message AddLDAPProviderResponse { @@ -13276,6 +13278,8 @@ message UpdateLDAPProviderRequest { google.protobuf.Duration timeout = 11; zitadel.idp.v1.LDAPAttributes attributes = 12; zitadel.idp.v1.Options provider_options = 13; + // Root_ca is for self signing certificates for TLS connections to LDAP servers it is intended to be filled with a .pem file. + bytes root_ca = 14 [(validate.rules).bytes.max_len = 12000]; } message UpdateLDAPProviderResponse {