feat: directly specify factors/idps on addCustomLoginPolicy and return on LoginPolicy responses (#3711)

* feat: directly specify factors on addCustomLoginPolicy and return on LoginPolicy responses

* fix proto

* update login policy

* feat: directly specify idp on addCustomLoginPolicy and return on LoginPolicy responses

* fix: tests

Co-authored-by: Max Peintner <max@caos.ch>
Co-authored-by: Fabi <38692350+hifabienne@users.noreply.github.com>
This commit is contained in:
Livio Amstutz
2022-05-30 13:51:07 +02:00
committed by GitHub
parent 2fc39c0da0
commit b3f50702f8
19 changed files with 494 additions and 142 deletions

View File

@@ -1,6 +1,7 @@
package management
import (
idp_grpc "github.com/zitadel/zitadel/internal/api/grpc/idp"
"github.com/zitadel/zitadel/internal/api/grpc/object"
policy_grpc "github.com/zitadel/zitadel/internal/api/grpc/policy"
"github.com/zitadel/zitadel/internal/domain"
@@ -23,8 +24,21 @@ func addLoginPolicyToDomain(p *mgmt_pb.AddCustomLoginPolicyRequest) *domain.Logi
MFAInitSkipLifetime: p.MfaInitSkipLifetime.AsDuration(),
SecondFactorCheckLifetime: p.SecondFactorCheckLifetime.AsDuration(),
MultiFactorCheckLifetime: p.MultiFactorCheckLifetime.AsDuration(),
SecondFactors: policy_grpc.SecondFactorsTypesToDomain(p.SecondFactors),
MultiFactors: policy_grpc.MultiFactorsTypesToDomain(p.MultiFactors),
IDPProviders: addLoginPolicyIDPsToDomain(p.Idps),
}
}
func addLoginPolicyIDPsToDomain(idps []*mgmt_pb.AddCustomLoginPolicyRequest_IDP) []*domain.IDPProvider {
providers := make([]*domain.IDPProvider, len(idps))
for i, idp := range idps {
providers[i] = &domain.IDPProvider{
Type: idp_grpc.IDPProviderTypeFromPb(idp.OwnerType),
IDPConfigID: idp.IdpId,
}
}
return providers
}
func updateLoginPolicyToDomain(p *mgmt_pb.UpdateCustomLoginPolicyRequest) *domain.LoginPolicy {
return &domain.LoginPolicy{

View File

@@ -5,6 +5,14 @@ import (
policy_pb "github.com/zitadel/zitadel/pkg/grpc/policy"
)
func SecondFactorsTypesToDomain(secondFactorTypes []policy_pb.SecondFactorType) []domain.SecondFactorType {
types := make([]domain.SecondFactorType, len(secondFactorTypes))
for i, factorType := range secondFactorTypes {
types[i] = SecondFactorTypeToDomain(factorType)
}
return types
}
func SecondFactorTypeToDomain(secondFactorType policy_pb.SecondFactorType) domain.SecondFactorType {
switch secondFactorType {
case policy_pb.SecondFactorType_SECOND_FACTOR_TYPE_OTP:
@@ -35,6 +43,23 @@ func ModelSecondFactorTypeToPb(secondFactorType domain.SecondFactorType) policy_
}
}
func MultiFactorsTypesToDomain(multiFactorTypes []policy_pb.MultiFactorType) []domain.MultiFactorType {
types := make([]domain.MultiFactorType, len(multiFactorTypes))
for i, factorType := range multiFactorTypes {
types[i] = MultiFactorTypeToDomain(factorType)
}
return types
}
func MultiFactorTypeToDomain(multiFactorType policy_pb.MultiFactorType) domain.MultiFactorType {
switch multiFactorType {
case policy_pb.MultiFactorType_MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION:
return domain.MultiFactorTypeU2FWithPIN
default:
return domain.MultiFactorTypeUnspecified
}
}
func ModelMultiFactorTypesToPb(types []domain.MultiFactorType) []policy_pb.MultiFactorType {
t := make([]policy_pb.MultiFactorType, len(types))
for i, typ := range types {

View File

@@ -4,6 +4,7 @@ import (
"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/timestamppb"
idp_grpc "github.com/zitadel/zitadel/internal/api/grpc/idp"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/query"
"github.com/zitadel/zitadel/pkg/grpc/object"
@@ -26,6 +27,9 @@ func ModelLoginPolicyToPb(policy *query.LoginPolicy) *policy_pb.LoginPolicy {
MfaInitSkipLifetime: durationpb.New(policy.MFAInitSkipLifetime),
SecondFactorCheckLifetime: durationpb.New(policy.SecondFactorCheckLifetime),
MultiFactorCheckLifetime: durationpb.New(policy.MultiFactorCheckLifetime),
SecondFactors: ModelSecondFactorTypesToPb(policy.SecondFactors),
MultiFactors: ModelMultiFactorTypesToPb(policy.MultiFactors),
Idps: idp_grpc.IDPLoginPolicyLinksToPb(policy.IDPLinks),
Details: &object.ObjectDetails{
Sequence: policy.Sequence,
CreationDate: timestamppb.New(policy.CreationDate),

View File

@@ -1,15 +0,0 @@
package policy
import (
"github.com/zitadel/zitadel/internal/domain"
policy_pb "github.com/zitadel/zitadel/pkg/grpc/policy"
)
func MultiFactorTypeToDomain(multiFactorType policy_pb.MultiFactorType) domain.MultiFactorType {
switch multiFactorType {
case policy_pb.MultiFactorType_MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION:
return domain.MultiFactorTypeU2FWithPIN
default:
return domain.MultiFactorTypeUnspecified
}
}

View File

@@ -29,8 +29,7 @@ func (c *Commands) AddLoginPolicy(ctx context.Context, resourceOwner string, pol
}
orgAgg := OrgAggregateFromWriteModel(&addedPolicy.WriteModel)
pushedEvents, err := c.eventstore.Push(
ctx,
cmds := []eventstore.Command{
org.NewLoginPolicyAddedEvent(
ctx,
orgAgg,
@@ -46,7 +45,32 @@ func (c *Commands) AddLoginPolicy(ctx context.Context, resourceOwner string, pol
policy.ExternalLoginCheckLifetime,
policy.MFAInitSkipLifetime,
policy.SecondFactorCheckLifetime,
policy.MultiFactorCheckLifetime))
policy.MultiFactorCheckLifetime),
}
for _, factor := range policy.SecondFactors {
if !factor.Valid() {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-SFeea", "Errors.Org.LoginPolicy.MFA.Unspecified")
}
cmds = append(cmds, org.NewLoginPolicySecondFactorAddedEvent(ctx, orgAgg, factor))
}
for _, factor := range policy.MultiFactors {
if !factor.Valid() {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-WSfrg", "Errors.Org.LoginPolicy.MFA.Unspecified")
}
cmds = append(cmds, org.NewLoginPolicyMultiFactorAddedEvent(ctx, orgAgg, factor))
}
for _, provider := range policy.IDPProviders {
if provider.Type == domain.IdentityProviderTypeOrg {
_, err = c.getOrgIDPConfigByID(ctx, provider.IDPConfigID, resourceOwner)
} else {
_, err = c.getInstanceIDPConfigByID(ctx, provider.IDPConfigID)
}
if err != nil {
return nil, caos_errs.ThrowPreconditionFailed(err, "Org-FEd32", "Errors.IDPConfig.NotExisting")
}
cmds = append(cmds, org.NewIdentityProviderAddedEvent(ctx, orgAgg, provider.IDPConfigID, provider.Type))
}
pushedEvents, err := c.eventstore.Push(ctx, cmds...)
if err != nil {
return nil, err
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/repository"
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
"github.com/zitadel/zitadel/internal/repository/instance"
"github.com/zitadel/zitadel/internal/repository/org"
"github.com/zitadel/zitadel/internal/repository/policy"
"github.com/zitadel/zitadel/internal/repository/user"
@@ -183,6 +184,257 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
},
},
},
{
name: "add policy with invalid factors, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
policy: &domain.LoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
HidePasswordReset: true,
IgnoreUnknownUsernames: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
DefaultRedirectURI: "https://example.com/redirect",
PasswordCheckLifetime: time.Hour * 1,
ExternalLoginCheckLifetime: time.Hour * 2,
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeUnspecified},
},
},
res: res{
err: caos_errs.IsErrorInvalidArgument,
},
},
{
name: "add policy factors,ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
expectPush(
[]*repository.Event{
eventFromEventPusher(
org.NewLoginPolicyAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
true,
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"https://example.com/redirect",
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
eventFromEventPusher(
org.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
domain.SecondFactorTypeOTP,
),
),
eventFromEventPusher(
org.NewLoginPolicyMultiFactorAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
domain.MultiFactorTypeU2FWithPIN,
),
),
},
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
policy: &domain.LoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
HidePasswordReset: true,
IgnoreUnknownUsernames: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
DefaultRedirectURI: "https://example.com/redirect",
PasswordCheckLifetime: time.Hour * 1,
ExternalLoginCheckLifetime: time.Hour * 2,
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
MultiFactors: []domain.MultiFactorType{domain.MultiFactorTypeU2FWithPIN},
},
},
res: res{
want: &domain.LoginPolicy{
ObjectRoot: models.ObjectRoot{
AggregateID: "org1",
ResourceOwner: "org1",
},
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
HidePasswordReset: true,
IgnoreUnknownUsernames: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
DefaultRedirectURI: "https://example.com/redirect",
PasswordCheckLifetime: time.Hour * 1,
ExternalLoginCheckLifetime: time.Hour * 2,
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
},
},
},
{
name: "add policy with unknown idp, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
expectFilter(),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
policy: &domain.LoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
HidePasswordReset: true,
IgnoreUnknownUsernames: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
DefaultRedirectURI: "https://example.com/redirect",
PasswordCheckLifetime: time.Hour * 1,
ExternalLoginCheckLifetime: time.Hour * 2,
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
IDPProviders: []*domain.IDPProvider{
{
Type: domain.IdentityProviderTypeSystem,
IDPConfigID: "invalid",
},
},
},
},
res: res{
err: caos_errs.IsPreconditionFailed,
},
},
{
name: "add policy idp, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
expectFilter(
eventFromEventPusher(
instance.NewIDPConfigAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"config1",
"name1",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeGoogle,
true,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
org.NewLoginPolicyAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
true,
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"https://example.com/redirect",
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
eventFromEventPusher(
org.NewIdentityProviderAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"config1",
domain.IdentityProviderTypeSystem,
),
),
},
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
policy: &domain.LoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
HidePasswordReset: true,
IgnoreUnknownUsernames: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
DefaultRedirectURI: "https://example.com/redirect",
PasswordCheckLifetime: time.Hour * 1,
ExternalLoginCheckLifetime: time.Hour * 2,
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
IDPProviders: []*domain.IDPProvider{
{
Type: domain.IdentityProviderTypeSystem,
IDPConfigID: "config1",
},
},
},
},
res: res{
want: &domain.LoginPolicy{
ObjectRoot: models.ObjectRoot{
AggregateID: "org1",
ResourceOwner: "org1",
},
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
HidePasswordReset: true,
IgnoreUnknownUsernames: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
DefaultRedirectURI: "https://example.com/redirect",
PasswordCheckLifetime: time.Hour * 1,
ExternalLoginCheckLifetime: time.Hour * 2,
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View File

@@ -36,6 +36,7 @@ type LoginPolicy struct {
MFAInitSkipLifetime time.Duration
SecondFactorCheckLifetime time.Duration
MultiFactorCheckLifetime time.Duration
IDPLinks []*IDPLoginPolicyLink
}
type SecondFactors struct {
@@ -160,8 +161,11 @@ func (q *Queries) LoginPolicyByID(ctx context.Context, orgID string) (*LoginPoli
return nil, errors.ThrowInternal(err, "QUERY-scVHo", "Errors.Query.SQLStatement")
}
row := q.client.QueryRowContext(ctx, stmt, args...)
return scan(row)
rows, err := q.client.QueryContext(ctx, stmt, args...)
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-SWgr3", "Errors.Internal")
}
return scan(rows)
}
func (q *Queries) DefaultLoginPolicy(ctx context.Context) (*LoginPolicy, error) {
@@ -174,8 +178,11 @@ func (q *Queries) DefaultLoginPolicy(ctx context.Context) (*LoginPolicy, error)
return nil, errors.ThrowInternal(err, "QUERY-t4TBK", "Errors.Query.SQLStatement")
}
row := q.client.QueryRowContext(ctx, stmt, args...)
return scan(row)
rows, err := q.client.QueryContext(ctx, stmt, args...)
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-SArt2", "Errors.Internal")
}
return scan(rows)
}
func (q *Queries) SecondFactorsByOrg(ctx context.Context, orgID string) (*SecondFactors, error) {
@@ -278,7 +285,7 @@ func (q *Queries) DefaultMultiFactors(ctx context.Context) (*MultiFactors, error
return factors, err
}
func prepareLoginPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*LoginPolicy, error)) {
func prepareLoginPolicyQuery() (sq.SelectBuilder, func(*sql.Rows) (*LoginPolicy, error)) {
return sq.Select(
LoginPolicyColumnOrgID.identifier(),
LoginPolicyColumnCreationDate.identifier(),
@@ -300,39 +307,69 @@ func prepareLoginPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*LoginPolicy,
LoginPolicyColumnMFAInitSkipLifetime.identifier(),
LoginPolicyColumnSecondFactorCheckLifetime.identifier(),
LoginPolicyColumnMultiFacotrCheckLifetime.identifier(),
).From(loginPolicyTable.identifier()).PlaceholderFormat(sq.Dollar),
func(row *sql.Row) (*LoginPolicy, error) {
IDPLoginPolicyLinkIDPIDCol.identifier(),
IDPNameCol.identifier(),
IDPTypeCol.identifier(),
).From(loginPolicyTable.identifier()).
LeftJoin(join(IDPLoginPolicyLinkIDPIDCol, LoginPolicyColumnOrgID)).
LeftJoin(join(IDPIDCol, IDPLoginPolicyLinkIDPIDCol)).
PlaceholderFormat(sq.Dollar),
func(rows *sql.Rows) (*LoginPolicy, error) {
p := new(LoginPolicy)
secondFactors := pq.Int32Array{}
multiFactors := pq.Int32Array{}
defaultRedirectURI := sql.NullString{}
err := row.Scan(
&p.OrgID,
&p.CreationDate,
&p.ChangeDate,
&p.Sequence,
&p.AllowRegister,
&p.AllowUsernamePassword,
&p.AllowExternalIDPs,
&p.ForceMFA,
&secondFactors,
&multiFactors,
&p.PasswordlessType,
&p.IsDefault,
&p.HidePasswordReset,
&p.IgnoreUnknownUsernames,
&defaultRedirectURI,
&p.PasswordCheckLifetime,
&p.ExternalLoginCheckLifetime,
&p.MFAInitSkipLifetime,
&p.SecondFactorCheckLifetime,
&p.MultiFactorCheckLifetime,
)
if err != nil {
if errs.Is(err, sql.ErrNoRows) {
return nil, errors.ThrowNotFound(err, "QUERY-QsUBJ", "Errors.LoginPolicy.NotFound")
links := make([]*IDPLoginPolicyLink, 0)
for rows.Next() {
var (
idpID = sql.NullString{}
idpName = sql.NullString{}
idpType = sql.NullInt16{}
)
err := rows.Scan(
&p.OrgID,
&p.CreationDate,
&p.ChangeDate,
&p.Sequence,
&p.AllowRegister,
&p.AllowUsernamePassword,
&p.AllowExternalIDPs,
&p.ForceMFA,
&secondFactors,
&multiFactors,
&p.PasswordlessType,
&p.IsDefault,
&p.HidePasswordReset,
&p.IgnoreUnknownUsernames,
&defaultRedirectURI,
&p.PasswordCheckLifetime,
&p.ExternalLoginCheckLifetime,
&p.MFAInitSkipLifetime,
&p.SecondFactorCheckLifetime,
&p.MultiFactorCheckLifetime,
&idpID,
&idpName,
&idpType,
)
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-YcC53", "Errors.Internal")
}
return nil, errors.ThrowInternal(err, "QUERY-YcC53", "Errors.Internal")
var link IDPLoginPolicyLink
if idpID.Valid {
link = IDPLoginPolicyLink{IDPID: idpID.String}
link.IDPName = idpName.String
//IDPType 0 is oidc so we have to set unspecified manually
if idpType.Valid {
link.IDPType = domain.IDPConfigType(idpType.Int16)
} else {
link.IDPType = domain.IDPConfigTypeUnspecified
}
links = append(links, &link)
}
}
if p.OrgID == "" {
return nil, errors.ThrowNotFound(nil, "QUERY-QsUBJ", "Errors.LoginPolicy.NotFound")
}
p.DefaultRedirectURI = defaultRedirectURI.String
p.MultiFactors = make([]domain.MultiFactorType, len(multiFactors))
@@ -343,6 +380,7 @@ func prepareLoginPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*LoginPolicy,
for i, mfa := range secondFactors {
p.SecondFactors[i] = domain.SecondFactorType(mfa)
}
p.IDPLinks = links
return p, nil
}
}

View File

@@ -50,8 +50,15 @@ func Test_LoginPolicyPrepares(t *testing.T) {
` projections.login_policies.external_login_check_lifetime,`+
` projections.login_policies.mfa_init_skip_lifetime,`+
` projections.login_policies.second_factor_check_lifetime,`+
` projections.login_policies.multi_factor_check_lifetime`+
` FROM projections.login_policies`),
` projections.login_policies.multi_factor_check_lifetime,`+
` projections.idp_login_policy_links.idp_id,`+
` projections.idps.name,`+
` projections.idps.type`+
` FROM projections.login_policies`+
` LEFT JOIN projections.idp_login_policy_links ON `+
` projections.login_policies.aggregate_id = projections.idp_login_policy_links.idp_id`+
` LEFT JOIN projections.idps ON`+
` projections.idp_login_policy_links.idp_id = projections.idps.id`),
nil,
nil,
),
@@ -88,8 +95,15 @@ func Test_LoginPolicyPrepares(t *testing.T) {
` projections.login_policies.external_login_check_lifetime,`+
` projections.login_policies.mfa_init_skip_lifetime,`+
` projections.login_policies.second_factor_check_lifetime,`+
` projections.login_policies.multi_factor_check_lifetime`+
` FROM projections.login_policies`),
` projections.login_policies.multi_factor_check_lifetime,`+
` projections.idp_login_policy_links.idp_id,`+
` projections.idps.name,`+
` projections.idps.type`+
` FROM projections.login_policies`+
` LEFT JOIN projections.idp_login_policy_links ON `+
` projections.login_policies.aggregate_id = projections.idp_login_policy_links.idp_id`+
` LEFT JOIN projections.idps ON`+
` projections.idp_login_policy_links.idp_id = projections.idps.id`),
[]string{
"aggregate_id",
"creation_date",
@@ -111,6 +125,9 @@ func Test_LoginPolicyPrepares(t *testing.T) {
"mfa_init_skip_lifetime",
"second_factor_check_lifetime",
"multi_factor_check_lifetime",
"idp_id",
"name",
"type",
},
[]driver.Value{
"ro",
@@ -133,6 +150,9 @@ func Test_LoginPolicyPrepares(t *testing.T) {
time.Hour * 2,
time.Hour * 2,
time.Hour * 2,
"config1",
"IDP",
domain.IDPConfigTypeJWT,
},
),
},
@@ -157,6 +177,13 @@ func Test_LoginPolicyPrepares(t *testing.T) {
MFAInitSkipLifetime: time.Hour * 2,
SecondFactorCheckLifetime: time.Hour * 2,
MultiFactorCheckLifetime: time.Hour * 2,
IDPLinks: []*IDPLoginPolicyLink{
{
IDPID: "config1",
IDPName: "IDP",
IDPType: domain.IDPConfigTypeJWT,
},
},
},
},
{
@@ -183,8 +210,15 @@ func Test_LoginPolicyPrepares(t *testing.T) {
` projections.login_policies.external_login_check_lifetime,`+
` projections.login_policies.mfa_init_skip_lifetime,`+
` projections.login_policies.second_factor_check_lifetime,`+
` projections.login_policies.multi_factor_check_lifetime`+
` FROM projections.login_policies`),
` projections.login_policies.multi_factor_check_lifetime,`+
` projections.idp_login_policy_links.idp_id,`+
` projections.idps.name,`+
` projections.idps.type`+
` FROM projections.login_policies`+
` LEFT JOIN projections.idp_login_policy_links ON `+
` projections.login_policies.aggregate_id = projections.idp_login_policy_links.idp_id`+
` LEFT JOIN projections.idps ON`+
` projections.idp_login_policy_links.idp_id = projections.idps.id`),
sql.ErrConnDone,
),
err: func(err error) (error, bool) {

View File

@@ -87,7 +87,7 @@ func NewAppProjection(ctx context.Context, config crdb.StatementHandlerConfig) *
crdb.NewColumn(AppAPIConfigColumnClientSecret, crdb.ColumnTypeJSONB, crdb.Nullable()),
crdb.NewColumn(AppAPIConfigColumnAuthMethod, crdb.ColumnTypeEnum),
},
crdb.NewPrimaryKey(AppAPIConfigColumnAppID),
crdb.NewPrimaryKey(AppAPIConfigColumnAppID, AppAPIConfigColumnInstanceID),
appAPITableSuffix,
crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys("fk_api_ref_apps")),
crdb.WithIndex(crdb.NewIndex("client_id_idx", []string{AppAPIConfigColumnClientID})),

View File

@@ -76,7 +76,7 @@ func NewIDPProjection(ctx context.Context, config crdb.StatementHandlerConfig) *
crdb.NewColumn(IDPStylingTypeCol, crdb.ColumnTypeEnum),
crdb.NewColumn(IDPOwnerTypeCol, crdb.ColumnTypeEnum),
crdb.NewColumn(IDPAutoRegisterCol, crdb.ColumnTypeBool, crdb.Default(false)),
crdb.NewColumn(IDPTypeCol, crdb.ColumnTypeEnum),
crdb.NewColumn(IDPTypeCol, crdb.ColumnTypeEnum, crdb.Nullable()),
},
crdb.NewPrimaryKey(IDPIDCol, IDPInstanceIDCol),
crdb.WithIndex(crdb.NewIndex("ro_idx", []string{IDPResourceOwnerCol})),
@@ -92,9 +92,9 @@ func NewIDPProjection(ctx context.Context, config crdb.StatementHandlerConfig) *
crdb.NewColumn(OIDCConfigDisplayNameMappingCol, crdb.ColumnTypeEnum, crdb.Nullable()),
crdb.NewColumn(OIDCConfigUsernameMappingCol, crdb.ColumnTypeEnum, crdb.Nullable()),
crdb.NewColumn(OIDCConfigAuthorizationEndpointCol, crdb.ColumnTypeText, crdb.Nullable()),
crdb.NewColumn(OIDCConfigTokenEndpointCol, crdb.ColumnTypeEnum, crdb.Nullable()),
crdb.NewColumn(OIDCConfigTokenEndpointCol, crdb.ColumnTypeText, crdb.Nullable()),
},
crdb.NewPrimaryKey(OIDCConfigIDPIDCol),
crdb.NewPrimaryKey(OIDCConfigIDPIDCol, OIDCConfigInstanceIDCol),
IDPOIDCSuffix,
crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys("fk_oidc_ref_idp")),
),
@@ -106,7 +106,7 @@ func NewIDPProjection(ctx context.Context, config crdb.StatementHandlerConfig) *
crdb.NewColumn(JWTConfigHeaderNameCol, crdb.ColumnTypeText, crdb.Nullable()),
crdb.NewColumn(JWTConfigEndpointCol, crdb.ColumnTypeText, crdb.Nullable()),
},
crdb.NewPrimaryKey(JWTConfigIDPIDCol),
crdb.NewPrimaryKey(JWTConfigIDPIDCol, JWTConfigInstanceIDCol),
IDPJWTSuffix,
crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys("fk_jwt_ref_idp")),
),

View File

@@ -60,7 +60,7 @@ func NewSMSConfigProjection(ctx context.Context, config crdb.StatementHandlerCon
crdb.NewColumn(SMSTwilioConfigColumnSenderNumber, crdb.ColumnTypeText),
crdb.NewColumn(SMSTwilioConfigColumnToken, crdb.ColumnTypeJSONB),
},
crdb.NewPrimaryKey(SMSTwilioConfigColumnSMSID),
crdb.NewPrimaryKey(SMSTwilioConfigColumnSMSID, SMSTwilioColumnInstanceID),
smsTwilioTableSuffix,
crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys("fk_twilio_ref_sms")),
),