feat: Login verification lifetimes (#3190)

* feat: add login check lifetimes to login policy

* feat: org features test

* feat: read lifetimes from loginpolicy
This commit is contained in:
Fabi
2022-02-21 16:05:02 +01:00
committed by GitHub
parent 7d235e3eed
commit f05d4063bf
33 changed files with 1188 additions and 421 deletions

View File

@@ -35,13 +35,18 @@ func memberWriteModelToMember(writeModel *MemberWriteModel) *domain.Member {
func writeModelToLoginPolicy(wm *LoginPolicyWriteModel) *domain.LoginPolicy {
return &domain.LoginPolicy{
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
AllowUsernamePassword: wm.AllowUserNamePassword,
AllowRegister: wm.AllowRegister,
AllowExternalIDP: wm.AllowExternalIDP,
HidePasswordReset: wm.HidePasswordReset,
ForceMFA: wm.ForceMFA,
PasswordlessType: wm.PasswordlessType,
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
AllowUsernamePassword: wm.AllowUserNamePassword,
AllowRegister: wm.AllowRegister,
AllowExternalIDP: wm.AllowExternalIDP,
HidePasswordReset: wm.HidePasswordReset,
ForceMFA: wm.ForceMFA,
PasswordlessType: wm.PasswordlessType,
PasswordCheckLifetime: wm.PasswordCheckLifetime,
ExternalLoginCheckLifetime: wm.ExternalLoginCheckLifetime,
MFAInitSkipLifetime: wm.MFAInitSkipLifetime,
SecondFactorCheckLifetime: wm.SecondFactorCheckLifetime,
MultiFactorCheckLifetime: wm.MultiFactorCheckLifetime,
}
}

View File

@@ -49,7 +49,19 @@ func (c *Commands) addDefaultLoginPolicy(ctx context.Context, iamAgg *eventstore
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-2B0ps", "Errors.IAM.LoginPolicy.AlreadyExists")
}
return iam_repo.NewLoginPolicyAddedEvent(ctx, iamAgg, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIDP, policy.ForceMFA, policy.HidePasswordReset, policy.PasswordlessType), nil
return iam_repo.NewLoginPolicyAddedEvent(ctx,
iamAgg,
policy.AllowUsernamePassword,
policy.AllowRegister,
policy.AllowExternalIDP,
policy.ForceMFA,
policy.HidePasswordReset,
policy.PasswordlessType,
policy.PasswordCheckLifetime,
policy.ExternalLoginCheckLifetime,
policy.MFAInitSkipLifetime,
policy.SecondFactorCheckLifetime,
policy.MultiFactorCheckLifetime), nil
}
func (c *Commands) ChangeDefaultLoginPolicy(ctx context.Context, policy *domain.LoginPolicy) (*domain.LoginPolicy, error) {
@@ -78,7 +90,19 @@ func (c *Commands) changeDefaultLoginPolicy(ctx context.Context, iamAgg *eventst
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "IAM-M0sif", "Errors.IAM.LoginPolicy.NotFound")
}
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, iamAgg, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIDP, policy.ForceMFA, policy.HidePasswordReset, policy.PasswordlessType)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx,
iamAgg,
policy.AllowUsernamePassword,
policy.AllowRegister,
policy.AllowExternalIDP,
policy.ForceMFA,
policy.HidePasswordReset,
policy.PasswordlessType,
policy.PasswordCheckLifetime,
policy.ExternalLoginCheckLifetime,
policy.MFAInitSkipLifetime,
policy.SecondFactorCheckLifetime,
policy.MultiFactorCheckLifetime)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-5M9vdd", "Errors.IAM.LoginPolicy.NotChanged")
}

View File

@@ -2,6 +2,7 @@ package command
import (
"context"
"time"
"github.com/caos/zitadel/internal/eventstore"
@@ -65,6 +66,11 @@ func (wm *IAMLoginPolicyWriteModel) NewChangedEvent(
forceMFA,
hidePasswordReset bool,
passwordlessType domain.PasswordlessType,
passwordCheckLifetime,
externalLoginCheckLifetime,
mfaInitSkipLifetime,
secondFactorCheckLifetime,
multiFactorCheckLifetime time.Duration,
) (*iam.LoginPolicyChangedEvent, bool) {
changes := make([]policy.LoginPolicyChanges, 0)
@@ -86,6 +92,21 @@ func (wm *IAMLoginPolicyWriteModel) NewChangedEvent(
if wm.HidePasswordReset != hidePasswordReset {
changes = append(changes, policy.ChangeHidePasswordReset(hidePasswordReset))
}
if wm.PasswordCheckLifetime != passwordCheckLifetime {
changes = append(changes, policy.ChangePasswordCheckLifetime(passwordCheckLifetime))
}
if wm.ExternalLoginCheckLifetime != externalLoginCheckLifetime {
changes = append(changes, policy.ChangeExternalLoginCheckLifetime(externalLoginCheckLifetime))
}
if wm.MFAInitSkipLifetime != mfaInitSkipLifetime {
changes = append(changes, policy.ChangeMFAInitSkipLifetime(mfaInitSkipLifetime))
}
if wm.SecondFactorCheckLifetime != secondFactorCheckLifetime {
changes = append(changes, policy.ChangeSecondFactorCheckLifetime(secondFactorCheckLifetime))
}
if wm.MultiFactorCheckLifetime != multiFactorCheckLifetime {
changes = append(changes, policy.ChangeMultiFactorCheckLifetime(multiFactorCheckLifetime))
}
if len(changes) == 0 {
return nil, false
}

View File

@@ -3,6 +3,7 @@ package command
import (
"context"
"testing"
"time"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors"
@@ -49,6 +50,11 @@ func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) {
false,
false,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*1,
time.Hour*1,
time.Hour*1,
time.Hour*1,
),
),
),
@@ -83,6 +89,11 @@ func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
},
@@ -92,12 +103,17 @@ func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) {
args: args{
ctx: context.Background(),
policy: &domain.LoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
PasswordCheckLifetime: time.Hour * 1,
ExternalLoginCheckLifetime: time.Hour * 2,
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
},
},
res: res{
@@ -106,12 +122,17 @@ func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) {
AggregateID: "IAM",
ResourceOwner: "IAM",
},
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
PasswordCheckLifetime: time.Hour * 1,
ExternalLoginCheckLifetime: time.Hour * 2,
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
},
},
},
@@ -187,6 +208,11 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -195,12 +221,17 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
args: args{
ctx: context.Background(),
policy: &domain.LoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
PasswordCheckLifetime: time.Hour * 1,
ExternalLoginCheckLifetime: time.Hour * 2,
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
},
},
res: res{
@@ -222,13 +253,29 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
newDefaultLoginPolicyChangedEvent(context.Background(), false, false, false, false, false, domain.PasswordlessTypeNotAllowed),
newDefaultLoginPolicyChangedEvent(context.Background(),
false,
false,
false,
false,
false,
domain.PasswordlessTypeNotAllowed,
time.Hour*10,
time.Hour*20,
time.Hour*30,
time.Hour*40,
time.Hour*50),
),
},
),
@@ -237,12 +284,17 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
args: args{
ctx: context.Background(),
policy: &domain.LoginPolicy{
AllowRegister: false,
AllowUsernamePassword: false,
AllowExternalIDP: false,
ForceMFA: false,
HidePasswordReset: false,
PasswordlessType: domain.PasswordlessTypeNotAllowed,
AllowRegister: false,
AllowUsernamePassword: false,
AllowExternalIDP: false,
ForceMFA: false,
HidePasswordReset: false,
PasswordlessType: domain.PasswordlessTypeNotAllowed,
PasswordCheckLifetime: time.Hour * 10,
ExternalLoginCheckLifetime: time.Hour * 20,
MFAInitSkipLifetime: time.Hour * 30,
SecondFactorCheckLifetime: time.Hour * 40,
MultiFactorCheckLifetime: time.Hour * 50,
},
},
res: res{
@@ -251,12 +303,17 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
AggregateID: "IAM",
ResourceOwner: "IAM",
},
AllowRegister: false,
AllowUsernamePassword: false,
AllowExternalIDP: false,
ForceMFA: false,
HidePasswordReset: false,
PasswordlessType: domain.PasswordlessTypeNotAllowed,
AllowRegister: false,
AllowUsernamePassword: false,
AllowExternalIDP: false,
ForceMFA: false,
HidePasswordReset: false,
PasswordlessType: domain.PasswordlessTypeNotAllowed,
PasswordCheckLifetime: time.Hour * 10,
ExternalLoginCheckLifetime: time.Hour * 20,
MFAInitSkipLifetime: time.Hour * 30,
SecondFactorCheckLifetime: time.Hour * 40,
MultiFactorCheckLifetime: time.Hour * 50,
},
},
},
@@ -346,6 +403,11 @@ func TestCommandSide_AddIDPProviderDefaultLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -377,6 +439,11 @@ func TestCommandSide_AddIDPProviderDefaultLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -427,6 +494,11 @@ func TestCommandSide_AddIDPProviderDefaultLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -557,6 +629,11 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -588,6 +665,11 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -632,6 +714,11 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -681,6 +768,11 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -738,6 +830,11 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -1283,7 +1380,9 @@ func TestCommandSide_RemoveMultiFactorDefaultLoginPolicy(t *testing.T) {
}
}
func newDefaultLoginPolicyChangedEvent(ctx context.Context, allowRegister, allowUsernamePassword, allowExternalIDP, forceMFA, hidePasswordReset bool, passwordlessType domain.PasswordlessType) *iam.LoginPolicyChangedEvent {
func newDefaultLoginPolicyChangedEvent(ctx context.Context, allowRegister, allowUsernamePassword, allowExternalIDP, forceMFA, hidePasswordReset bool,
passwordlessType domain.PasswordlessType,
passwordLifetime, externalLoginLifetime, mfaInitSkipLifetime, secondFactorLifetime, multiFactorLifetime time.Duration) *iam.LoginPolicyChangedEvent {
event, _ := iam.NewLoginPolicyChangedEvent(ctx,
&iam.NewAggregate().Aggregate,
[]policy.LoginPolicyChanges{
@@ -1293,6 +1392,11 @@ func newDefaultLoginPolicyChangedEvent(ctx context.Context, allowRegister, allow
policy.ChangeAllowUserNamePassword(allowUsernamePassword),
policy.ChangeHidePasswordReset(hidePasswordReset),
policy.ChangePasswordlessType(passwordlessType),
policy.ChangePasswordCheckLifetime(passwordLifetime),
policy.ChangeExternalLoginCheckLifetime(externalLoginLifetime),
policy.ChangeMFAInitSkipLifetime(mfaInitSkipLifetime),
policy.ChangeSecondFactorCheckLifetime(secondFactorLifetime),
policy.ChangeMultiFactorCheckLifetime(multiFactorLifetime),
},
)
return event

View File

@@ -241,7 +241,19 @@ func (c *Commands) setAllowedLoginPolicy(ctx context.Context, orgID string, feat
if !features.LoginPolicyPasswordReset && defaultPolicy.HidePasswordReset != existingPolicy.HidePasswordReset {
policy.HidePasswordReset = defaultPolicy.HidePasswordReset
}
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, OrgAggregateFromWriteModel(&existingPolicy.WriteModel), policy.AllowUserNamePassword, policy.AllowRegister, policy.AllowExternalIDP, policy.ForceMFA, policy.HidePasswordReset, policy.PasswordlessType)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx,
OrgAggregateFromWriteModel(&existingPolicy.WriteModel),
policy.AllowUserNamePassword,
policy.AllowRegister,
policy.AllowExternalIDP,
policy.ForceMFA,
policy.HidePasswordReset,
policy.PasswordlessType,
policy.PasswordCheckLifetime,
policy.ExternalLoginCheckLifetime,
policy.MFAInitSkipLifetime,
policy.SecondFactorCheckLifetime,
policy.MultiFactorCheckLifetime)
if hasChanged {
events = append(events, changedEvent)
}

View File

@@ -5,6 +5,8 @@ import (
"testing"
"time"
"github.com/caos/zitadel/internal/repository/user"
"github.com/caos/zitadel/internal/static/mock"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"golang.org/x/text/language"
@@ -16,9 +18,7 @@ import (
"github.com/caos/zitadel/internal/repository/features"
"github.com/caos/zitadel/internal/repository/iam"
"github.com/caos/zitadel/internal/repository/org"
"github.com/caos/zitadel/internal/repository/user"
"github.com/caos/zitadel/internal/static"
"github.com/caos/zitadel/internal/static/mock"
)
func TestCommandSide_SetOrgFeatures(t *testing.T) {
@@ -165,6 +165,11 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
false,
false,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -343,6 +348,11 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
false,
false,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -547,6 +557,11 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
false,
false,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -761,6 +776,11 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
false,
false,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -990,6 +1010,11 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
eventFromEventPusher(
@@ -1002,6 +1027,11 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
false,
false,
domain.PasswordlessTypeNotAllowed,
time.Hour*10,
time.Hour*20,
time.Hour*30,
time.Hour*40,
time.Hour*50,
),
),
),
@@ -1017,6 +1047,11 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -1195,7 +1230,13 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
org.NewLoginPolicyMultiFactorAddedEvent(context.Background(), &org.NewAggregate("org1", "org1").Aggregate, domain.MultiFactorTypeU2FWithPIN),
),
eventFromEventPusher(
newLoginPolicyChangedEvent(context.Background(), "org1", true, true, true, true, true, domain.PasswordlessTypeAllowed),
newLoginPolicyChangedEvent(context.Background(), "org1",
true, true, true, true, true, domain.PasswordlessTypeAllowed,
nil,
nil,
nil,
nil,
nil),
),
eventFromEventPusher(
org.NewPasswordComplexityPolicyRemovedEvent(context.Background(), &org.NewAggregate("org1", "org1").Aggregate),
@@ -1278,6 +1319,11 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
false,
false,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -1537,6 +1583,11 @@ func TestCommandSide_RemoveOrgFeatures(t *testing.T) {
false,
false,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),

View File

@@ -42,7 +42,12 @@ func (c *Commands) AddLoginPolicy(ctx context.Context, resourceOwner string, pol
policy.AllowExternalIDP,
policy.ForceMFA,
policy.HidePasswordReset,
policy.PasswordlessType))
policy.PasswordlessType,
policy.PasswordCheckLifetime,
policy.ExternalLoginCheckLifetime,
policy.MFAInitSkipLifetime,
policy.SecondFactorCheckLifetime,
policy.MultiFactorCheckLifetime))
if err != nil {
return nil, err
}
@@ -100,7 +105,12 @@ func (c *Commands) ChangeLoginPolicy(ctx context.Context, resourceOwner string,
policy.AllowExternalIDP,
policy.ForceMFA,
policy.HidePasswordReset,
policy.PasswordlessType)
policy.PasswordlessType,
policy.PasswordCheckLifetime,
policy.ExternalLoginCheckLifetime,
policy.MFAInitSkipLifetime,
policy.SecondFactorCheckLifetime,
policy.MultiFactorCheckLifetime)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-5M9vdd", "Errors.Org.LoginPolicy.NotChanged")

View File

@@ -2,6 +2,7 @@ package command
import (
"context"
"time"
"github.com/caos/zitadel/internal/eventstore"
@@ -68,6 +69,11 @@ func (wm *OrgLoginPolicyWriteModel) NewChangedEvent(
forceMFA,
hidePasswordReset bool,
passwordlessType domain.PasswordlessType,
passwordCheckLifetime,
externalLoginCheckLifetime,
mfaInitSkipLifetime,
secondFactorCheckLifetime,
multiFactorCheckLifetime time.Duration,
) (*org.LoginPolicyChangedEvent, bool) {
changes := make([]policy.LoginPolicyChanges, 0)
@@ -86,6 +92,21 @@ func (wm *OrgLoginPolicyWriteModel) NewChangedEvent(
if wm.HidePasswordReset != hidePasswordReset {
changes = append(changes, policy.ChangeHidePasswordReset(hidePasswordReset))
}
if wm.PasswordCheckLifetime != passwordCheckLifetime {
changes = append(changes, policy.ChangePasswordCheckLifetime(passwordCheckLifetime))
}
if wm.ExternalLoginCheckLifetime != externalLoginCheckLifetime {
changes = append(changes, policy.ChangeExternalLoginCheckLifetime(externalLoginCheckLifetime))
}
if wm.MFAInitSkipLifetime != mfaInitSkipLifetime {
changes = append(changes, policy.ChangeMFAInitSkipLifetime(mfaInitSkipLifetime))
}
if wm.SecondFactorCheckLifetime != secondFactorCheckLifetime {
changes = append(changes, policy.ChangeSecondFactorCheckLifetime(secondFactorCheckLifetime))
}
if wm.MultiFactorCheckLifetime != multiFactorCheckLifetime {
changes = append(changes, policy.ChangeMultiFactorCheckLifetime(multiFactorCheckLifetime))
}
if passwordlessType.Valid() && wm.PasswordlessType != passwordlessType {
changes = append(changes, policy.ChangePasswordlessType(passwordlessType))
}

View File

@@ -3,6 +3,7 @@ package command
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
@@ -17,6 +18,14 @@ import (
"github.com/caos/zitadel/internal/repository/user"
)
var (
duration10 = time.Hour * 10
duration20 = time.Hour * 20
duration30 = time.Hour * 30
duration40 = time.Hour * 40
duration50 = time.Hour * 50
)
func TestCommandSide_AddLoginPolicy(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
@@ -71,6 +80,11 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -80,11 +94,16 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
ctx: context.Background(),
orgID: "org1",
policy: &domain.LoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
PasswordCheckLifetime: time.Hour * 1,
ExternalLoginCheckLifetime: time.Hour * 2,
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
},
},
res: res{
@@ -107,6 +126,11 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -117,11 +141,16 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
ctx: context.Background(),
orgID: "org1",
policy: &domain.LoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
PasswordCheckLifetime: time.Hour * 1,
ExternalLoginCheckLifetime: time.Hour * 2,
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
},
},
res: res{
@@ -144,6 +173,11 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -158,6 +192,11 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
},
@@ -169,12 +208,17 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
ctx: context.Background(),
orgID: "org1",
policy: &domain.LoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
PasswordCheckLifetime: time.Hour * 1,
ExternalLoginCheckLifetime: time.Hour * 2,
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
},
},
res: res{
@@ -183,12 +227,17 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
AggregateID: "org1",
ResourceOwner: "org1",
},
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
PasswordCheckLifetime: time.Hour * 1,
ExternalLoginCheckLifetime: time.Hour * 2,
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
},
},
},
@@ -292,6 +341,11 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -305,6 +359,11 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -315,11 +374,16 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
ctx: context.Background(),
orgID: "org1",
policy: &domain.LoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
PasswordCheckLifetime: time.Hour * 1,
ExternalLoginCheckLifetime: time.Hour * 2,
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
},
},
res: res{
@@ -341,6 +405,11 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -354,6 +423,11 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -364,12 +438,17 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
ctx: context.Background(),
orgID: "org1",
policy: &domain.LoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
PasswordCheckLifetime: time.Hour * 1,
ExternalLoginCheckLifetime: time.Hour * 2,
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
},
},
res: res{
@@ -391,6 +470,11 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -404,13 +488,31 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
false,
false,
domain.PasswordlessTypeNotAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
newLoginPolicyChangedEvent(context.Background(), "org1", false, false, false, false, false, domain.PasswordlessTypeNotAllowed),
newLoginPolicyChangedEvent(context.Background(),
"org1",
false,
false,
false,
false,
false,
domain.PasswordlessTypeNotAllowed,
&duration10,
&duration20,
&duration30,
&duration40,
&duration50,
),
),
},
),
@@ -421,11 +523,16 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
ctx: context.Background(),
orgID: "org1",
policy: &domain.LoginPolicy{
AllowRegister: false,
AllowUsernamePassword: false,
AllowExternalIDP: false,
ForceMFA: false,
PasswordlessType: domain.PasswordlessTypeNotAllowed,
AllowRegister: false,
AllowUsernamePassword: false,
AllowExternalIDP: false,
ForceMFA: false,
PasswordlessType: domain.PasswordlessTypeNotAllowed,
PasswordCheckLifetime: time.Hour * 10,
ExternalLoginCheckLifetime: time.Hour * 20,
MFAInitSkipLifetime: time.Hour * 30,
SecondFactorCheckLifetime: time.Hour * 40,
MultiFactorCheckLifetime: time.Hour * 50,
},
},
res: res{
@@ -434,12 +541,17 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
AggregateID: "org1",
ResourceOwner: "org1",
},
AllowRegister: false,
AllowUsernamePassword: false,
AllowExternalIDP: false,
ForceMFA: false,
HidePasswordReset: false,
PasswordlessType: domain.PasswordlessTypeNotAllowed,
AllowRegister: false,
AllowUsernamePassword: false,
AllowExternalIDP: false,
ForceMFA: false,
HidePasswordReset: false,
PasswordlessType: domain.PasswordlessTypeNotAllowed,
PasswordCheckLifetime: time.Hour * 10,
ExternalLoginCheckLifetime: time.Hour * 20,
MFAInitSkipLifetime: time.Hour * 30,
SecondFactorCheckLifetime: time.Hour * 40,
MultiFactorCheckLifetime: time.Hour * 50,
},
},
},
@@ -527,6 +639,11 @@ func TestCommandSide_RemoveLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -660,6 +777,11 @@ func TestCommandSide_AddIDPProviderLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -694,6 +816,11 @@ func TestCommandSide_AddIDPProviderLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -748,6 +875,11 @@ func TestCommandSide_AddIDPProviderLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -905,6 +1037,11 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -939,6 +1076,11 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -985,6 +1127,11 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -1038,6 +1185,11 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -1099,6 +1251,11 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -1710,17 +1867,35 @@ func TestCommandSide_RemoveMultiFactorLoginPolicy(t *testing.T) {
}
}
func newLoginPolicyChangedEvent(ctx context.Context, orgID string, usernamePassword, register, externalIDP, mfa, passwordReset bool, passwordlessType domain.PasswordlessType) *org.LoginPolicyChangedEvent {
func newLoginPolicyChangedEvent(ctx context.Context, orgID string, usernamePassword, register, externalIDP, mfa, passwordReset bool,
passwordlessType domain.PasswordlessType,
passwordLifetime, externalLoginLifetime, mfaInitSkipLifetime, secondFactorLifetime, multiFactorLifetime *time.Duration) *org.LoginPolicyChangedEvent {
changes := []policy.LoginPolicyChanges{
policy.ChangeAllowUserNamePassword(usernamePassword),
policy.ChangeAllowRegister(register),
policy.ChangeAllowExternalIDP(externalIDP),
policy.ChangeForceMFA(mfa),
policy.ChangeHidePasswordReset(passwordReset),
policy.ChangePasswordlessType(passwordlessType),
}
if passwordLifetime != nil {
changes = append(changes, policy.ChangePasswordCheckLifetime(*passwordLifetime))
}
if externalLoginLifetime != nil {
changes = append(changes, policy.ChangeExternalLoginCheckLifetime(*externalLoginLifetime))
}
if mfaInitSkipLifetime != nil {
changes = append(changes, policy.ChangeMFAInitSkipLifetime(*mfaInitSkipLifetime))
}
if secondFactorLifetime != nil {
changes = append(changes, policy.ChangeSecondFactorCheckLifetime(*secondFactorLifetime))
}
if multiFactorLifetime != nil {
changes = append(changes, policy.ChangeMultiFactorCheckLifetime(*multiFactorLifetime))
}
event, _ := org.NewLoginPolicyChangedEvent(ctx,
&org.NewAggregate(orgID, orgID).Aggregate,
[]policy.LoginPolicyChanges{
policy.ChangeAllowUserNamePassword(usernamePassword),
policy.ChangeAllowRegister(register),
policy.ChangeAllowExternalIDP(externalIDP),
policy.ChangeForceMFA(mfa),
policy.ChangeHidePasswordReset(passwordReset),
policy.ChangePasswordlessType(passwordlessType),
},
changes,
)
return event
}

View File

@@ -1,6 +1,8 @@
package command
import (
"time"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/repository/policy"
@@ -9,13 +11,18 @@ import (
type LoginPolicyWriteModel struct {
eventstore.WriteModel
AllowUserNamePassword bool
AllowRegister bool
AllowExternalIDP bool
ForceMFA bool
HidePasswordReset bool
PasswordlessType domain.PasswordlessType
State domain.PolicyState
AllowUserNamePassword bool
AllowRegister bool
AllowExternalIDP bool
ForceMFA bool
HidePasswordReset bool
PasswordlessType domain.PasswordlessType
PasswordCheckLifetime time.Duration
ExternalLoginCheckLifetime time.Duration
MFAInitSkipLifetime time.Duration
SecondFactorCheckLifetime time.Duration
MultiFactorCheckLifetime time.Duration
State domain.PolicyState
}
func (wm *LoginPolicyWriteModel) Reduce() error {
@@ -28,6 +35,11 @@ func (wm *LoginPolicyWriteModel) Reduce() error {
wm.ForceMFA = e.ForceMFA
wm.PasswordlessType = e.PasswordlessType
wm.HidePasswordReset = e.HidePasswordReset
wm.PasswordCheckLifetime = e.PasswordCheckLifetime
wm.ExternalLoginCheckLifetime = e.ExternalLoginCheckLifetime
wm.MFAInitSkipLifetime = e.MFAInitSkipLifetime
wm.SecondFactorCheckLifetime = e.SecondFactorCheckLifetime
wm.MultiFactorCheckLifetime = e.MultiFactorCheckLifetime
wm.State = domain.PolicyStateActive
case *policy.LoginPolicyChangedEvent:
if e.AllowRegister != nil {
@@ -48,6 +60,21 @@ func (wm *LoginPolicyWriteModel) Reduce() error {
if e.PasswordlessType != nil {
wm.PasswordlessType = *e.PasswordlessType
}
if e.PasswordCheckLifetime != nil {
wm.PasswordCheckLifetime = *e.PasswordCheckLifetime
}
if e.ExternalLoginCheckLifetime != nil {
wm.ExternalLoginCheckLifetime = *e.ExternalLoginCheckLifetime
}
if e.MFAInitSkipLifetime != nil {
wm.MFAInitSkipLifetime = *e.MFAInitSkipLifetime
}
if e.SecondFactorCheckLifetime != nil {
wm.SecondFactorCheckLifetime = *e.SecondFactorCheckLifetime
}
if e.MultiFactorCheckLifetime != nil {
wm.MultiFactorCheckLifetime = *e.MultiFactorCheckLifetime
}
case *policy.LoginPolicyRemovedEvent:
wm.State = domain.PolicyStateRemoved
}

View File

@@ -1157,6 +1157,11 @@ func TestCommandSide_CheckPassword(t *testing.T) {
false,
false,
domain.PasswordlessTypeNotAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -1187,6 +1192,11 @@ func TestCommandSide_CheckPassword(t *testing.T) {
false,
false,
domain.PasswordlessTypeNotAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -1218,6 +1228,11 @@ func TestCommandSide_CheckPassword(t *testing.T) {
false,
false,
domain.PasswordlessTypeNotAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -1265,6 +1280,11 @@ func TestCommandSide_CheckPassword(t *testing.T) {
false,
false,
domain.PasswordlessTypeNotAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -1346,6 +1366,11 @@ func TestCommandSide_CheckPassword(t *testing.T) {
false,
false,
domain.PasswordlessTypeNotAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -1434,6 +1459,11 @@ func TestCommandSide_CheckPassword(t *testing.T) {
false,
false,
domain.PasswordlessTypeNotAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),

View File

@@ -1607,6 +1607,11 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
false,
false,
domain.PasswordlessTypeNotAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -1664,6 +1669,11 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
false,
false,
domain.PasswordlessTypeNotAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -1721,6 +1731,11 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
false,
false,
domain.PasswordlessTypeNotAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -1795,6 +1810,11 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
false,
false,
domain.PasswordlessTypeNotAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -1927,6 +1947,11 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
false,
false,
domain.PasswordlessTypeNotAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -2027,6 +2052,11 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
false,
false,
domain.PasswordlessTypeNotAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -2121,6 +2151,11 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
false,
false,
domain.PasswordlessTypeNotAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
@@ -2237,6 +2272,11 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
false,
false,
domain.PasswordlessTypeNotAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),