package query import ( "context" "database/sql" errs "errors" "time" sq "github.com/Masterminds/squirrel" "github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/query/projection" ) type Features struct { AggregateID string ChangeDate time.Time Sequence uint64 IsDefault bool TierName string TierDescription string State domain.FeaturesState StateDescription string AuditLogRetention time.Duration LoginPolicyFactors bool LoginPolicyIDP bool LoginPolicyPasswordless bool LoginPolicyRegistration bool LoginPolicyUsernameLogin bool LoginPolicyPasswordReset bool PasswordComplexityPolicy bool LabelPolicyPrivateLabel bool LabelPolicyWatermark bool CustomDomain bool PrivacyPolicy bool MetadataUser bool CustomTextMessage bool CustomTextLogin bool LockoutPolicy bool ActionsAllowed domain.ActionsAllowed MaxActions int32 } var ( featureTable = table{ name: projection.FeatureTable, } FeatureColumnAggregateID = Column{ name: projection.FeatureAggregateIDCol, table: featureTable, } FeatureColumnInstanceID = Column{ name: projection.FeatureInstanceIDCol, table: featureTable, } FeatureColumnChangeDate = Column{ name: projection.FeatureChangeDateCol, table: featureTable, } FeatureColumnSequence = Column{ name: projection.FeatureSequenceCol, table: featureTable, } FeatureColumnIsDefault = Column{ name: projection.FeatureIsDefaultCol, table: featureTable, } FeatureTierName = Column{ name: projection.FeatureTierNameCol, table: featureTable, } FeatureTierDescription = Column{ name: projection.FeatureTierDescriptionCol, table: featureTable, } FeatureState = Column{ name: projection.FeatureStateCol, table: featureTable, } FeatureStateDescription = Column{ name: projection.FeatureStateDescriptionCol, table: featureTable, } FeatureAuditLogRetention = Column{ name: projection.FeatureAuditLogRetentionCol, table: featureTable, } FeatureLoginPolicyFactors = Column{ name: projection.FeatureLoginPolicyFactorsCol, table: featureTable, } FeatureLoginPolicyIDP = Column{ name: projection.FeatureLoginPolicyIDPCol, table: featureTable, } FeatureLoginPolicyPasswordless = Column{ name: projection.FeatureLoginPolicyPasswordlessCol, table: featureTable, } FeatureLoginPolicyRegistration = Column{ name: projection.FeatureLoginPolicyRegistrationCol, table: featureTable, } FeatureLoginPolicyUsernameLogin = Column{ name: projection.FeatureLoginPolicyUsernameLoginCol, table: featureTable, } FeatureLoginPolicyPasswordReset = Column{ name: projection.FeatureLoginPolicyPasswordResetCol, table: featureTable, } FeaturePasswordComplexityPolicy = Column{ name: projection.FeaturePasswordComplexityPolicyCol, table: featureTable, } FeatureLabelPolicyPrivateLabel = Column{ name: projection.FeatureLabelPolicyPrivateLabelCol, table: featureTable, } FeatureLabelPolicyWatermark = Column{ name: projection.FeatureLabelPolicyWatermarkCol, table: featureTable, } FeatureCustomDomain = Column{ name: projection.FeatureCustomDomainCol, table: featureTable, } FeaturePrivacyPolicy = Column{ name: projection.FeaturePrivacyPolicyCol, table: featureTable, } FeatureMetadataUser = Column{ name: projection.FeatureMetadataUserCol, table: featureTable, } FeatureCustomTextMessage = Column{ name: projection.FeatureCustomTextMessageCol, table: featureTable, } FeatureCustomTextLogin = Column{ name: projection.FeatureCustomTextLoginCol, table: featureTable, } FeatureLockoutPolicy = Column{ name: projection.FeatureLockoutPolicyCol, table: featureTable, } FeatureActionsAllowed = Column{ name: projection.FeatureActionsAllowedCol, table: featureTable, } FeatureMaxActions = Column{ name: projection.FeatureMaxActionsCol, table: featureTable, } ) func (q *Queries) FeaturesByOrgID(ctx context.Context, orgID string) (*Features, error) { query, scan := prepareFeaturesQuery() stmt, args, err := query.Where( sq.And{ sq.Eq{ FeatureColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(), }, sq.Or{ sq.Eq{ FeatureColumnAggregateID.identifier(): orgID, }, sq.Eq{ FeatureColumnAggregateID.identifier(): authz.GetInstance(ctx).InstanceID(), }, }, }). OrderBy(FeatureColumnIsDefault.identifier()). Limit(1).ToSql() if err != nil { return nil, errors.ThrowInternal(err, "QUERY-P9gwg", "Errors.Query.SQLStatement") } row := q.client.QueryRowContext(ctx, stmt, args...) return scan(row) } func (q *Queries) DefaultFeatures(ctx context.Context) (*Features, error) { query, scan := prepareFeaturesQuery() stmt, args, err := query.Where(sq.Eq{ FeatureColumnAggregateID.identifier(): authz.GetInstance(ctx).InstanceID(), FeatureColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(), }).ToSql() if err != nil { return nil, errors.ThrowInternal(err, "QUERY-1Ndlg", "Errors.Query.SQLStatement") } row := q.client.QueryRowContext(ctx, stmt, args...) return scan(row) } func prepareFeaturesQuery() (sq.SelectBuilder, func(*sql.Row) (*Features, error)) { return sq.Select( FeatureColumnAggregateID.identifier(), FeatureColumnChangeDate.identifier(), FeatureColumnSequence.identifier(), FeatureColumnIsDefault.identifier(), FeatureTierName.identifier(), FeatureTierDescription.identifier(), FeatureState.identifier(), FeatureStateDescription.identifier(), FeatureAuditLogRetention.identifier(), FeatureLoginPolicyFactors.identifier(), FeatureLoginPolicyIDP.identifier(), FeatureLoginPolicyPasswordless.identifier(), FeatureLoginPolicyRegistration.identifier(), FeatureLoginPolicyUsernameLogin.identifier(), FeatureLoginPolicyPasswordReset.identifier(), FeaturePasswordComplexityPolicy.identifier(), FeatureLabelPolicyPrivateLabel.identifier(), FeatureLabelPolicyWatermark.identifier(), FeatureCustomDomain.identifier(), FeaturePrivacyPolicy.identifier(), FeatureMetadataUser.identifier(), FeatureCustomTextMessage.identifier(), FeatureCustomTextLogin.identifier(), FeatureLockoutPolicy.identifier(), FeatureActionsAllowed.identifier(), FeatureMaxActions.identifier(), ).From(featureTable.identifier()).PlaceholderFormat(sq.Dollar), func(row *sql.Row) (*Features, error) { p := new(Features) tierName := sql.NullString{} tierDescription := sql.NullString{} stateDescription := sql.NullString{} err := row.Scan( &p.AggregateID, &p.ChangeDate, &p.Sequence, &p.IsDefault, &tierName, &tierDescription, &p.State, &stateDescription, &p.AuditLogRetention, &p.LoginPolicyFactors, &p.LoginPolicyIDP, &p.LoginPolicyPasswordless, &p.LoginPolicyRegistration, &p.LoginPolicyUsernameLogin, &p.LoginPolicyPasswordReset, &p.PasswordComplexityPolicy, &p.LabelPolicyPrivateLabel, &p.LabelPolicyWatermark, &p.CustomDomain, &p.PrivacyPolicy, &p.MetadataUser, &p.CustomTextMessage, &p.CustomTextLogin, &p.LockoutPolicy, &p.ActionsAllowed, &p.MaxActions, ) if err != nil { if errs.Is(err, sql.ErrNoRows) { return nil, errors.ThrowNotFound(err, "QUERY-M9fse", "Errors.Features.NotFound") } return nil, errors.ThrowInternal(err, "QUERY-3o9gd", "Errors.Internal") } p.TierName = tierName.String p.TierDescription = tierDescription.String p.StateDescription = stateDescription.String return p, nil } } func (f *Features) EnabledFeatureTypes() []string { list := make([]string, 0) if f.LoginPolicyFactors { list = append(list, domain.FeatureLoginPolicyFactors) } if f.LoginPolicyIDP { list = append(list, domain.FeatureLoginPolicyIDP) } if f.LoginPolicyPasswordless { list = append(list, domain.FeatureLoginPolicyPasswordless) } if f.LoginPolicyRegistration { list = append(list, domain.FeatureLoginPolicyRegistration) } if f.LoginPolicyUsernameLogin { list = append(list, domain.FeatureLoginPolicyUsernameLogin) } if f.LoginPolicyPasswordReset { list = append(list, domain.FeatureLoginPolicyPasswordReset) } if f.PasswordComplexityPolicy { list = append(list, domain.FeaturePasswordComplexityPolicy) } if f.LabelPolicyPrivateLabel { list = append(list, domain.FeatureLabelPolicyPrivateLabel) } if f.LabelPolicyWatermark { list = append(list, domain.FeatureLabelPolicyWatermark) } if f.CustomDomain { list = append(list, domain.FeatureCustomDomain) } if f.PrivacyPolicy { list = append(list, domain.FeaturePrivacyPolicy) } if f.MetadataUser { list = append(list, domain.FeatureMetadataUser) } if f.CustomTextMessage { list = append(list, domain.FeatureCustomTextMessage) } if f.CustomTextLogin { list = append(list, domain.FeatureCustomTextLogin) } if f.LockoutPolicy { list = append(list, domain.FeatureLockoutPolicy) } if f.ActionsAllowed != domain.ActionsNotAllowed { list = append(list, domain.FeatureActions) } return list }