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

@@ -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) {