package query import ( "context" "database/sql" errs "errors" "time" sq "github.com/Masterminds/squirrel" "github.com/zitadel/logging" "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/repository/idp" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) type IDPTemplate struct { CreationDate time.Time ChangeDate time.Time Sequence uint64 ResourceOwner string ID string State domain.IDPState Name string Type domain.IDPType OwnerType domain.IdentityProviderType IsCreationAllowed bool IsLinkingAllowed bool IsAutoCreation bool IsAutoUpdate bool *LDAPIDPTemplate } type IDPTemplates struct { SearchResponse Templates []*IDPTemplate } type LDAPIDPTemplate struct { IDPID string Host string Port string TLS bool BaseDN string UserObjectClass string UserUniqueAttribute string Admin string Password *crypto.CryptoValue idp.LDAPAttributes idp.Options } var ( idpTemplateTable = table{ name: projection.IDPTemplateTable, instanceIDCol: projection.IDPTemplateInstanceIDCol, } IDPTemplateIDCol = Column{ name: projection.IDPTemplateIDCol, table: idpTemplateTable, } IDPTemplateCreationDateCol = Column{ name: projection.IDPTemplateCreationDateCol, table: idpTemplateTable, } IDPTemplateChangeDateCol = Column{ name: projection.IDPTemplateChangeDateCol, table: idpTemplateTable, } IDPTemplateSequenceCol = Column{ name: projection.IDPTemplateSequenceCol, table: idpTemplateTable, } IDPTemplateResourceOwnerCol = Column{ name: projection.IDPTemplateResourceOwnerCol, table: idpTemplateTable, } IDPTemplateInstanceIDCol = Column{ name: projection.IDPTemplateInstanceIDCol, table: idpTemplateTable, } IDPTemplateStateCol = Column{ name: projection.IDPTemplateStateCol, table: idpTemplateTable, } IDPTemplateNameCol = Column{ name: projection.IDPTemplateNameCol, table: idpTemplateTable, } IDPTemplateOwnerTypeCol = Column{ name: projection.IDPOwnerTypeCol, table: idpTemplateTable, } IDPTemplateTypeCol = Column{ name: projection.IDPTemplateTypeCol, table: idpTemplateTable, } IDPTemplateOwnerRemovedCol = Column{ name: projection.IDPTemplateOwnerRemovedCol, table: idpTemplateTable, } IDPTemplateIsCreationAllowedCol = Column{ name: projection.IDPTemplateIsCreationAllowedCol, table: idpTemplateTable, } IDPTemplateIsLinkingAllowedCol = Column{ name: projection.IDPTemplateIsLinkingAllowedCol, table: idpTemplateTable, } IDPTemplateIsAutoCreationCol = Column{ name: projection.IDPTemplateIsAutoCreationCol, table: idpTemplateTable, } IDPTemplateIsAutoUpdateCol = Column{ name: projection.IDPTemplateIsAutoUpdateCol, table: idpTemplateTable, } ) var ( ldapIdpTemplateTable = table{ name: projection.IDPTemplateLDAPTable, instanceIDCol: projection.IDPTemplateInstanceIDCol, } LDAPIDCol = Column{ name: projection.LDAPIDCol, table: ldapIdpTemplateTable, } LDAPInstanceIDCol = Column{ name: projection.LDAPInstanceIDCol, table: ldapIdpTemplateTable, } LDAPHostCol = Column{ name: projection.LDAPHostCol, table: ldapIdpTemplateTable, } LDAPPortCol = Column{ name: projection.LDAPPortCol, table: ldapIdpTemplateTable, } LDAPTlsCol = Column{ name: projection.LDAPTlsCol, table: ldapIdpTemplateTable, } LDAPBaseDNCol = Column{ name: projection.LDAPBaseDNCol, table: ldapIdpTemplateTable, } LDAPUserObjectClassCol = Column{ name: projection.LDAPUserObjectClassCol, table: ldapIdpTemplateTable, } LDAPUserUniqueAttributeCol = Column{ name: projection.LDAPUserUniqueAttributeCol, table: ldapIdpTemplateTable, } LDAPAdminCol = Column{ name: projection.LDAPAdminCol, table: ldapIdpTemplateTable, } LDAPPasswordCol = Column{ name: projection.LDAPPasswordCol, table: ldapIdpTemplateTable, } LDAPIDAttributeCol = Column{ name: projection.LDAPIDAttributeCol, table: ldapIdpTemplateTable, } LDAPFirstNameAttributeCol = Column{ name: projection.LDAPFirstNameAttributeCol, table: ldapIdpTemplateTable, } LDAPLastNameAttributeCol = Column{ name: projection.LDAPLastNameAttributeCol, table: ldapIdpTemplateTable, } LDAPDisplayNameAttributeCol = Column{ name: projection.LDAPDisplayNameAttributeCol, table: ldapIdpTemplateTable, } LDAPNickNameAttributeCol = Column{ name: projection.LDAPNickNameAttributeCol, table: ldapIdpTemplateTable, } LDAPPreferredUsernameAttributeCol = Column{ name: projection.LDAPPreferredUsernameAttributeCol, table: ldapIdpTemplateTable, } LDAPEmailAttributeCol = Column{ name: projection.LDAPEmailAttributeCol, table: ldapIdpTemplateTable, } LDAPEmailVerifiedAttributeCol = Column{ name: projection.LDAPEmailVerifiedAttributeCol, table: ldapIdpTemplateTable, } LDAPPhoneAttributeCol = Column{ name: projection.LDAPPhoneAttributeCol, table: ldapIdpTemplateTable, } LDAPPhoneVerifiedAttributeCol = Column{ name: projection.LDAPPhoneVerifiedAttributeCol, table: ldapIdpTemplateTable, } LDAPPreferredLanguageAttributeCol = Column{ name: projection.LDAPPreferredLanguageAttributeCol, table: ldapIdpTemplateTable, } LDAPAvatarURLAttributeCol = Column{ name: projection.LDAPAvatarURLAttributeCol, table: ldapIdpTemplateTable, } LDAPProfileAttributeCol = Column{ name: projection.LDAPProfileAttributeCol, table: ldapIdpTemplateTable, } ) // IDPTemplateByIDAndResourceOwner searches for the requested id in the context of the resource owner and IAM func (q *Queries) IDPTemplateByIDAndResourceOwner(ctx context.Context, shouldTriggerBulk bool, id, resourceOwner string, withOwnerRemoved bool) (_ *IDPTemplate, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() if shouldTriggerBulk { err := projection.IDPTemplateProjection.Trigger(ctx) logging.OnError(err).WithField("projection", idpTemplateTable.identifier()).Warn("could not trigger projection for query") } eq := sq.Eq{ IDPTemplateIDCol.identifier(): id, IDPTemplateInstanceIDCol.identifier(): authz.GetInstance(ctx).InstanceID(), } if !withOwnerRemoved { eq[IDPTemplateOwnerRemovedCol.identifier()] = false } where := sq.And{ eq, sq.Or{ sq.Eq{IDPTemplateResourceOwnerCol.identifier(): resourceOwner}, sq.Eq{IDPTemplateResourceOwnerCol.identifier(): authz.GetInstance(ctx).InstanceID()}, }, } stmt, scan := prepareIDPTemplateByIDQuery() query, args, err := stmt.Where(where).ToSql() if err != nil { return nil, errors.ThrowInternal(err, "QUERY-SFAew", "Errors.Query.SQLStatement") } row := q.client.QueryRowContext(ctx, query, args...) return scan(row) } // IDPTemplates searches idp templates matching the query func (q *Queries) IDPTemplates(ctx context.Context, queries *IDPTemplateSearchQueries, withOwnerRemoved bool) (idps *IDPTemplates, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() query, scan := prepareIDPTemplatesQuery() eq := sq.Eq{ IDPTemplateInstanceIDCol.identifier(): authz.GetInstance(ctx).InstanceID(), } if !withOwnerRemoved { eq[IDPTemplateOwnerRemovedCol.identifier()] = false } stmt, args, err := queries.toQuery(query).Where(eq).ToSql() if err != nil { return nil, errors.ThrowInvalidArgument(err, "QUERY-SAF34", "Errors.Query.InvalidRequest") } rows, err := q.client.QueryContext(ctx, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-BDFrq", "Errors.Internal") } idps, err = scan(rows) if err != nil { return nil, err } idps.LatestSequence, err = q.latestSequence(ctx, idpTemplateTable) return idps, err } type IDPTemplateSearchQueries struct { SearchRequest Queries []SearchQuery } func NewIDPTemplateIDSearchQuery(id string) (SearchQuery, error) { return NewTextQuery(IDPTemplateIDCol, id, TextEquals) } func NewIDPTemplateOwnerTypeSearchQuery(ownerType domain.IdentityProviderType) (SearchQuery, error) { return NewNumberQuery(IDPTemplateOwnerTypeCol, ownerType, NumberEquals) } func NewIDPTemplateNameSearchQuery(method TextComparison, value string) (SearchQuery, error) { return NewTextQuery(IDPTemplateNameCol, value, method) } func NewIDPTemplateResourceOwnerSearchQuery(value string) (SearchQuery, error) { return NewTextQuery(IDPTemplateResourceOwnerCol, value, TextEquals) } func NewIDPTemplateResourceOwnerListSearchQuery(ids ...string) (SearchQuery, error) { list := make([]interface{}, len(ids)) for i, value := range ids { list[i] = value } return NewListQuery(IDPTemplateResourceOwnerCol, list, ListIn) } func (q *IDPTemplateSearchQueries) toQuery(query sq.SelectBuilder) sq.SelectBuilder { query = q.SearchRequest.toQuery(query) for _, q := range q.Queries { query = q.toQuery(query) } return query } func prepareIDPTemplateByIDQuery() (sq.SelectBuilder, func(*sql.Row) (*IDPTemplate, error)) { return sq.Select( IDPTemplateIDCol.identifier(), IDPTemplateResourceOwnerCol.identifier(), IDPTemplateCreationDateCol.identifier(), IDPTemplateChangeDateCol.identifier(), IDPTemplateSequenceCol.identifier(), IDPTemplateStateCol.identifier(), IDPTemplateNameCol.identifier(), IDPTemplateTypeCol.identifier(), IDPTemplateOwnerTypeCol.identifier(), IDPTemplateIsCreationAllowedCol.identifier(), IDPTemplateIsLinkingAllowedCol.identifier(), IDPTemplateIsAutoCreationCol.identifier(), IDPTemplateIsAutoUpdateCol.identifier(), LDAPIDCol.identifier(), LDAPHostCol.identifier(), LDAPPortCol.identifier(), LDAPTlsCol.identifier(), LDAPBaseDNCol.identifier(), LDAPUserObjectClassCol.identifier(), LDAPUserUniqueAttributeCol.identifier(), LDAPAdminCol.identifier(), LDAPPasswordCol.identifier(), LDAPIDAttributeCol.identifier(), LDAPFirstNameAttributeCol.identifier(), LDAPLastNameAttributeCol.identifier(), LDAPDisplayNameAttributeCol.identifier(), LDAPNickNameAttributeCol.identifier(), LDAPPreferredUsernameAttributeCol.identifier(), LDAPEmailAttributeCol.identifier(), LDAPEmailVerifiedAttributeCol.identifier(), LDAPPhoneAttributeCol.identifier(), LDAPPhoneVerifiedAttributeCol.identifier(), LDAPPreferredLanguageAttributeCol.identifier(), LDAPAvatarURLAttributeCol.identifier(), LDAPProfileAttributeCol.identifier(), ).From(idpTemplateTable.identifier()). LeftJoin(join(LDAPIDCol, IDPTemplateIDCol)). PlaceholderFormat(sq.Dollar), func(row *sql.Row) (*IDPTemplate, error) { idpTemplate := new(IDPTemplate) ldapID := sql.NullString{} ldapHost := sql.NullString{} ldapPort := sql.NullString{} ldapTls := sql.NullBool{} ldapBaseDN := sql.NullString{} ldapUserObjectClass := sql.NullString{} ldapUserUniqueAttribute := sql.NullString{} ldapAdmin := sql.NullString{} ldapPassword := new(crypto.CryptoValue) ldapIDAttribute := sql.NullString{} ldapFirstNameAttribute := sql.NullString{} ldapLastNameAttribute := sql.NullString{} ldapDisplayNameAttribute := sql.NullString{} ldapNickNameAttribute := sql.NullString{} ldapPreferredUsernameAttribute := sql.NullString{} ldapEmailAttribute := sql.NullString{} ldapEmailVerifiedAttribute := sql.NullString{} ldapPhoneAttribute := sql.NullString{} ldapPhoneVerifiedAttribute := sql.NullString{} ldapPreferredLanguageAttribute := sql.NullString{} ldapAvatarURLAttribute := sql.NullString{} ldapProfileAttribute := sql.NullString{} err := row.Scan( &idpTemplate.ID, &idpTemplate.ResourceOwner, &idpTemplate.CreationDate, &idpTemplate.ChangeDate, &idpTemplate.Sequence, &idpTemplate.State, &idpTemplate.Name, &idpTemplate.Type, &idpTemplate.OwnerType, &idpTemplate.IsCreationAllowed, &idpTemplate.IsLinkingAllowed, &idpTemplate.IsAutoCreation, &idpTemplate.IsAutoUpdate, &ldapID, &ldapHost, &ldapPort, &ldapTls, &ldapBaseDN, &ldapUserObjectClass, &ldapUserUniqueAttribute, &ldapAdmin, &ldapPassword, &ldapIDAttribute, &ldapFirstNameAttribute, &ldapLastNameAttribute, &ldapDisplayNameAttribute, &ldapNickNameAttribute, &ldapPreferredUsernameAttribute, &ldapEmailAttribute, &ldapEmailVerifiedAttribute, &ldapPhoneAttribute, &ldapPhoneVerifiedAttribute, &ldapPreferredLanguageAttribute, &ldapAvatarURLAttribute, &ldapProfileAttribute, ) if err != nil { if errs.Is(err, sql.ErrNoRows) { return nil, errors.ThrowNotFound(err, "QUERY-SAFrt", "Errors.IDPConfig.NotExisting") } return nil, errors.ThrowInternal(err, "QUERY-ADG42", "Errors.Internal") } if ldapID.Valid { idpTemplate.LDAPIDPTemplate = &LDAPIDPTemplate{ IDPID: ldapID.String, Host: ldapHost.String, Port: ldapPort.String, TLS: ldapTls.Bool, BaseDN: ldapBaseDN.String, UserObjectClass: ldapUserObjectClass.String, UserUniqueAttribute: ldapUserUniqueAttribute.String, Admin: ldapAdmin.String, Password: ldapPassword, LDAPAttributes: idp.LDAPAttributes{ IDAttribute: ldapIDAttribute.String, FirstNameAttribute: ldapFirstNameAttribute.String, LastNameAttribute: ldapLastNameAttribute.String, DisplayNameAttribute: ldapDisplayNameAttribute.String, NickNameAttribute: ldapNickNameAttribute.String, PreferredUsernameAttribute: ldapPreferredUsernameAttribute.String, EmailAttribute: ldapEmailAttribute.String, EmailVerifiedAttribute: ldapEmailVerifiedAttribute.String, PhoneAttribute: ldapPhoneAttribute.String, PhoneVerifiedAttribute: ldapPhoneVerifiedAttribute.String, PreferredLanguageAttribute: ldapPreferredLanguageAttribute.String, AvatarURLAttribute: ldapAvatarURLAttribute.String, ProfileAttribute: ldapProfileAttribute.String, }, } } return idpTemplate, nil } } func prepareIDPTemplatesQuery() (sq.SelectBuilder, func(*sql.Rows) (*IDPTemplates, error)) { return sq.Select( IDPTemplateIDCol.identifier(), IDPTemplateResourceOwnerCol.identifier(), IDPTemplateCreationDateCol.identifier(), IDPTemplateChangeDateCol.identifier(), IDPTemplateSequenceCol.identifier(), IDPTemplateStateCol.identifier(), IDPTemplateNameCol.identifier(), IDPTemplateTypeCol.identifier(), IDPTemplateOwnerTypeCol.identifier(), IDPTemplateIsCreationAllowedCol.identifier(), IDPTemplateIsLinkingAllowedCol.identifier(), IDPTemplateIsAutoCreationCol.identifier(), IDPTemplateIsAutoUpdateCol.identifier(), LDAPIDCol.identifier(), LDAPHostCol.identifier(), LDAPPortCol.identifier(), LDAPTlsCol.identifier(), LDAPBaseDNCol.identifier(), LDAPUserObjectClassCol.identifier(), LDAPUserUniqueAttributeCol.identifier(), LDAPAdminCol.identifier(), LDAPPasswordCol.identifier(), LDAPIDAttributeCol.identifier(), LDAPFirstNameAttributeCol.identifier(), LDAPLastNameAttributeCol.identifier(), LDAPDisplayNameAttributeCol.identifier(), LDAPNickNameAttributeCol.identifier(), LDAPPreferredUsernameAttributeCol.identifier(), LDAPEmailAttributeCol.identifier(), LDAPEmailVerifiedAttributeCol.identifier(), LDAPPhoneAttributeCol.identifier(), LDAPPhoneVerifiedAttributeCol.identifier(), LDAPPreferredLanguageAttributeCol.identifier(), LDAPAvatarURLAttributeCol.identifier(), LDAPProfileAttributeCol.identifier(), countColumn.identifier(), ).From(idpTemplateTable.identifier()). LeftJoin(join(LDAPIDCol, IDPTemplateIDCol)). PlaceholderFormat(sq.Dollar), func(rows *sql.Rows) (*IDPTemplates, error) { templates := make([]*IDPTemplate, 0) var count uint64 for rows.Next() { idpTemplate := new(IDPTemplate) ldapID := sql.NullString{} ldapHost := sql.NullString{} ldapPort := sql.NullString{} ldapTls := sql.NullBool{} ldapBaseDN := sql.NullString{} ldapUserObjectClass := sql.NullString{} ldapUserUniqueAttribute := sql.NullString{} ldapAdmin := sql.NullString{} ldapPassword := new(crypto.CryptoValue) ldapIDAttribute := sql.NullString{} ldapFirstNameAttribute := sql.NullString{} ldapLastNameAttribute := sql.NullString{} ldapDisplayNameAttribute := sql.NullString{} ldapNickNameAttribute := sql.NullString{} ldapPreferredUsernameAttribute := sql.NullString{} ldapEmailAttribute := sql.NullString{} ldapEmailVerifiedAttribute := sql.NullString{} ldapPhoneAttribute := sql.NullString{} ldapPhoneVerifiedAttribute := sql.NullString{} ldapPreferredLanguageAttribute := sql.NullString{} ldapAvatarURLAttribute := sql.NullString{} ldapProfileAttribute := sql.NullString{} err := rows.Scan( &idpTemplate.ID, &idpTemplate.ResourceOwner, &idpTemplate.CreationDate, &idpTemplate.ChangeDate, &idpTemplate.Sequence, &idpTemplate.State, &idpTemplate.Name, &idpTemplate.Type, &idpTemplate.OwnerType, &idpTemplate.IsCreationAllowed, &idpTemplate.IsLinkingAllowed, &idpTemplate.IsAutoCreation, &idpTemplate.IsAutoUpdate, &ldapID, &ldapHost, &ldapPort, &ldapTls, &ldapBaseDN, &ldapUserObjectClass, &ldapUserUniqueAttribute, &ldapAdmin, &ldapPassword, &ldapIDAttribute, &ldapFirstNameAttribute, &ldapLastNameAttribute, &ldapDisplayNameAttribute, &ldapNickNameAttribute, &ldapPreferredUsernameAttribute, &ldapEmailAttribute, &ldapEmailVerifiedAttribute, &ldapPhoneAttribute, &ldapPhoneVerifiedAttribute, &ldapPreferredLanguageAttribute, &ldapAvatarURLAttribute, &ldapProfileAttribute, &count, ) if err != nil { return nil, err } if ldapID.Valid { idpTemplate.LDAPIDPTemplate = &LDAPIDPTemplate{ IDPID: ldapID.String, Host: ldapHost.String, Port: ldapPort.String, TLS: ldapTls.Bool, BaseDN: ldapBaseDN.String, UserObjectClass: ldapUserObjectClass.String, UserUniqueAttribute: ldapUserUniqueAttribute.String, Admin: ldapAdmin.String, Password: ldapPassword, LDAPAttributes: idp.LDAPAttributes{ IDAttribute: ldapIDAttribute.String, FirstNameAttribute: ldapFirstNameAttribute.String, LastNameAttribute: ldapLastNameAttribute.String, DisplayNameAttribute: ldapDisplayNameAttribute.String, NickNameAttribute: ldapNickNameAttribute.String, PreferredUsernameAttribute: ldapPreferredUsernameAttribute.String, EmailAttribute: ldapEmailAttribute.String, EmailVerifiedAttribute: ldapEmailVerifiedAttribute.String, PhoneAttribute: ldapPhoneAttribute.String, PhoneVerifiedAttribute: ldapPhoneVerifiedAttribute.String, PreferredLanguageAttribute: ldapPreferredLanguageAttribute.String, AvatarURLAttribute: ldapAvatarURLAttribute.String, ProfileAttribute: ldapProfileAttribute.String, }, } } templates = append(templates, idpTemplate) } if err := rows.Close(); err != nil { return nil, errors.ThrowInternal(err, "QUERY-SAGrt", "Errors.Query.CloseRows") } return &IDPTemplates{ Templates: templates, SearchResponse: SearchResponse{ Count: count, }, }, nil } }