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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 1188 additions and 421 deletions

View File

@ -1514,7 +1514,7 @@ This is an empty request
| ----- | ---- | ----------- | ----------- | | ----- | ---- | ----------- | ----------- |
| sid | string | - | string.min_len: 1<br /> string.max_len: 200<br /> | | sid | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
| token | string | - | string.min_len: 1<br /> string.max_len: 200<br /> | | token | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
| from | string | - | string.min_len: 1<br /> string.max_len: 200<br /> | | sender_number | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
@ -3593,6 +3593,11 @@ This is an empty request
| force_mfa | bool | - | | | force_mfa | bool | - | |
| passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> | | passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> |
| hide_password_reset | bool | - | | | hide_password_reset | bool | - | |
| password_check_lifetime | google.protobuf.Duration | - | |
| external_login_check_lifetime | google.protobuf.Duration | - | |
| mfa_init_skip_lifetime | google.protobuf.Duration | - | |
| second_factor_check_lifetime | google.protobuf.Duration | - | |
| multi_factor_check_lifetime | google.protobuf.Duration | - | |
@ -3710,7 +3715,7 @@ This is an empty request
| ----- | ---- | ----------- | ----------- | | ----- | ---- | ----------- | ----------- |
| id | string | - | string.min_len: 1<br /> string.max_len: 200<br /> | | id | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
| sid | string | - | string.min_len: 1<br /> string.max_len: 200<br /> | | sid | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
| from | string | - | string.min_len: 1<br /> string.max_len: 200<br /> | | sender_number | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |

View File

@ -3036,6 +3036,11 @@ This is an empty request
| force_mfa | bool | - | | | force_mfa | bool | - | |
| passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> | | passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> |
| hide_password_reset | bool | - | | | hide_password_reset | bool | - | |
| password_check_lifetime | google.protobuf.Duration | - | |
| external_login_check_lifetime | google.protobuf.Duration | - | |
| mfa_init_skip_lifetime | google.protobuf.Duration | - | |
| second_factor_check_lifetime | google.protobuf.Duration | - | |
| multi_factor_check_lifetime | google.protobuf.Duration | - | |
@ -7751,6 +7756,11 @@ This is an empty request
| force_mfa | bool | - | | | force_mfa | bool | - | |
| passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> | | passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> |
| hide_password_reset | bool | - | | | hide_password_reset | bool | - | |
| password_check_lifetime | google.protobuf.Duration | - | |
| external_login_check_lifetime | google.protobuf.Duration | - | |
| mfa_init_skip_lifetime | google.protobuf.Duration | - | |
| second_factor_check_lifetime | google.protobuf.Duration | - | |
| multi_factor_check_lifetime | google.protobuf.Duration | - | |

View File

@ -63,6 +63,11 @@ title: zitadel/policy.proto
| passwordless_type | PasswordlessType | - | | | passwordless_type | PasswordlessType | - | |
| is_default | bool | - | | | is_default | bool | - | |
| hide_password_reset | bool | - | | | hide_password_reset | bool | - | |
| password_check_lifetime | google.protobuf.Duration | - | |
| external_login_check_lifetime | google.protobuf.Duration | - | |
| mfa_init_skip_lifetime | google.protobuf.Duration | - | |
| second_factor_check_lifetime | google.protobuf.Duration | - | |
| multi_factor_check_lifetime | google.protobuf.Duration | - | |

View File

@ -10,12 +10,17 @@ import (
func updateLoginPolicyToDomain(p *admin_pb.UpdateLoginPolicyRequest) *domain.LoginPolicy { func updateLoginPolicyToDomain(p *admin_pb.UpdateLoginPolicyRequest) *domain.LoginPolicy {
return &domain.LoginPolicy{ return &domain.LoginPolicy{
AllowUsernamePassword: p.AllowUsernamePassword, AllowUsernamePassword: p.AllowUsernamePassword,
AllowRegister: p.AllowRegister, AllowRegister: p.AllowRegister,
AllowExternalIDP: p.AllowExternalIdp, AllowExternalIDP: p.AllowExternalIdp,
ForceMFA: p.ForceMfa, ForceMFA: p.ForceMfa,
PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType), PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType),
HidePasswordReset: p.HidePasswordReset, HidePasswordReset: p.HidePasswordReset,
PasswordCheckLifetime: p.PasswordCheckLifetime.AsDuration(),
ExternalLoginCheckLifetime: p.ExternalLoginCheckLifetime.AsDuration(),
MFAInitSkipLifetime: p.MfaInitSkipLifetime.AsDuration(),
SecondFactorCheckLifetime: p.SecondFactorCheckLifetime.AsDuration(),
MultiFactorCheckLifetime: p.MultiFactorCheckLifetime.AsDuration(),
} }
} }

View File

@ -10,23 +10,33 @@ import (
func addLoginPolicyToDomain(p *mgmt_pb.AddCustomLoginPolicyRequest) *domain.LoginPolicy { func addLoginPolicyToDomain(p *mgmt_pb.AddCustomLoginPolicyRequest) *domain.LoginPolicy {
return &domain.LoginPolicy{ return &domain.LoginPolicy{
AllowUsernamePassword: p.AllowUsernamePassword, AllowUsernamePassword: p.AllowUsernamePassword,
AllowRegister: p.AllowRegister, AllowRegister: p.AllowRegister,
AllowExternalIDP: p.AllowExternalIdp, AllowExternalIDP: p.AllowExternalIdp,
ForceMFA: p.ForceMfa, ForceMFA: p.ForceMfa,
PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType), PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType),
HidePasswordReset: p.HidePasswordReset, HidePasswordReset: p.HidePasswordReset,
PasswordCheckLifetime: p.PasswordCheckLifetime.AsDuration(),
ExternalLoginCheckLifetime: p.ExternalLoginCheckLifetime.AsDuration(),
MFAInitSkipLifetime: p.MfaInitSkipLifetime.AsDuration(),
SecondFactorCheckLifetime: p.SecondFactorCheckLifetime.AsDuration(),
MultiFactorCheckLifetime: p.MultiFactorCheckLifetime.AsDuration(),
} }
} }
func updateLoginPolicyToDomain(p *mgmt_pb.UpdateCustomLoginPolicyRequest) *domain.LoginPolicy { func updateLoginPolicyToDomain(p *mgmt_pb.UpdateCustomLoginPolicyRequest) *domain.LoginPolicy {
return &domain.LoginPolicy{ return &domain.LoginPolicy{
AllowUsernamePassword: p.AllowUsernamePassword, AllowUsernamePassword: p.AllowUsernamePassword,
AllowRegister: p.AllowRegister, AllowRegister: p.AllowRegister,
AllowExternalIDP: p.AllowExternalIdp, AllowExternalIDP: p.AllowExternalIdp,
ForceMFA: p.ForceMfa, ForceMFA: p.ForceMfa,
PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType), PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType),
HidePasswordReset: p.HidePasswordReset, HidePasswordReset: p.HidePasswordReset,
PasswordCheckLifetime: p.PasswordCheckLifetime.AsDuration(),
ExternalLoginCheckLifetime: p.ExternalLoginCheckLifetime.AsDuration(),
MFAInitSkipLifetime: p.MfaInitSkipLifetime.AsDuration(),
SecondFactorCheckLifetime: p.SecondFactorCheckLifetime.AsDuration(),
MultiFactorCheckLifetime: p.MultiFactorCheckLifetime.AsDuration(),
} }
} }

View File

@ -5,18 +5,24 @@ import (
"github.com/caos/zitadel/internal/query" "github.com/caos/zitadel/internal/query"
"github.com/caos/zitadel/pkg/grpc/object" "github.com/caos/zitadel/pkg/grpc/object"
policy_pb "github.com/caos/zitadel/pkg/grpc/policy" policy_pb "github.com/caos/zitadel/pkg/grpc/policy"
"google.golang.org/protobuf/types/known/durationpb"
timestamp_pb "google.golang.org/protobuf/types/known/timestamppb" timestamp_pb "google.golang.org/protobuf/types/known/timestamppb"
) )
func ModelLoginPolicyToPb(policy *query.LoginPolicy) *policy_pb.LoginPolicy { func ModelLoginPolicyToPb(policy *query.LoginPolicy) *policy_pb.LoginPolicy {
return &policy_pb.LoginPolicy{ return &policy_pb.LoginPolicy{
IsDefault: policy.IsDefault, IsDefault: policy.IsDefault,
AllowUsernamePassword: policy.AllowUsernamePassword, AllowUsernamePassword: policy.AllowUsernamePassword,
AllowRegister: policy.AllowRegister, AllowRegister: policy.AllowRegister,
AllowExternalIdp: policy.AllowExternalIDPs, AllowExternalIdp: policy.AllowExternalIDPs,
ForceMfa: policy.ForceMFA, ForceMfa: policy.ForceMFA,
PasswordlessType: ModelPasswordlessTypeToPb(policy.PasswordlessType), PasswordlessType: ModelPasswordlessTypeToPb(policy.PasswordlessType),
HidePasswordReset: policy.HidePasswordReset, HidePasswordReset: policy.HidePasswordReset,
PasswordCheckLifetime: durationpb.New(policy.PasswordCheckLifetime),
ExternalLoginCheckLifetime: durationpb.New(policy.ExternalLoginCheckLifetime),
MfaInitSkipLifetime: durationpb.New(policy.MFAInitSkipLifetime),
SecondFactorCheckLifetime: durationpb.New(policy.SecondFactorCheckLifetime),
MultiFactorCheckLifetime: durationpb.New(policy.MultiFactorCheckLifetime),
Details: &object.ObjectDetails{ Details: &object.ObjectDetails{
Sequence: policy.Sequence, Sequence: policy.Sequence,
CreationDate: timestamp_pb.New(policy.CreationDate), CreationDate: timestamp_pb.New(policy.CreationDate),

View File

@ -50,12 +50,6 @@ type AuthRequestRepo struct {
ApplicationProvider applicationProvider ApplicationProvider applicationProvider
IdGenerator id.Generator IdGenerator id.Generator
PasswordCheckLifeTime time.Duration
ExternalLoginCheckLifeTime time.Duration
MFAInitSkippedLifeTime time.Duration
SecondFactorCheckLifeTime time.Duration
MultiFactorCheckLifeTime time.Duration
} }
type labelPolicyProvider interface { type labelPolicyProvider interface {
@ -761,7 +755,7 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *domain.Auth
} }
isInternalLogin := request.SelectedIDPConfigID == "" && userSession.SelectedIDPConfigID == "" isInternalLogin := request.SelectedIDPConfigID == "" && userSession.SelectedIDPConfigID == ""
if !isInternalLogin && len(request.LinkingUsers) == 0 && !checkVerificationTimeMaxAge(userSession.ExternalLoginVerification, repo.ExternalLoginCheckLifeTime, request) { if !isInternalLogin && len(request.LinkingUsers) == 0 && !checkVerificationTimeMaxAge(userSession.ExternalLoginVerification, request.LoginPolicy.ExternalLoginCheckLifetime, request) {
selectedIDPConfigID := request.SelectedIDPConfigID selectedIDPConfigID := request.SelectedIDPConfigID
if selectedIDPConfigID == "" { if selectedIDPConfigID == "" {
selectedIDPConfigID = userSession.SelectedIDPConfigID selectedIDPConfigID = userSession.SelectedIDPConfigID
@ -858,7 +852,7 @@ func (repo *AuthRequestRepo) firstFactorChecked(request *domain.AuthRequest, use
var step domain.NextStep var step domain.NextStep
if request.LoginPolicy.PasswordlessType != domain.PasswordlessTypeNotAllowed && user.IsPasswordlessReady() { if request.LoginPolicy.PasswordlessType != domain.PasswordlessTypeNotAllowed && user.IsPasswordlessReady() {
if checkVerificationTimeMaxAge(userSession.PasswordlessVerification, repo.MultiFactorCheckLifeTime, request) { if checkVerificationTimeMaxAge(userSession.PasswordlessVerification, request.LoginPolicy.MultiFactorCheckLifetime, request) {
request.AuthTime = userSession.PasswordlessVerification request.AuthTime = userSession.PasswordlessVerification
return nil return nil
} }
@ -875,7 +869,7 @@ func (repo *AuthRequestRepo) firstFactorChecked(request *domain.AuthRequest, use
return &domain.InitPasswordStep{} return &domain.InitPasswordStep{}
} }
if checkVerificationTimeMaxAge(userSession.PasswordVerification, repo.PasswordCheckLifeTime, request) { if checkVerificationTimeMaxAge(userSession.PasswordVerification, request.LoginPolicy.PasswordCheckLifetime, request) {
request.PasswordVerified = true request.PasswordVerified = true
request.AuthTime = userSession.PasswordVerification request.AuthTime = userSession.PasswordVerification
return nil return nil
@ -890,7 +884,7 @@ func (repo *AuthRequestRepo) mfaChecked(userSession *user_model.UserSessionView,
mfaLevel := request.MFALevel() mfaLevel := request.MFALevel()
allowedProviders, required := user.MFATypesAllowed(mfaLevel, request.LoginPolicy) allowedProviders, required := user.MFATypesAllowed(mfaLevel, request.LoginPolicy)
promptRequired := (model.MFALevelToDomain(user.MFAMaxSetUp) < mfaLevel) || (len(allowedProviders) == 0 && required) promptRequired := (model.MFALevelToDomain(user.MFAMaxSetUp) < mfaLevel) || (len(allowedProviders) == 0 && required)
if promptRequired || !repo.mfaSkippedOrSetUp(user) { if promptRequired || !repo.mfaSkippedOrSetUp(user, request) {
types := user.MFATypesSetupPossible(mfaLevel, request.LoginPolicy) types := user.MFATypesSetupPossible(mfaLevel, request.LoginPolicy)
if promptRequired && len(types) == 0 { if promptRequired && len(types) == 0 {
return nil, false, errors.ThrowPreconditionFailed(nil, "LOGIN-5Hm8s", "Errors.Login.LoginPolicy.MFA.ForceAndNotConfigured") return nil, false, errors.ThrowPreconditionFailed(nil, "LOGIN-5Hm8s", "Errors.Login.LoginPolicy.MFA.ForceAndNotConfigured")
@ -912,14 +906,14 @@ func (repo *AuthRequestRepo) mfaChecked(userSession *user_model.UserSessionView,
} }
fallthrough fallthrough
case domain.MFALevelSecondFactor: case domain.MFALevelSecondFactor:
if checkVerificationTimeMaxAge(userSession.SecondFactorVerification, repo.SecondFactorCheckLifeTime, request) { if checkVerificationTimeMaxAge(userSession.SecondFactorVerification, request.LoginPolicy.SecondFactorCheckLifetime, request) {
request.MFAsVerified = append(request.MFAsVerified, model.MFATypeToDomain(userSession.SecondFactorVerificationType)) request.MFAsVerified = append(request.MFAsVerified, model.MFATypeToDomain(userSession.SecondFactorVerificationType))
request.AuthTime = userSession.SecondFactorVerification request.AuthTime = userSession.SecondFactorVerification
return nil, true, nil return nil, true, nil
} }
fallthrough fallthrough
case domain.MFALevelMultiFactor: case domain.MFALevelMultiFactor:
if checkVerificationTimeMaxAge(userSession.MultiFactorVerification, repo.MultiFactorCheckLifeTime, request) { if checkVerificationTimeMaxAge(userSession.MultiFactorVerification, request.LoginPolicy.MultiFactorCheckLifetime, request) {
request.MFAsVerified = append(request.MFAsVerified, model.MFATypeToDomain(userSession.MultiFactorVerificationType)) request.MFAsVerified = append(request.MFAsVerified, model.MFATypeToDomain(userSession.MultiFactorVerificationType))
request.AuthTime = userSession.MultiFactorVerification request.AuthTime = userSession.MultiFactorVerification
return nil, true, nil return nil, true, nil
@ -930,11 +924,11 @@ func (repo *AuthRequestRepo) mfaChecked(userSession *user_model.UserSessionView,
}, false, nil }, false, nil
} }
func (repo *AuthRequestRepo) mfaSkippedOrSetUp(user *user_model.UserView) bool { func (repo *AuthRequestRepo) mfaSkippedOrSetUp(user *user_model.UserView, request *domain.AuthRequest) bool {
if user.MFAMaxSetUp > model.MFALevelNotSetUp { if user.MFAMaxSetUp > model.MFALevelNotSetUp {
return true return true
} }
return checkVerificationTime(user.MFAInitSkipped, repo.MFAInitSkippedLifeTime) return checkVerificationTime(user.MFAInitSkipped, request.LoginPolicy.MFAInitSkipLifetime)
} }
func (repo *AuthRequestRepo) getPrivacyPolicy(ctx context.Context, orgID string) (*domain.PrivacyPolicy, error) { func (repo *AuthRequestRepo) getPrivacyPolicy(ctx context.Context, orgID string) (*domain.PrivacyPolicy, error) {

View File

@ -252,22 +252,17 @@ func (m *mockApp) AppByOIDCClientID(ctx context.Context, id string) (*query.App,
func TestAuthRequestRepo_nextSteps(t *testing.T) { func TestAuthRequestRepo_nextSteps(t *testing.T) {
type fields struct { type fields struct {
AuthRequests *cache.AuthRequestCache AuthRequests *cache.AuthRequestCache
View *view.View View *view.View
userSessionViewProvider userSessionViewProvider userSessionViewProvider userSessionViewProvider
userViewProvider userViewProvider userViewProvider userViewProvider
userEventProvider userEventProvider userEventProvider userEventProvider
orgViewProvider orgViewProvider orgViewProvider orgViewProvider
userGrantProvider userGrantProvider userGrantProvider userGrantProvider
projectProvider projectProvider projectProvider projectProvider
applicationProvider applicationProvider applicationProvider applicationProvider
loginPolicyProvider loginPolicyViewProvider loginPolicyProvider loginPolicyViewProvider
lockoutPolicyProvider lockoutPolicyViewProvider lockoutPolicyProvider lockoutPolicyViewProvider
PasswordCheckLifeTime time.Duration
ExternalLoginCheckLifeTime time.Duration
MFAInitSkippedLifeTime time.Duration
SecondFactorCheckLifeTime time.Duration
MultiFactorCheckLifeTime time.Duration
} }
type args struct { type args struct {
request *domain.AuthRequest request *domain.AuthRequest
@ -570,14 +565,18 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userViewProvider: &mockViewUser{ userViewProvider: &mockViewUser{
PasswordlessInitRequired: true, PasswordlessInitRequired: true,
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
MultiFactorCheckLifeTime: 10 * time.Hour,
lockoutPolicyProvider: &mockLockoutPolicy{ lockoutPolicyProvider: &mockLockoutPolicy{
policy: &query.LockoutPolicy{ policy: &query.LockoutPolicy{
ShowFailures: true, ShowFailures: true,
}, },
}, },
loginPolicyProvider: &mockLoginPolicy{
policy: &query.LoginPolicy{
MultiFactorCheckLifetime: 10 * time.Hour,
},
},
}, },
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{PasswordlessType: domain.PasswordlessTypeAllowed}}, false}, args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{PasswordlessType: domain.PasswordlessTypeAllowed}}, false},
[]domain.NextStep{&domain.PasswordlessRegistrationPromptStep{}}, []domain.NextStep{&domain.PasswordlessRegistrationPromptStep{}},
@ -597,7 +596,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true, ShowFailures: true,
}, },
}, },
MultiFactorCheckLifeTime: 10 * time.Hour, loginPolicyProvider: &mockLoginPolicy{
policy: &query.LoginPolicy{
MultiFactorCheckLifetime: 10 * time.Hour,
},
},
}, },
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{PasswordlessType: domain.PasswordlessTypeAllowed}}, false}, args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{PasswordlessType: domain.PasswordlessTypeAllowed}}, false},
[]domain.NextStep{&domain.PasswordlessStep{}}, []domain.NextStep{&domain.PasswordlessStep{}},
@ -618,7 +621,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true, ShowFailures: true,
}, },
}, },
MultiFactorCheckLifeTime: 10 * time.Hour, loginPolicyProvider: &mockLoginPolicy{
policy: &query.LoginPolicy{
MultiFactorCheckLifetime: 10 * time.Hour,
},
},
}, },
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{PasswordlessType: domain.PasswordlessTypeAllowed}}, false}, args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{PasswordlessType: domain.PasswordlessTypeAllowed}}, false},
[]domain.NextStep{&domain.PasswordlessStep{PasswordSet: true}}, []domain.NextStep{&domain.PasswordlessStep{PasswordSet: true}},
@ -644,14 +651,14 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true, ShowFailures: true,
}, },
}, },
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
MultiFactorCheckLifeTime: 10 * time.Hour,
}, },
args{&domain.AuthRequest{ args{&domain.AuthRequest{
UserID: "UserID", UserID: "UserID",
LoginPolicy: &domain.LoginPolicy{ LoginPolicy: &domain.LoginPolicy{
PasswordlessType: domain.PasswordlessTypeAllowed, PasswordlessType: domain.PasswordlessTypeAllowed,
MultiFactors: []domain.MultiFactorType{domain.MultiFactorTypeU2FWithPIN}, MultiFactors: []domain.MultiFactorType{domain.MultiFactorTypeU2FWithPIN},
MultiFactorCheckLifetime: 10 * time.Hour,
}, },
}, false}, }, false},
[]domain.NextStep{&domain.VerifyEMailStep{}}, []domain.NextStep{&domain.VerifyEMailStep{}},
@ -692,10 +699,19 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true, ShowFailures: true,
}, },
}, },
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
SecondFactorCheckLifeTime: 18 * time.Hour, loginPolicyProvider: &mockLoginPolicy{
policy: &query.LoginPolicy{
SecondFactorCheckLifetime: 18 * time.Hour,
},
},
}, },
args{&domain.AuthRequest{UserID: "UserID", SelectedIDPConfigID: "IDPConfigID"}, false}, args{&domain.AuthRequest{
UserID: "UserID",
SelectedIDPConfigID: "IDPConfigID",
LoginPolicy: &domain.LoginPolicy{
SecondFactorCheckLifetime: 18 * time.Hour,
}}, false},
[]domain.NextStep{&domain.ExternalLoginStep{SelectedIDPConfigID: "IDPConfigID"}}, []domain.NextStep{&domain.ExternalLoginStep{SelectedIDPConfigID: "IDPConfigID"}},
nil, nil,
}, },
@ -715,23 +731,21 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userGrantProvider: &mockUserGrants{}, userGrantProvider: &mockUserGrants{},
projectProvider: &mockProject{}, projectProvider: &mockProject{},
applicationProvider: &mockApp{app: &query.App{OIDCConfig: &query.OIDCApp{AppType: domain.OIDCApplicationTypeWeb}}}, applicationProvider: &mockApp{app: &query.App{OIDCConfig: &query.OIDCApp{AppType: domain.OIDCApplicationTypeWeb}}},
loginPolicyProvider: &mockLoginPolicy{
policy: &query.LoginPolicy{},
},
lockoutPolicyProvider: &mockLockoutPolicy{ lockoutPolicyProvider: &mockLockoutPolicy{
policy: &query.LockoutPolicy{ policy: &query.LockoutPolicy{
ShowFailures: true, ShowFailures: true,
}, },
}, },
ExternalLoginCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
args{ args{
&domain.AuthRequest{ &domain.AuthRequest{
UserID: "UserID", UserID: "UserID",
SelectedIDPConfigID: "IDPConfigID", SelectedIDPConfigID: "IDPConfigID",
Request: &domain.AuthRequestOIDC{}, Request: &domain.AuthRequestOIDC{},
LoginPolicy: &domain.LoginPolicy{}, LoginPolicy: &domain.LoginPolicy{
ExternalLoginCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
},
}, },
false}, false},
[]domain.NextStep{&domain.RedirectToCallbackStep{}}, []domain.NextStep{&domain.RedirectToCallbackStep{}},
@ -751,7 +765,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true, ShowFailures: true,
}, },
}, },
PasswordCheckLifeTime: 10 * 24 * time.Hour, loginPolicyProvider: &mockLoginPolicy{
policy: &query.LoginPolicy{
PasswordCheckLifetime: 10 * 24 * time.Hour,
},
},
}, },
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false}, args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
[]domain.NextStep{&domain.PasswordStep{}}, []domain.NextStep{&domain.PasswordStep{}},
@ -779,15 +797,16 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true, ShowFailures: true,
}, },
}, },
SecondFactorCheckLifeTime: 18 * time.Hour,
ExternalLoginCheckLifeTime: 10 * 24 * time.Hour,
}, },
args{ args{
&domain.AuthRequest{ &domain.AuthRequest{
UserID: "UserID", UserID: "UserID",
SelectedIDPConfigID: "IDPConfigID", SelectedIDPConfigID: "IDPConfigID",
Request: &domain.AuthRequestOIDC{}, Request: &domain.AuthRequestOIDC{},
LoginPolicy: &domain.LoginPolicy{}, LoginPolicy: &domain.LoginPolicy{
SecondFactorCheckLifetime: 18 * time.Hour,
ExternalLoginCheckLifetime: 10 * 24 * time.Hour,
},
}, false}, }, false},
[]domain.NextStep{&domain.RedirectToCallbackStep{}}, []domain.NextStep{&domain.RedirectToCallbackStep{}},
nil, nil,
@ -811,14 +830,14 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true, ShowFailures: true,
}, },
}, },
PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
args{ args{
&domain.AuthRequest{ &domain.AuthRequest{
UserID: "UserID", UserID: "UserID",
LoginPolicy: &domain.LoginPolicy{ LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP}, SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
}, },
}, false}, }, false},
[]domain.NextStep{&domain.MFAVerificationStep{ []domain.NextStep{&domain.MFAVerificationStep{
@ -844,14 +863,14 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true, ShowFailures: true,
}, },
}, },
PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
args{ args{
&domain.AuthRequest{ &domain.AuthRequest{
UserID: "UserID", UserID: "UserID",
LoginPolicy: &domain.LoginPolicy{ LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP}, SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
}, },
}, false}, }, false},
[]domain.NextStep{&domain.MFAVerificationStep{ []domain.NextStep{&domain.MFAVerificationStep{
@ -878,16 +897,16 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true, ShowFailures: true,
}, },
}, },
PasswordCheckLifeTime: 10 * 24 * time.Hour,
ExternalLoginCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
args{ args{
&domain.AuthRequest{ &domain.AuthRequest{
UserID: "UserID", UserID: "UserID",
SelectedIDPConfigID: "IDPConfigID", SelectedIDPConfigID: "IDPConfigID",
LoginPolicy: &domain.LoginPolicy{ LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP}, SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
ExternalLoginCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
}, },
}, false}, }, false},
[]domain.NextStep{&domain.MFAVerificationStep{ []domain.NextStep{&domain.MFAVerificationStep{
@ -915,14 +934,14 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true, ShowFailures: true,
}, },
}, },
PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
args{ args{
&domain.AuthRequest{ &domain.AuthRequest{
UserID: "UserID", UserID: "UserID",
LoginPolicy: &domain.LoginPolicy{ LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP}, SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
}, },
}, false}, }, false},
[]domain.NextStep{&domain.ChangePasswordStep{}}, []domain.NextStep{&domain.ChangePasswordStep{}},
@ -946,13 +965,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true, ShowFailures: true,
}, },
}, },
PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
args{&domain.AuthRequest{ args{&domain.AuthRequest{
UserID: "UserID", UserID: "UserID",
LoginPolicy: &domain.LoginPolicy{ LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP}, SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
}, },
}, false}, }, false},
[]domain.NextStep{&domain.VerifyEMailStep{}}, []domain.NextStep{&domain.VerifyEMailStep{}},
@ -977,13 +996,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true, ShowFailures: true,
}, },
}, },
PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
args{&domain.AuthRequest{ args{&domain.AuthRequest{
UserID: "UserID", UserID: "UserID",
LoginPolicy: &domain.LoginPolicy{ LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP}, SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
}, },
}, false}, }, false},
[]domain.NextStep{&domain.ChangePasswordStep{}, &domain.VerifyEMailStep{}}, []domain.NextStep{&domain.ChangePasswordStep{}, &domain.VerifyEMailStep{}},
@ -1011,14 +1030,14 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true, ShowFailures: true,
}, },
}, },
PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
args{&domain.AuthRequest{ args{&domain.AuthRequest{
UserID: "UserID", UserID: "UserID",
Request: &domain.AuthRequestOIDC{}, Request: &domain.AuthRequestOIDC{},
LoginPolicy: &domain.LoginPolicy{ LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP}, SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
}, },
}, false}, }, false},
[]domain.NextStep{&domain.RedirectToCallbackStep{}}, []domain.NextStep{&domain.RedirectToCallbackStep{}},
@ -1046,15 +1065,15 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true, ShowFailures: true,
}, },
}, },
PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
args{&domain.AuthRequest{ args{&domain.AuthRequest{
UserID: "UserID", UserID: "UserID",
Prompt: []domain.Prompt{domain.PromptNone}, Prompt: []domain.Prompt{domain.PromptNone},
Request: &domain.AuthRequestOIDC{}, Request: &domain.AuthRequestOIDC{},
LoginPolicy: &domain.LoginPolicy{ LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP}, SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
}, },
}, true}, }, true},
[]domain.NextStep{&domain.RedirectToCallbackStep{}}, []domain.NextStep{&domain.RedirectToCallbackStep{}},
@ -1082,15 +1101,15 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true, ShowFailures: true,
}, },
}, },
PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
args{&domain.AuthRequest{ args{&domain.AuthRequest{
UserID: "UserID", UserID: "UserID",
Prompt: []domain.Prompt{domain.PromptNone}, Prompt: []domain.Prompt{domain.PromptNone},
Request: &domain.AuthRequestOIDC{}, Request: &domain.AuthRequestOIDC{},
LoginPolicy: &domain.LoginPolicy{ LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP}, SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
}, },
}, true}, }, true},
[]domain.NextStep{&domain.LoginSucceededStep{}, &domain.RedirectToCallbackStep{}}, []domain.NextStep{&domain.LoginSucceededStep{}, &domain.RedirectToCallbackStep{}},
@ -1120,15 +1139,15 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true, ShowFailures: true,
}, },
}, },
PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
args{&domain.AuthRequest{ args{&domain.AuthRequest{
UserID: "UserID", UserID: "UserID",
Prompt: []domain.Prompt{domain.PromptNone}, Prompt: []domain.Prompt{domain.PromptNone},
Request: &domain.AuthRequestOIDC{}, Request: &domain.AuthRequestOIDC{},
LoginPolicy: &domain.LoginPolicy{ LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP}, SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
}, },
}, true}, }, true},
[]domain.NextStep{&domain.GrantRequiredStep{}}, []domain.NextStep{&domain.GrantRequiredStep{}},
@ -1159,15 +1178,15 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true, ShowFailures: true,
}, },
}, },
PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
args{&domain.AuthRequest{ args{&domain.AuthRequest{
UserID: "UserID", UserID: "UserID",
Prompt: []domain.Prompt{domain.PromptNone}, Prompt: []domain.Prompt{domain.PromptNone},
Request: &domain.AuthRequestOIDC{}, Request: &domain.AuthRequestOIDC{},
LoginPolicy: &domain.LoginPolicy{ LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP}, SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
}, },
}, true}, }, true},
[]domain.NextStep{&domain.RedirectToCallbackStep{}}, []domain.NextStep{&domain.RedirectToCallbackStep{}},
@ -1197,15 +1216,15 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true, ShowFailures: true,
}, },
}, },
PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
args{&domain.AuthRequest{ args{&domain.AuthRequest{
UserID: "UserID", UserID: "UserID",
Prompt: []domain.Prompt{domain.PromptNone}, Prompt: []domain.Prompt{domain.PromptNone},
Request: &domain.AuthRequestOIDC{}, Request: &domain.AuthRequestOIDC{},
LoginPolicy: &domain.LoginPolicy{ LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP}, SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
}, },
}, true}, }, true},
[]domain.NextStep{&domain.ProjectRequiredStep{}}, []domain.NextStep{&domain.ProjectRequiredStep{}},
@ -1236,15 +1255,15 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true, ShowFailures: true,
}, },
}, },
PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
args{&domain.AuthRequest{ args{&domain.AuthRequest{
UserID: "UserID", UserID: "UserID",
Prompt: []domain.Prompt{domain.PromptNone}, Prompt: []domain.Prompt{domain.PromptNone},
Request: &domain.AuthRequestOIDC{}, Request: &domain.AuthRequestOIDC{},
LoginPolicy: &domain.LoginPolicy{ LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP}, SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
}, },
}, true}, }, true},
[]domain.NextStep{&domain.RedirectToCallbackStep{}}, []domain.NextStep{&domain.RedirectToCallbackStep{}},
@ -1266,9 +1285,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true, ShowFailures: true,
}, },
}, },
userEventProvider: &mockEventUser{}, loginPolicyProvider: &mockLoginPolicy{
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive}, policy: &query.LoginPolicy{
SecondFactorCheckLifeTime: 18 * time.Hour, SecondFactorCheckLifetime: 18 * time.Hour,
},
},
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
}, },
args{ args{
&domain.AuthRequest{ &domain.AuthRequest{
@ -1299,8 +1322,6 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true, ShowFailures: true,
}, },
}, },
SecondFactorCheckLifeTime: 18 * time.Hour,
PasswordCheckLifeTime: 10 * 24 * time.Hour,
}, },
args{ args{
&domain.AuthRequest{ &domain.AuthRequest{
@ -1308,7 +1329,9 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
SelectedIDPConfigID: "IDPConfigID", SelectedIDPConfigID: "IDPConfigID",
LinkingUsers: []*domain.ExternalUser{{IDPConfigID: "IDPConfigID", ExternalUserID: "UserID", DisplayName: "DisplayName"}}, LinkingUsers: []*domain.ExternalUser{{IDPConfigID: "IDPConfigID", ExternalUserID: "UserID", DisplayName: "DisplayName"}},
LoginPolicy: &domain.LoginPolicy{ LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP}, SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactorCheckLifetime: 18 * time.Hour,
PasswordCheckLifetime: 10 * 24 * time.Hour,
}, },
}, false}, }, false},
[]domain.NextStep{&domain.LinkUsersStep{}}, []domain.NextStep{&domain.LinkUsersStep{}},
@ -1318,22 +1341,17 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
repo := &AuthRequestRepo{ repo := &AuthRequestRepo{
AuthRequests: tt.fields.AuthRequests, AuthRequests: tt.fields.AuthRequests,
View: tt.fields.View, View: tt.fields.View,
UserSessionViewProvider: tt.fields.userSessionViewProvider, UserSessionViewProvider: tt.fields.userSessionViewProvider,
UserViewProvider: tt.fields.userViewProvider, UserViewProvider: tt.fields.userViewProvider,
UserEventProvider: tt.fields.userEventProvider, UserEventProvider: tt.fields.userEventProvider,
OrgViewProvider: tt.fields.orgViewProvider, OrgViewProvider: tt.fields.orgViewProvider,
UserGrantProvider: tt.fields.userGrantProvider, UserGrantProvider: tt.fields.userGrantProvider,
ProjectProvider: tt.fields.projectProvider, ProjectProvider: tt.fields.projectProvider,
ApplicationProvider: tt.fields.applicationProvider, ApplicationProvider: tt.fields.applicationProvider,
LoginPolicyViewProvider: tt.fields.loginPolicyProvider, LoginPolicyViewProvider: tt.fields.loginPolicyProvider,
LockoutPolicyViewProvider: tt.fields.lockoutPolicyProvider, LockoutPolicyViewProvider: tt.fields.lockoutPolicyProvider,
PasswordCheckLifeTime: tt.fields.PasswordCheckLifeTime,
ExternalLoginCheckLifeTime: tt.fields.ExternalLoginCheckLifeTime,
MFAInitSkippedLifeTime: tt.fields.MFAInitSkippedLifeTime,
SecondFactorCheckLifeTime: tt.fields.SecondFactorCheckLifeTime,
MultiFactorCheckLifeTime: tt.fields.MultiFactorCheckLifeTime,
} }
got, err := repo.nextSteps(context.Background(), tt.args.request, tt.args.checkLoggedIn) got, err := repo.nextSteps(context.Background(), tt.args.request, tt.args.checkLoggedIn)
if (err != nil && tt.wantErr == nil) || (tt.wantErr != nil && !tt.wantErr(err)) { if (err != nil && tt.wantErr == nil) || (tt.wantErr != nil && !tt.wantErr(err)) {
@ -1346,11 +1364,6 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
} }
func TestAuthRequestRepo_mfaChecked(t *testing.T) { func TestAuthRequestRepo_mfaChecked(t *testing.T) {
type fields struct {
MFAInitSkippedLifeTime time.Duration
SecondFactorCheckLifeTime time.Duration
MultiFactorCheckLifeTime time.Duration
}
type args struct { type args struct {
userSession *user_model.UserSessionView userSession *user_model.UserSessionView
request *domain.AuthRequest request *domain.AuthRequest
@ -1358,7 +1371,6 @@ func TestAuthRequestRepo_mfaChecked(t *testing.T) {
} }
tests := []struct { tests := []struct {
name string name string
fields fields
args args args args
want domain.NextStep want domain.NextStep
wantChecked bool wantChecked bool
@ -1377,13 +1389,11 @@ func TestAuthRequestRepo_mfaChecked(t *testing.T) {
//}, //},
{ {
"not set up, forced by policy, no mfas configured, error", "not set up, forced by policy, no mfas configured, error",
fields{
MFAInitSkippedLifeTime: 30 * 24 * time.Hour,
},
args{ args{
request: &domain.AuthRequest{ request: &domain.AuthRequest{
LoginPolicy: &domain.LoginPolicy{ LoginPolicy: &domain.LoginPolicy{
ForceMFA: true, ForceMFA: true,
MFAInitSkipLifetime: 30 * 24 * time.Hour,
}, },
}, },
user: &user_model.UserView{ user: &user_model.UserView{
@ -1398,12 +1408,11 @@ func TestAuthRequestRepo_mfaChecked(t *testing.T) {
}, },
{ {
"not set up, no mfas configured, no prompt and true", "not set up, no mfas configured, no prompt and true",
fields{
MFAInitSkippedLifeTime: 30 * 24 * time.Hour,
},
args{ args{
request: &domain.AuthRequest{ request: &domain.AuthRequest{
LoginPolicy: &domain.LoginPolicy{}, LoginPolicy: &domain.LoginPolicy{
MFAInitSkipLifetime: 30 * 24 * time.Hour,
},
}, },
user: &user_model.UserView{ user: &user_model.UserView{
HumanView: &user_model.HumanView{ HumanView: &user_model.HumanView{
@ -1417,13 +1426,11 @@ func TestAuthRequestRepo_mfaChecked(t *testing.T) {
}, },
{ {
"not set up, prompt and false", "not set up, prompt and false",
fields{
MFAInitSkippedLifeTime: 30 * 24 * time.Hour,
},
args{ args{
request: &domain.AuthRequest{ request: &domain.AuthRequest{
LoginPolicy: &domain.LoginPolicy{ LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP}, SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
MFAInitSkipLifetime: 30 * 24 * time.Hour,
}, },
}, },
user: &user_model.UserView{ user: &user_model.UserView{
@ -1442,14 +1449,12 @@ func TestAuthRequestRepo_mfaChecked(t *testing.T) {
}, },
{ {
"not set up, forced by org, true", "not set up, forced by org, true",
fields{
MFAInitSkippedLifeTime: 30 * 24 * time.Hour,
},
args{ args{
request: &domain.AuthRequest{ request: &domain.AuthRequest{
LoginPolicy: &domain.LoginPolicy{ LoginPolicy: &domain.LoginPolicy{
ForceMFA: true, ForceMFA: true,
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP}, SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
MFAInitSkipLifetime: 30 * 24 * time.Hour,
}, },
}, },
user: &user_model.UserView{ user: &user_model.UserView{
@ -1469,12 +1474,11 @@ func TestAuthRequestRepo_mfaChecked(t *testing.T) {
}, },
{ {
"not set up and skipped, true", "not set up and skipped, true",
fields{
MFAInitSkippedLifeTime: 30 * 24 * time.Hour,
},
args{ args{
request: &domain.AuthRequest{ request: &domain.AuthRequest{
LoginPolicy: &domain.LoginPolicy{}, LoginPolicy: &domain.LoginPolicy{
MFAInitSkipLifetime: 30 * 24 * time.Hour,
},
}, },
user: &user_model.UserView{ user: &user_model.UserView{
HumanView: &user_model.HumanView{ HumanView: &user_model.HumanView{
@ -1489,13 +1493,11 @@ func TestAuthRequestRepo_mfaChecked(t *testing.T) {
}, },
{ {
"checked second factor, true", "checked second factor, true",
fields{
SecondFactorCheckLifeTime: 18 * time.Hour,
},
args{ args{
request: &domain.AuthRequest{ request: &domain.AuthRequest{
LoginPolicy: &domain.LoginPolicy{ LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP}, SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactorCheckLifetime: 18 * time.Hour,
}, },
}, },
user: &user_model.UserView{ user: &user_model.UserView{
@ -1512,13 +1514,11 @@ func TestAuthRequestRepo_mfaChecked(t *testing.T) {
}, },
{ {
"not checked, check and false", "not checked, check and false",
fields{
SecondFactorCheckLifeTime: 18 * time.Hour,
},
args{ args{
request: &domain.AuthRequest{ request: &domain.AuthRequest{
LoginPolicy: &domain.LoginPolicy{ LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP}, SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactorCheckLifetime: 18 * time.Hour,
}, },
}, },
user: &user_model.UserView{ user: &user_model.UserView{
@ -1539,11 +1539,7 @@ func TestAuthRequestRepo_mfaChecked(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
repo := &AuthRequestRepo{ repo := &AuthRequestRepo{}
MFAInitSkippedLifeTime: tt.fields.MFAInitSkippedLifeTime,
SecondFactorCheckLifeTime: tt.fields.SecondFactorCheckLifeTime,
MultiFactorCheckLifeTime: tt.fields.MultiFactorCheckLifeTime,
}
got, ok, err := repo.mfaChecked(tt.args.userSession, tt.args.request, tt.args.user) got, ok, err := repo.mfaChecked(tt.args.userSession, tt.args.request, tt.args.user)
if (tt.errFunc != nil && !tt.errFunc(err)) || (err != nil && tt.errFunc == nil) { if (tt.errFunc != nil && !tt.errFunc(err)) || (err != nil && tt.errFunc == nil) {
t.Errorf("got wrong err: %v ", err) t.Errorf("got wrong err: %v ", err)
@ -1562,7 +1558,8 @@ func TestAuthRequestRepo_mfaSkippedOrSetUp(t *testing.T) {
MFAInitSkippedLifeTime time.Duration MFAInitSkippedLifeTime time.Duration
} }
type args struct { type args struct {
user *user_model.UserView user *user_model.UserView
request *domain.AuthRequest
} }
tests := []struct { tests := []struct {
name string name string
@ -1574,51 +1571,58 @@ func TestAuthRequestRepo_mfaSkippedOrSetUp(t *testing.T) {
"mfa set up, true", "mfa set up, true",
fields{}, fields{},
args{ args{
&user_model.UserView{ user: &user_model.UserView{
HumanView: &user_model.HumanView{ HumanView: &user_model.HumanView{
MFAMaxSetUp: model.MFALevelSecondFactor, MFAMaxSetUp: model.MFALevelSecondFactor,
}, },
}, },
request: &domain.AuthRequest{
LoginPolicy: &domain.LoginPolicy{},
},
}, },
true, true,
}, },
{ {
"mfa skipped active, true", "mfa skipped active, true",
fields{ fields{},
MFAInitSkippedLifeTime: 30 * 24 * time.Hour,
},
args{ args{
&user_model.UserView{ user: &user_model.UserView{
HumanView: &user_model.HumanView{ HumanView: &user_model.HumanView{
MFAMaxSetUp: -1, MFAMaxSetUp: -1,
MFAInitSkipped: time.Now().UTC().Add(-10 * time.Hour), MFAInitSkipped: time.Now().UTC().Add(-10 * time.Hour),
}, },
}, },
request: &domain.AuthRequest{
LoginPolicy: &domain.LoginPolicy{
MFAInitSkipLifetime: 30 * 24 * time.Hour,
},
},
}, },
true, true,
}, },
{ {
"mfa skipped inactive, false", "mfa skipped inactive, false",
fields{ fields{},
MFAInitSkippedLifeTime: 30 * 24 * time.Hour,
},
args{ args{
&user_model.UserView{ user: &user_model.UserView{
HumanView: &user_model.HumanView{ HumanView: &user_model.HumanView{
MFAMaxSetUp: -1, MFAMaxSetUp: -1,
MFAInitSkipped: time.Now().UTC().Add(-40 * 24 * time.Hour), MFAInitSkipped: time.Now().UTC().Add(-40 * 24 * time.Hour),
}, },
}, },
request: &domain.AuthRequest{
LoginPolicy: &domain.LoginPolicy{
MFAInitSkipLifetime: 30 * 24 * time.Hour,
},
},
}, },
false, false,
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
repo := &AuthRequestRepo{ repo := &AuthRequestRepo{}
MFAInitSkippedLifeTime: tt.fields.MFAInitSkippedLifeTime, if got := repo.mfaSkippedOrSetUp(tt.args.user, tt.args.request); got != tt.want {
}
if got := repo.mfaSkippedOrSetUp(tt.args.user); got != tt.want {
t.Errorf("mfaSkippedOrSetUp() = %v, want %v", got, tt.want) t.Errorf("mfaSkippedOrSetUp() = %v, want %v", got, tt.want)
} }
}) })

View File

@ -72,31 +72,26 @@ func Start(conf Config, systemDefaults sd.SystemDefaults, command *command.Comma
es, es,
userRepo, userRepo,
eventstore.AuthRequestRepo{ eventstore.AuthRequestRepo{
PrivacyPolicyProvider: queries, PrivacyPolicyProvider: queries,
LabelPolicyProvider: queries, LabelPolicyProvider: queries,
Command: command, Command: command,
Query: queries, Query: queries,
OrgViewProvider: queries, OrgViewProvider: queries,
AuthRequests: authReq, AuthRequests: authReq,
View: view, View: view,
Eventstore: es, Eventstore: es,
UserCodeAlg: userCrypto, UserCodeAlg: userCrypto,
UserSessionViewProvider: view, UserSessionViewProvider: view,
UserViewProvider: view, UserViewProvider: view,
UserCommandProvider: command, UserCommandProvider: command,
UserEventProvider: &userRepo, UserEventProvider: &userRepo,
IDPProviderViewProvider: view, IDPProviderViewProvider: view,
LockoutPolicyViewProvider: queries, LockoutPolicyViewProvider: queries,
LoginPolicyViewProvider: queries, LoginPolicyViewProvider: queries,
UserGrantProvider: queryView, UserGrantProvider: queryView,
ProjectProvider: queryView, ProjectProvider: queryView,
ApplicationProvider: queries, ApplicationProvider: queries,
IdGenerator: idGenerator, IdGenerator: idGenerator,
PasswordCheckLifeTime: systemDefaults.VerificationLifetimes.PasswordCheck,
ExternalLoginCheckLifeTime: systemDefaults.VerificationLifetimes.PasswordCheck,
MFAInitSkippedLifeTime: systemDefaults.VerificationLifetimes.MFAInitSkip,
SecondFactorCheckLifeTime: systemDefaults.VerificationLifetimes.SecondFactorCheck,
MultiFactorCheckLifeTime: systemDefaults.VerificationLifetimes.MultiFactorCheck,
}, },
eventstore.TokenRepo{ eventstore.TokenRepo{
View: view, View: view,

View File

@ -35,13 +35,18 @@ func memberWriteModelToMember(writeModel *MemberWriteModel) *domain.Member {
func writeModelToLoginPolicy(wm *LoginPolicyWriteModel) *domain.LoginPolicy { func writeModelToLoginPolicy(wm *LoginPolicyWriteModel) *domain.LoginPolicy {
return &domain.LoginPolicy{ return &domain.LoginPolicy{
ObjectRoot: writeModelToObjectRoot(wm.WriteModel), ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
AllowUsernamePassword: wm.AllowUserNamePassword, AllowUsernamePassword: wm.AllowUserNamePassword,
AllowRegister: wm.AllowRegister, AllowRegister: wm.AllowRegister,
AllowExternalIDP: wm.AllowExternalIDP, AllowExternalIDP: wm.AllowExternalIDP,
HidePasswordReset: wm.HidePasswordReset, HidePasswordReset: wm.HidePasswordReset,
ForceMFA: wm.ForceMFA, ForceMFA: wm.ForceMFA,
PasswordlessType: wm.PasswordlessType, 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 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) { 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 { if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "IAM-M0sif", "Errors.IAM.LoginPolicy.NotFound") 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 { if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-5M9vdd", "Errors.IAM.LoginPolicy.NotChanged") return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-5M9vdd", "Errors.IAM.LoginPolicy.NotChanged")
} }

View File

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

View File

@ -3,6 +3,7 @@ package command
import ( import (
"context" "context"
"testing" "testing"
"time"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
@ -49,6 +50,11 @@ func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) {
false, false,
false, false,
domain.PasswordlessTypeAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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{ args: args{
ctx: context.Background(), ctx: context.Background(),
policy: &domain.LoginPolicy{ policy: &domain.LoginPolicy{
AllowRegister: true, AllowRegister: true,
AllowUsernamePassword: true, AllowUsernamePassword: true,
AllowExternalIDP: true, AllowExternalIDP: true,
ForceMFA: true, ForceMFA: true,
HidePasswordReset: true, HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed, 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{ res: res{
@ -106,12 +122,17 @@ func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) {
AggregateID: "IAM", AggregateID: "IAM",
ResourceOwner: "IAM", ResourceOwner: "IAM",
}, },
AllowRegister: true, AllowRegister: true,
AllowUsernamePassword: true, AllowUsernamePassword: true,
AllowExternalIDP: true, AllowExternalIDP: true,
ForceMFA: true, ForceMFA: true,
HidePasswordReset: true, HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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{ args: args{
ctx: context.Background(), ctx: context.Background(),
policy: &domain.LoginPolicy{ policy: &domain.LoginPolicy{
AllowRegister: true, AllowRegister: true,
AllowUsernamePassword: true, AllowUsernamePassword: true,
AllowExternalIDP: true, AllowExternalIDP: true,
ForceMFA: true, ForceMFA: true,
HidePasswordReset: true, HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed, 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{ res: res{
@ -222,13 +253,29 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
true, true,
true, true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
), ),
), ),
), ),
expectPush( expectPush(
[]*repository.Event{ []*repository.Event{
eventFromEventPusher( 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{ args: args{
ctx: context.Background(), ctx: context.Background(),
policy: &domain.LoginPolicy{ policy: &domain.LoginPolicy{
AllowRegister: false, AllowRegister: false,
AllowUsernamePassword: false, AllowUsernamePassword: false,
AllowExternalIDP: false, AllowExternalIDP: false,
ForceMFA: false, ForceMFA: false,
HidePasswordReset: false, HidePasswordReset: false,
PasswordlessType: domain.PasswordlessTypeNotAllowed, 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{ res: res{
@ -251,12 +303,17 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
AggregateID: "IAM", AggregateID: "IAM",
ResourceOwner: "IAM", ResourceOwner: "IAM",
}, },
AllowRegister: false, AllowRegister: false,
AllowUsernamePassword: false, AllowUsernamePassword: false,
AllowExternalIDP: false, AllowExternalIDP: false,
ForceMFA: false, ForceMFA: false,
HidePasswordReset: false, HidePasswordReset: false,
PasswordlessType: domain.PasswordlessTypeNotAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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, event, _ := iam.NewLoginPolicyChangedEvent(ctx,
&iam.NewAggregate().Aggregate, &iam.NewAggregate().Aggregate,
[]policy.LoginPolicyChanges{ []policy.LoginPolicyChanges{
@ -1293,6 +1392,11 @@ func newDefaultLoginPolicyChangedEvent(ctx context.Context, allowRegister, allow
policy.ChangeAllowUserNamePassword(allowUsernamePassword), policy.ChangeAllowUserNamePassword(allowUsernamePassword),
policy.ChangeHidePasswordReset(hidePasswordReset), policy.ChangeHidePasswordReset(hidePasswordReset),
policy.ChangePasswordlessType(passwordlessType), policy.ChangePasswordlessType(passwordlessType),
policy.ChangePasswordCheckLifetime(passwordLifetime),
policy.ChangeExternalLoginCheckLifetime(externalLoginLifetime),
policy.ChangeMFAInitSkipLifetime(mfaInitSkipLifetime),
policy.ChangeSecondFactorCheckLifetime(secondFactorLifetime),
policy.ChangeMultiFactorCheckLifetime(multiFactorLifetime),
}, },
) )
return event 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 { if !features.LoginPolicyPasswordReset && defaultPolicy.HidePasswordReset != existingPolicy.HidePasswordReset {
policy.HidePasswordReset = defaultPolicy.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 { if hasChanged {
events = append(events, changedEvent) events = append(events, changedEvent)
} }

View File

@ -5,6 +5,8 @@ import (
"testing" "testing"
"time" "time"
"github.com/caos/zitadel/internal/repository/user"
"github.com/caos/zitadel/internal/static/mock"
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"golang.org/x/text/language" "golang.org/x/text/language"
@ -16,9 +18,7 @@ import (
"github.com/caos/zitadel/internal/repository/features" "github.com/caos/zitadel/internal/repository/features"
"github.com/caos/zitadel/internal/repository/iam" "github.com/caos/zitadel/internal/repository/iam"
"github.com/caos/zitadel/internal/repository/org" "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"
"github.com/caos/zitadel/internal/static/mock"
) )
func TestCommandSide_SetOrgFeatures(t *testing.T) { func TestCommandSide_SetOrgFeatures(t *testing.T) {
@ -165,6 +165,11 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
false, false,
false, false,
domain.PasswordlessTypeAllowed, 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,
false, false,
domain.PasswordlessTypeAllowed, 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,
false, false,
domain.PasswordlessTypeAllowed, 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,
false, false,
domain.PasswordlessTypeAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
), ),
), ),
eventFromEventPusher( eventFromEventPusher(
@ -1002,6 +1027,11 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
false, false,
false, false,
domain.PasswordlessTypeNotAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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), org.NewLoginPolicyMultiFactorAddedEvent(context.Background(), &org.NewAggregate("org1", "org1").Aggregate, domain.MultiFactorTypeU2FWithPIN),
), ),
eventFromEventPusher( 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( eventFromEventPusher(
org.NewPasswordComplexityPolicyRemovedEvent(context.Background(), &org.NewAggregate("org1", "org1").Aggregate), org.NewPasswordComplexityPolicyRemovedEvent(context.Background(), &org.NewAggregate("org1", "org1").Aggregate),
@ -1278,6 +1319,11 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
false, false,
false, false,
domain.PasswordlessTypeAllowed, 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,
false, false,
domain.PasswordlessTypeAllowed, 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.AllowExternalIDP,
policy.ForceMFA, policy.ForceMFA,
policy.HidePasswordReset, policy.HidePasswordReset,
policy.PasswordlessType)) policy.PasswordlessType,
policy.PasswordCheckLifetime,
policy.ExternalLoginCheckLifetime,
policy.MFAInitSkipLifetime,
policy.SecondFactorCheckLifetime,
policy.MultiFactorCheckLifetime))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -100,7 +105,12 @@ func (c *Commands) ChangeLoginPolicy(ctx context.Context, resourceOwner string,
policy.AllowExternalIDP, policy.AllowExternalIDP,
policy.ForceMFA, policy.ForceMFA,
policy.HidePasswordReset, policy.HidePasswordReset,
policy.PasswordlessType) policy.PasswordlessType,
policy.PasswordCheckLifetime,
policy.ExternalLoginCheckLifetime,
policy.MFAInitSkipLifetime,
policy.SecondFactorCheckLifetime,
policy.MultiFactorCheckLifetime)
if !hasChanged { if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-5M9vdd", "Errors.Org.LoginPolicy.NotChanged") return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-5M9vdd", "Errors.Org.LoginPolicy.NotChanged")

View File

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

View File

@ -3,6 +3,7 @@ package command
import ( import (
"context" "context"
"testing" "testing"
"time"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -17,6 +18,14 @@ import (
"github.com/caos/zitadel/internal/repository/user" "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) { func TestCommandSide_AddLoginPolicy(t *testing.T) {
type fields struct { type fields struct {
eventstore *eventstore.Eventstore eventstore *eventstore.Eventstore
@ -71,6 +80,11 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
true, true,
true, true,
domain.PasswordlessTypeAllowed, 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(), ctx: context.Background(),
orgID: "org1", orgID: "org1",
policy: &domain.LoginPolicy{ policy: &domain.LoginPolicy{
AllowRegister: true, AllowRegister: true,
AllowUsernamePassword: true, AllowUsernamePassword: true,
AllowExternalIDP: true, AllowExternalIDP: true,
ForceMFA: true, ForceMFA: true,
PasswordlessType: domain.PasswordlessTypeAllowed, 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{ res: res{
@ -107,6 +126,11 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
true, true,
true, true,
domain.PasswordlessTypeAllowed, 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(), ctx: context.Background(),
orgID: "org1", orgID: "org1",
policy: &domain.LoginPolicy{ policy: &domain.LoginPolicy{
AllowRegister: true, AllowRegister: true,
AllowUsernamePassword: true, AllowUsernamePassword: true,
AllowExternalIDP: true, AllowExternalIDP: true,
ForceMFA: true, ForceMFA: true,
PasswordlessType: domain.PasswordlessTypeAllowed, 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{ res: res{
@ -144,6 +173,11 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
true, true,
true, true,
domain.PasswordlessTypeAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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(), ctx: context.Background(),
orgID: "org1", orgID: "org1",
policy: &domain.LoginPolicy{ policy: &domain.LoginPolicy{
AllowRegister: true, AllowRegister: true,
AllowUsernamePassword: true, AllowUsernamePassword: true,
AllowExternalIDP: true, AllowExternalIDP: true,
ForceMFA: true, ForceMFA: true,
HidePasswordReset: true, HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed, 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{ res: res{
@ -183,12 +227,17 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
AggregateID: "org1", AggregateID: "org1",
ResourceOwner: "org1", ResourceOwner: "org1",
}, },
AllowRegister: true, AllowRegister: true,
AllowUsernamePassword: true, AllowUsernamePassword: true,
AllowExternalIDP: true, AllowExternalIDP: true,
ForceMFA: true, ForceMFA: true,
HidePasswordReset: true, HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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(), ctx: context.Background(),
orgID: "org1", orgID: "org1",
policy: &domain.LoginPolicy{ policy: &domain.LoginPolicy{
AllowRegister: true, AllowRegister: true,
AllowUsernamePassword: true, AllowUsernamePassword: true,
AllowExternalIDP: true, AllowExternalIDP: true,
ForceMFA: true, ForceMFA: true,
PasswordlessType: domain.PasswordlessTypeAllowed, 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{ res: res{
@ -341,6 +405,11 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
true, true,
true, true,
domain.PasswordlessTypeAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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(), ctx: context.Background(),
orgID: "org1", orgID: "org1",
policy: &domain.LoginPolicy{ policy: &domain.LoginPolicy{
AllowRegister: true, AllowRegister: true,
AllowUsernamePassword: true, AllowUsernamePassword: true,
AllowExternalIDP: true, AllowExternalIDP: true,
ForceMFA: true, ForceMFA: true,
HidePasswordReset: true, HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed, 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{ res: res{
@ -391,6 +470,11 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
true, true,
true, true,
domain.PasswordlessTypeAllowed, 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,
false, false,
domain.PasswordlessTypeNotAllowed, domain.PasswordlessTypeNotAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
), ),
), ),
), ),
expectPush( expectPush(
[]*repository.Event{ []*repository.Event{
eventFromEventPusher( 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(), ctx: context.Background(),
orgID: "org1", orgID: "org1",
policy: &domain.LoginPolicy{ policy: &domain.LoginPolicy{
AllowRegister: false, AllowRegister: false,
AllowUsernamePassword: false, AllowUsernamePassword: false,
AllowExternalIDP: false, AllowExternalIDP: false,
ForceMFA: false, ForceMFA: false,
PasswordlessType: domain.PasswordlessTypeNotAllowed, 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{ res: res{
@ -434,12 +541,17 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
AggregateID: "org1", AggregateID: "org1",
ResourceOwner: "org1", ResourceOwner: "org1",
}, },
AllowRegister: false, AllowRegister: false,
AllowUsernamePassword: false, AllowUsernamePassword: false,
AllowExternalIDP: false, AllowExternalIDP: false,
ForceMFA: false, ForceMFA: false,
HidePasswordReset: false, HidePasswordReset: false,
PasswordlessType: domain.PasswordlessTypeNotAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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,
true, true,
domain.PasswordlessTypeAllowed, 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, event, _ := org.NewLoginPolicyChangedEvent(ctx,
&org.NewAggregate(orgID, orgID).Aggregate, &org.NewAggregate(orgID, orgID).Aggregate,
[]policy.LoginPolicyChanges{ changes,
policy.ChangeAllowUserNamePassword(usernamePassword),
policy.ChangeAllowRegister(register),
policy.ChangeAllowExternalIDP(externalIDP),
policy.ChangeForceMFA(mfa),
policy.ChangeHidePasswordReset(passwordReset),
policy.ChangePasswordlessType(passwordlessType),
},
) )
return event return event
} }

View File

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

View File

@ -1157,6 +1157,11 @@ func TestCommandSide_CheckPassword(t *testing.T) {
false, false,
false, false,
domain.PasswordlessTypeNotAllowed, 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,
false, false,
domain.PasswordlessTypeNotAllowed, 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,
false, false,
domain.PasswordlessTypeNotAllowed, 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,
false, false,
domain.PasswordlessTypeNotAllowed, 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,
false, false,
domain.PasswordlessTypeNotAllowed, 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,
false, false,
domain.PasswordlessTypeNotAllowed, 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,
false, false,
domain.PasswordlessTypeNotAllowed, 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,
false, false,
domain.PasswordlessTypeNotAllowed, 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,
false, false,
domain.PasswordlessTypeNotAllowed, 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,
false, false,
domain.PasswordlessTypeNotAllowed, 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,
false, false,
domain.PasswordlessTypeNotAllowed, 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,
false, false,
domain.PasswordlessTypeNotAllowed, 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,
false, false,
domain.PasswordlessTypeNotAllowed, 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,
false, false,
domain.PasswordlessTypeNotAllowed, domain.PasswordlessTypeNotAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
), ),
), ),
), ),

View File

@ -23,7 +23,6 @@ type SystemDefaults struct {
SMTPPasswordVerificationKey *crypto.KeyConfig SMTPPasswordVerificationKey *crypto.KeyConfig
SMSVerificationKey *crypto.KeyConfig SMSVerificationKey *crypto.KeyConfig
Multifactors MultifactorConfig Multifactors MultifactorConfig
VerificationLifetimes VerificationLifetimes
DomainVerification DomainVerification DomainVerification DomainVerification
Notifications Notifications Notifications Notifications
KeyConfig KeyConfig KeyConfig KeyConfig
@ -49,14 +48,6 @@ type OTPConfig struct {
VerificationKey *crypto.KeyConfig VerificationKey *crypto.KeyConfig
} }
type VerificationLifetimes struct {
PasswordCheck time.Duration
ExternalLoginCheck time.Duration
MFAInitSkip time.Duration
SecondFactorCheck time.Duration
MultiFactorCheck time.Duration
}
type DomainVerification struct { type DomainVerification struct {
VerificationKey *crypto.KeyConfig VerificationKey *crypto.KeyConfig
VerificationGenerator crypto.GeneratorConfig VerificationGenerator crypto.GeneratorConfig

View File

@ -1,20 +1,29 @@
package domain package domain
import "github.com/caos/zitadel/internal/eventstore/v1/models" import (
"time"
"github.com/caos/zitadel/internal/eventstore/v1/models"
)
type LoginPolicy struct { type LoginPolicy struct {
models.ObjectRoot models.ObjectRoot
Default bool Default bool
AllowUsernamePassword bool AllowUsernamePassword bool
AllowRegister bool AllowRegister bool
AllowExternalIDP bool AllowExternalIDP bool
IDPProviders []*IDPProvider IDPProviders []*IDPProvider
ForceMFA bool ForceMFA bool
SecondFactors []SecondFactorType SecondFactors []SecondFactorType
MultiFactors []MultiFactorType MultiFactors []MultiFactorType
PasswordlessType PasswordlessType PasswordlessType PasswordlessType
HidePasswordReset bool HidePasswordReset bool
PasswordCheckLifetime time.Duration
ExternalLoginCheckLifetime time.Duration
MFAInitSkipLifetime time.Duration
SecondFactorCheckLifetime time.Duration
MultiFactorCheckLifetime time.Duration
} }
type IDPProvider struct { type IDPProvider struct {

View File

@ -14,19 +14,24 @@ import (
) )
type LoginPolicy struct { type LoginPolicy struct {
OrgID string OrgID string
CreationDate time.Time CreationDate time.Time
ChangeDate time.Time ChangeDate time.Time
Sequence uint64 Sequence uint64
AllowRegister bool AllowRegister bool
AllowUsernamePassword bool AllowUsernamePassword bool
AllowExternalIDPs bool AllowExternalIDPs bool
ForceMFA bool ForceMFA bool
SecondFactors []domain.SecondFactorType SecondFactors []domain.SecondFactorType
MultiFactors []domain.MultiFactorType MultiFactors []domain.MultiFactorType
PasswordlessType domain.PasswordlessType PasswordlessType domain.PasswordlessType
IsDefault bool IsDefault bool
HidePasswordReset bool HidePasswordReset bool
PasswordCheckLifetime time.Duration
ExternalLoginCheckLifetime time.Duration
MFAInitSkipLifetime time.Duration
SecondFactorCheckLifetime time.Duration
MultiFactorCheckLifetime time.Duration
} }
type SecondFactors struct { type SecondFactors struct {
@ -95,6 +100,26 @@ var (
name: projection.LoginPolicyHidePWResetCol, name: projection.LoginPolicyHidePWResetCol,
table: loginPolicyTable, table: loginPolicyTable,
} }
LoginPolicyColumnPasswordCheckLifetime = Column{
name: projection.PasswordCheckLifetimeCol,
table: loginPolicyTable,
}
LoginPolicyColumnExternalLoginCheckLifetime = Column{
name: projection.ExternalLoginCheckLifetimeCol,
table: loginPolicyTable,
}
LoginPolicyColumnMFAInitSkipLifetime = Column{
name: projection.MFAInitSkipLifetimeCol,
table: loginPolicyTable,
}
LoginPolicyColumnSecondFactorCheckLifetime = Column{
name: projection.SecondFactorCheckLifetimeCol,
table: loginPolicyTable,
}
LoginPolicyColumnMultiFacotrCheckLifetime = Column{
name: projection.MultiFactorCheckLifetimeCol,
table: loginPolicyTable,
}
) )
func (q *Queries) LoginPolicyByID(ctx context.Context, orgID string) (*LoginPolicy, error) { func (q *Queries) LoginPolicyByID(ctx context.Context, orgID string) (*LoginPolicy, error) {
@ -234,6 +259,11 @@ func prepareLoginPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*LoginPolicy,
LoginPolicyColumnPasswordlessType.identifier(), LoginPolicyColumnPasswordlessType.identifier(),
LoginPolicyColumnIsDefault.identifier(), LoginPolicyColumnIsDefault.identifier(),
LoginPolicyColumnHidePasswordReset.identifier(), LoginPolicyColumnHidePasswordReset.identifier(),
LoginPolicyColumnPasswordCheckLifetime.identifier(),
LoginPolicyColumnExternalLoginCheckLifetime.identifier(),
LoginPolicyColumnMFAInitSkipLifetime.identifier(),
LoginPolicyColumnSecondFactorCheckLifetime.identifier(),
LoginPolicyColumnMultiFacotrCheckLifetime.identifier(),
).From(loginPolicyTable.identifier()).PlaceholderFormat(sq.Dollar), ).From(loginPolicyTable.identifier()).PlaceholderFormat(sq.Dollar),
func(row *sql.Row) (*LoginPolicy, error) { func(row *sql.Row) (*LoginPolicy, error) {
p := new(LoginPolicy) p := new(LoginPolicy)
@ -253,6 +283,11 @@ func prepareLoginPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*LoginPolicy,
&p.PasswordlessType, &p.PasswordlessType,
&p.IsDefault, &p.IsDefault,
&p.HidePasswordReset, &p.HidePasswordReset,
&p.PasswordCheckLifetime,
&p.ExternalLoginCheckLifetime,
&p.MFAInitSkipLifetime,
&p.SecondFactorCheckLifetime,
&p.MultiFactorCheckLifetime,
) )
if err != nil { if err != nil {
if errs.Is(err, sql.ErrNoRows) { if errs.Is(err, sql.ErrNoRows) {

View File

@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"regexp" "regexp"
"testing" "testing"
"time"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
errs "github.com/caos/zitadel/internal/errors" errs "github.com/caos/zitadel/internal/errors"
@ -41,7 +42,12 @@ func Test_LoginPolicyPrepares(t *testing.T) {
` zitadel.projections.login_policies.multi_factors,`+ ` zitadel.projections.login_policies.multi_factors,`+
` zitadel.projections.login_policies.passwordless_type,`+ ` zitadel.projections.login_policies.passwordless_type,`+
` zitadel.projections.login_policies.is_default,`+ ` zitadel.projections.login_policies.is_default,`+
` zitadel.projections.login_policies.hide_password_reset`+ ` zitadel.projections.login_policies.hide_password_reset,`+
` zitadel.projections.login_policies.password_check_lifetime,`+
` zitadel.projections.login_policies.external_login_check_lifetime,`+
` zitadel.projections.login_policies.mfa_init_skip_lifetime,`+
` zitadel.projections.login_policies.second_factor_check_lifetime,`+
` zitadel.projections.login_policies.multi_factor_check_lifetime`+
` FROM zitadel.projections.login_policies`), ` FROM zitadel.projections.login_policies`),
nil, nil,
nil, nil,
@ -72,7 +78,12 @@ func Test_LoginPolicyPrepares(t *testing.T) {
` zitadel.projections.login_policies.multi_factors,`+ ` zitadel.projections.login_policies.multi_factors,`+
` zitadel.projections.login_policies.passwordless_type,`+ ` zitadel.projections.login_policies.passwordless_type,`+
` zitadel.projections.login_policies.is_default,`+ ` zitadel.projections.login_policies.is_default,`+
` zitadel.projections.login_policies.hide_password_reset`+ ` zitadel.projections.login_policies.hide_password_reset,`+
` zitadel.projections.login_policies.password_check_lifetime,`+
` zitadel.projections.login_policies.external_login_check_lifetime,`+
` zitadel.projections.login_policies.mfa_init_skip_lifetime,`+
` zitadel.projections.login_policies.second_factor_check_lifetime,`+
` zitadel.projections.login_policies.multi_factor_check_lifetime`+
` FROM zitadel.projections.login_policies`), ` FROM zitadel.projections.login_policies`),
[]string{ []string{
"aggregate_id", "aggregate_id",
@ -88,6 +99,11 @@ func Test_LoginPolicyPrepares(t *testing.T) {
"passwordless_type", "passwordless_type",
"is_default", "is_default",
"hide_password_reset", "hide_password_reset",
"password_check_lifetime",
"external_login_check_lifetime",
"mfa_init_skip_lifetime",
"second_factor_check_lifetime",
"multi_factor_check_lifetime",
}, },
[]driver.Value{ []driver.Value{
"ro", "ro",
@ -103,23 +119,33 @@ func Test_LoginPolicyPrepares(t *testing.T) {
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
true, true,
true, true,
time.Hour * 2,
time.Hour * 2,
time.Hour * 2,
time.Hour * 2,
time.Hour * 2,
}, },
), ),
}, },
object: &LoginPolicy{ object: &LoginPolicy{
OrgID: "ro", OrgID: "ro",
CreationDate: testNow, CreationDate: testNow,
ChangeDate: testNow, ChangeDate: testNow,
Sequence: 20211109, Sequence: 20211109,
AllowRegister: true, AllowRegister: true,
AllowUsernamePassword: true, AllowUsernamePassword: true,
AllowExternalIDPs: true, AllowExternalIDPs: true,
ForceMFA: true, ForceMFA: true,
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP}, SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
MultiFactors: []domain.MultiFactorType{domain.MultiFactorTypeU2FWithPIN}, MultiFactors: []domain.MultiFactorType{domain.MultiFactorTypeU2FWithPIN},
PasswordlessType: domain.PasswordlessTypeAllowed, PasswordlessType: domain.PasswordlessTypeAllowed,
IsDefault: true, IsDefault: true,
HidePasswordReset: true, HidePasswordReset: true,
PasswordCheckLifetime: time.Hour * 2,
ExternalLoginCheckLifetime: time.Hour * 2,
MFAInitSkipLifetime: time.Hour * 2,
SecondFactorCheckLifetime: time.Hour * 2,
MultiFactorCheckLifetime: time.Hour * 2,
}, },
}, },
{ {
@ -139,7 +165,12 @@ func Test_LoginPolicyPrepares(t *testing.T) {
` zitadel.projections.login_policies.multi_factors,`+ ` zitadel.projections.login_policies.multi_factors,`+
` zitadel.projections.login_policies.passwordless_type,`+ ` zitadel.projections.login_policies.passwordless_type,`+
` zitadel.projections.login_policies.is_default,`+ ` zitadel.projections.login_policies.is_default,`+
` zitadel.projections.login_policies.hide_password_reset`+ ` zitadel.projections.login_policies.hide_password_reset,`+
` zitadel.projections.login_policies.password_check_lifetime,`+
` zitadel.projections.login_policies.external_login_check_lifetime,`+
` zitadel.projections.login_policies.mfa_init_skip_lifetime,`+
` zitadel.projections.login_policies.second_factor_check_lifetime,`+
` zitadel.projections.login_policies.multi_factor_check_lifetime`+
` FROM zitadel.projections.login_policies`), ` FROM zitadel.projections.login_policies`),
sql.ErrConnDone, sql.ErrConnDone,
), ),

View File

@ -111,6 +111,11 @@ const (
LoginPolicyPasswordlessTypeCol = "passwordless_type" LoginPolicyPasswordlessTypeCol = "passwordless_type"
LoginPolicyIsDefaultCol = "is_default" LoginPolicyIsDefaultCol = "is_default"
LoginPolicyHidePWResetCol = "hide_password_reset" LoginPolicyHidePWResetCol = "hide_password_reset"
PasswordCheckLifetimeCol = "password_check_lifetime"
ExternalLoginCheckLifetimeCol = "external_login_check_lifetime"
MFAInitSkipLifetimeCol = "mfa_init_skip_lifetime"
SecondFactorCheckLifetimeCol = "second_factor_check_lifetime"
MultiFactorCheckLifetimeCol = "multi_factor_check_lifetime"
) )
func (p *LoginPolicyProjection) reduceLoginPolicyAdded(event eventstore.Event) (*handler.Statement, error) { func (p *LoginPolicyProjection) reduceLoginPolicyAdded(event eventstore.Event) (*handler.Statement, error) {
@ -140,6 +145,11 @@ func (p *LoginPolicyProjection) reduceLoginPolicyAdded(event eventstore.Event) (
handler.NewCol(LoginPolicyPasswordlessTypeCol, policyEvent.PasswordlessType), handler.NewCol(LoginPolicyPasswordlessTypeCol, policyEvent.PasswordlessType),
handler.NewCol(LoginPolicyIsDefaultCol, isDefault), handler.NewCol(LoginPolicyIsDefaultCol, isDefault),
handler.NewCol(LoginPolicyHidePWResetCol, policyEvent.HidePasswordReset), handler.NewCol(LoginPolicyHidePWResetCol, policyEvent.HidePasswordReset),
handler.NewCol(PasswordCheckLifetimeCol, policyEvent.PasswordCheckLifetime),
handler.NewCol(ExternalLoginCheckLifetimeCol, policyEvent.ExternalLoginCheckLifetime),
handler.NewCol(MFAInitSkipLifetimeCol, policyEvent.MFAInitSkipLifetime),
handler.NewCol(SecondFactorCheckLifetimeCol, policyEvent.SecondFactorCheckLifetime),
handler.NewCol(MultiFactorCheckLifetimeCol, policyEvent.MultiFactorCheckLifetime),
}), nil }), nil
} }
@ -177,6 +187,22 @@ func (p *LoginPolicyProjection) reduceLoginPolicyChanged(event eventstore.Event)
if policyEvent.HidePasswordReset != nil { if policyEvent.HidePasswordReset != nil {
cols = append(cols, handler.NewCol(LoginPolicyHidePWResetCol, *policyEvent.HidePasswordReset)) cols = append(cols, handler.NewCol(LoginPolicyHidePWResetCol, *policyEvent.HidePasswordReset))
} }
if policyEvent.PasswordCheckLifetime != nil {
cols = append(cols, handler.NewCol(PasswordCheckLifetimeCol, *policyEvent.PasswordCheckLifetime))
}
if policyEvent.ExternalLoginCheckLifetime != nil {
cols = append(cols, handler.NewCol(ExternalLoginCheckLifetimeCol, *policyEvent.ExternalLoginCheckLifetime))
}
if policyEvent.MFAInitSkipLifetime != nil {
cols = append(cols, handler.NewCol(MFAInitSkipLifetimeCol, *policyEvent.MFAInitSkipLifetime))
}
if policyEvent.SecondFactorCheckLifetime != nil {
cols = append(cols, handler.NewCol(SecondFactorCheckLifetimeCol, *policyEvent.SecondFactorCheckLifetime))
}
if policyEvent.MultiFactorCheckLifetime != nil {
cols = append(cols, handler.NewCol(MultiFactorCheckLifetimeCol, *policyEvent.MultiFactorCheckLifetime))
}
return crdb.NewUpdateStatement( return crdb.NewUpdateStatement(
&policyEvent, &policyEvent,
cols, cols,

View File

@ -2,6 +2,7 @@ package projection
import ( import (
"testing" "testing"
"time"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
@ -29,13 +30,18 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
repository.EventType(org.LoginPolicyAddedEventType), repository.EventType(org.LoginPolicyAddedEventType),
org.AggregateType, org.AggregateType,
[]byte(`{ []byte(`{
"allowUsernamePassword": true, "allowUsernamePassword": true,
"allowRegister": true, "allowRegister": true,
"allowExternalIdp": false, "allowExternalIdp": false,
"forceMFA": false, "forceMFA": false,
"hidePasswordReset": true, "hidePasswordReset": true,
"passwordlessType": 1 "passwordlessType": 1,
}`), "passwordCheckLifetime": 10000000,
"externalLoginCheckLifetime": 10000000,
"mfaInitSkipLifetime": 10000000,
"secondFactorCheckLifetime": 10000000,
"multiFactorCheckLifetime": 10000000
}`),
), org.LoginPolicyAddedEventMapper), ), org.LoginPolicyAddedEventMapper),
}, },
reduce: (&LoginPolicyProjection{}).reduceLoginPolicyAdded, reduce: (&LoginPolicyProjection{}).reduceLoginPolicyAdded,
@ -47,7 +53,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{ executer: &testExecuter{
executions: []execution{ executions: []execution{
{ {
expectedStmt: "INSERT INTO zitadel.projections.login_policies (aggregate_id, creation_date, change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, passwordless_type, is_default, hide_password_reset) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)", expectedStmt: "INSERT INTO zitadel.projections.login_policies (aggregate_id, creation_date, change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, passwordless_type, is_default, hide_password_reset, password_check_lifetime, external_login_check_lifetime, mfa_init_skip_lifetime, second_factor_check_lifetime, multi_factor_check_lifetime) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)",
expectedArgs: []interface{}{ expectedArgs: []interface{}{
"agg-id", "agg-id",
anyArg{}, anyArg{},
@ -60,6 +66,11 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
false, false,
true, true,
time.Millisecond * 10,
time.Millisecond * 10,
time.Millisecond * 10,
time.Millisecond * 10,
time.Millisecond * 10,
}, },
}, },
}, },
@ -74,13 +85,18 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
repository.EventType(org.LoginPolicyChangedEventType), repository.EventType(org.LoginPolicyChangedEventType),
org.AggregateType, org.AggregateType,
[]byte(`{ []byte(`{
"allowUsernamePassword": true, "allowUsernamePassword": true,
"allowRegister": true, "allowRegister": true,
"allowExternalIdp": true, "allowExternalIdp": true,
"forceMFA": true, "forceMFA": true,
"hidePasswordReset": true, "hidePasswordReset": true,
"passwordlessType": 1 "passwordlessType": 1,
}`), "passwordCheckLifetime": 10000000,
"externalLoginCheckLifetime": 10000000,
"mfaInitSkipLifetime": 10000000,
"secondFactorCheckLifetime": 10000000,
"multiFactorCheckLifetime": 10000000
}`),
), org.LoginPolicyChangedEventMapper), ), org.LoginPolicyChangedEventMapper),
}, },
want: wantReduce{ want: wantReduce{
@ -91,7 +107,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{ executer: &testExecuter{
executions: []execution{ executions: []execution{
{ {
expectedStmt: "UPDATE zitadel.projections.login_policies SET (change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, passwordless_type, hide_password_reset) = ($1, $2, $3, $4, $5, $6, $7, $8) WHERE (aggregate_id = $9)", expectedStmt: "UPDATE zitadel.projections.login_policies SET (change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, passwordless_type, hide_password_reset, password_check_lifetime, external_login_check_lifetime, mfa_init_skip_lifetime, second_factor_check_lifetime, multi_factor_check_lifetime) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) WHERE (aggregate_id = $14)",
expectedArgs: []interface{}{ expectedArgs: []interface{}{
anyArg{}, anyArg{},
uint64(15), uint64(15),
@ -101,6 +117,11 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
true, true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
true, true,
time.Millisecond * 10,
time.Millisecond * 10,
time.Millisecond * 10,
time.Millisecond * 10,
time.Millisecond * 10,
"agg-id", "agg-id",
}, },
}, },
@ -271,12 +292,17 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
repository.EventType(iam.LoginPolicyAddedEventType), repository.EventType(iam.LoginPolicyAddedEventType),
iam.AggregateType, iam.AggregateType,
[]byte(`{ []byte(`{
"allowUsernamePassword": true, "allowUsernamePassword": true,
"allowRegister": true, "allowRegister": true,
"allowExternalIdp": false, "allowExternalIdp": false,
"forceMFA": false, "forceMFA": false,
"hidePasswordReset": true, "hidePasswordReset": true,
"passwordlessType": 1 "passwordlessType": 1,
"passwordCheckLifetime": 10000000,
"externalLoginCheckLifetime": 10000000,
"mfaInitSkipLifetime": 10000000,
"secondFactorCheckLifetime": 10000000,
"multiFactorCheckLifetime": 10000000
}`), }`),
), iam.LoginPolicyAddedEventMapper), ), iam.LoginPolicyAddedEventMapper),
}, },
@ -288,7 +314,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{ executer: &testExecuter{
executions: []execution{ executions: []execution{
{ {
expectedStmt: "INSERT INTO zitadel.projections.login_policies (aggregate_id, creation_date, change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, passwordless_type, is_default, hide_password_reset) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)", expectedStmt: "INSERT INTO zitadel.projections.login_policies (aggregate_id, creation_date, change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, passwordless_type, is_default, hide_password_reset, password_check_lifetime, external_login_check_lifetime, mfa_init_skip_lifetime, second_factor_check_lifetime, multi_factor_check_lifetime) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)",
expectedArgs: []interface{}{ expectedArgs: []interface{}{
"agg-id", "agg-id",
anyArg{}, anyArg{},
@ -301,6 +327,11 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
true, true,
true, true,
time.Millisecond * 10,
time.Millisecond * 10,
time.Millisecond * 10,
time.Millisecond * 10,
time.Millisecond * 10,
}, },
}, },
}, },

View File

@ -2,6 +2,7 @@ package iam
import ( import (
"context" "context"
"time"
"github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore"
@ -28,6 +29,11 @@ func NewLoginPolicyAddedEvent(
forceMFA, forceMFA,
hidePasswordReset bool, hidePasswordReset bool,
passwordlessType domain.PasswordlessType, passwordlessType domain.PasswordlessType,
passwordCheckLifetime,
externalLoginCheckLifetime,
mfaInitSkipLifetime,
secondFactorCheckLifetime,
multiFactorCheckLifetime time.Duration,
) *LoginPolicyAddedEvent { ) *LoginPolicyAddedEvent {
return &LoginPolicyAddedEvent{ return &LoginPolicyAddedEvent{
LoginPolicyAddedEvent: *policy.NewLoginPolicyAddedEvent( LoginPolicyAddedEvent: *policy.NewLoginPolicyAddedEvent(
@ -40,7 +46,12 @@ func NewLoginPolicyAddedEvent(
allowExternalIDP, allowExternalIDP,
forceMFA, forceMFA,
hidePasswordReset, hidePasswordReset,
passwordlessType), passwordlessType,
passwordCheckLifetime,
externalLoginCheckLifetime,
mfaInitSkipLifetime,
secondFactorCheckLifetime,
multiFactorCheckLifetime),
} }
} }

View File

@ -2,6 +2,7 @@ package org
import ( import (
"context" "context"
"time"
"github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore"
@ -29,6 +30,11 @@ func NewLoginPolicyAddedEvent(
forceMFA, forceMFA,
hidePasswordReset bool, hidePasswordReset bool,
passwordlessType domain.PasswordlessType, passwordlessType domain.PasswordlessType,
passwordCheckLifetime,
externalLoginCheckLifetime,
mfaInitSkipLifetime,
secondFactorCheckLifetime,
multiFactorCheckLifetime time.Duration,
) *LoginPolicyAddedEvent { ) *LoginPolicyAddedEvent {
return &LoginPolicyAddedEvent{ return &LoginPolicyAddedEvent{
LoginPolicyAddedEvent: *policy.NewLoginPolicyAddedEvent( LoginPolicyAddedEvent: *policy.NewLoginPolicyAddedEvent(
@ -41,7 +47,12 @@ func NewLoginPolicyAddedEvent(
allowExternalIDP, allowExternalIDP,
forceMFA, forceMFA,
hidePasswordReset, hidePasswordReset,
passwordlessType), passwordlessType,
passwordCheckLifetime,
externalLoginCheckLifetime,
mfaInitSkipLifetime,
secondFactorCheckLifetime,
multiFactorCheckLifetime),
} }
} }

View File

@ -2,6 +2,7 @@ package policy
import ( import (
"encoding/json" "encoding/json"
"time"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
@ -19,12 +20,17 @@ const (
type LoginPolicyAddedEvent struct { type LoginPolicyAddedEvent struct {
eventstore.BaseEvent `json:"-"` eventstore.BaseEvent `json:"-"`
AllowUserNamePassword bool `json:"allowUsernamePassword,omitempty"` AllowUserNamePassword bool `json:"allowUsernamePassword,omitempty"`
AllowRegister bool `json:"allowRegister,omitempty"` AllowRegister bool `json:"allowRegister,omitempty"`
AllowExternalIDP bool `json:"allowExternalIdp,omitempty"` AllowExternalIDP bool `json:"allowExternalIdp,omitempty"`
ForceMFA bool `json:"forceMFA,omitempty"` ForceMFA bool `json:"forceMFA,omitempty"`
HidePasswordReset bool `json:"hidePasswordReset,omitempty"` HidePasswordReset bool `json:"hidePasswordReset,omitempty"`
PasswordlessType domain.PasswordlessType `json:"passwordlessType,omitempty"` PasswordlessType domain.PasswordlessType `json:"passwordlessType,omitempty"`
PasswordCheckLifetime time.Duration `json:"passwordCheckLifetime,omitempty"`
ExternalLoginCheckLifetime time.Duration `json:"externalLoginCheckLifetime,omitempty"`
MFAInitSkipLifetime time.Duration `json:"mfaInitSkipLifetime,omitempty"`
SecondFactorCheckLifetime time.Duration `json:"secondFactorCheckLifetime,omitempty"`
MultiFactorCheckLifetime time.Duration `json:"multiFactorCheckLifetime,omitempty"`
} }
func (e *LoginPolicyAddedEvent) Data() interface{} { func (e *LoginPolicyAddedEvent) Data() interface{} {
@ -43,15 +49,25 @@ func NewLoginPolicyAddedEvent(
forceMFA, forceMFA,
hidePasswordReset bool, hidePasswordReset bool,
passwordlessType domain.PasswordlessType, passwordlessType domain.PasswordlessType,
passwordCheckLifetime,
externalLoginCheckLifetime,
mfaInitSkipLifetime,
secondFactorCheckLifetime,
multiFactorCheckLifetime time.Duration,
) *LoginPolicyAddedEvent { ) *LoginPolicyAddedEvent {
return &LoginPolicyAddedEvent{ return &LoginPolicyAddedEvent{
BaseEvent: *base, BaseEvent: *base,
AllowExternalIDP: allowExternalIDP, AllowExternalIDP: allowExternalIDP,
AllowRegister: allowRegister, AllowRegister: allowRegister,
AllowUserNamePassword: allowUserNamePassword, AllowUserNamePassword: allowUserNamePassword,
ForceMFA: forceMFA, ForceMFA: forceMFA,
PasswordlessType: passwordlessType, PasswordlessType: passwordlessType,
HidePasswordReset: hidePasswordReset, HidePasswordReset: hidePasswordReset,
PasswordCheckLifetime: passwordCheckLifetime,
ExternalLoginCheckLifetime: externalLoginCheckLifetime,
MFAInitSkipLifetime: mfaInitSkipLifetime,
SecondFactorCheckLifetime: secondFactorCheckLifetime,
MultiFactorCheckLifetime: multiFactorCheckLifetime,
} }
} }
@ -71,12 +87,17 @@ func LoginPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, err
type LoginPolicyChangedEvent struct { type LoginPolicyChangedEvent struct {
eventstore.BaseEvent `json:"-"` eventstore.BaseEvent `json:"-"`
AllowUserNamePassword *bool `json:"allowUsernamePassword,omitempty"` AllowUserNamePassword *bool `json:"allowUsernamePassword,omitempty"`
AllowRegister *bool `json:"allowRegister,omitempty"` AllowRegister *bool `json:"allowRegister,omitempty"`
AllowExternalIDP *bool `json:"allowExternalIdp,omitempty"` AllowExternalIDP *bool `json:"allowExternalIdp,omitempty"`
ForceMFA *bool `json:"forceMFA,omitempty"` ForceMFA *bool `json:"forceMFA,omitempty"`
HidePasswordReset *bool `json:"hidePasswordReset,omitempty"` HidePasswordReset *bool `json:"hidePasswordReset,omitempty"`
PasswordlessType *domain.PasswordlessType `json:"passwordlessType,omitempty"` PasswordlessType *domain.PasswordlessType `json:"passwordlessType,omitempty"`
PasswordCheckLifetime *time.Duration `json:"passwordCheckLifetime,omitempty"`
ExternalLoginCheckLifetime *time.Duration `json:"externalLoginCheckLifetime,omitempty"`
MFAInitSkipLifetime *time.Duration `json:"mfaInitSkipLifetime,omitempty"`
SecondFactorCheckLifetime *time.Duration `json:"secondFactorCheckLifetime,omitempty"`
MultiFactorCheckLifetime *time.Duration `json:"multiFactorCheckLifetime,omitempty"`
} }
func (e *LoginPolicyChangedEvent) Data() interface{} { func (e *LoginPolicyChangedEvent) Data() interface{} {
@ -141,6 +162,31 @@ func ChangeHidePasswordReset(hidePasswordReset bool) func(*LoginPolicyChangedEve
} }
} }
func ChangePasswordCheckLifetime(passwordCheckLifetime time.Duration) func(*LoginPolicyChangedEvent) {
return func(e *LoginPolicyChangedEvent) {
e.PasswordCheckLifetime = &passwordCheckLifetime
}
}
func ChangeExternalLoginCheckLifetime(externalLoginCheckLifetime time.Duration) func(*LoginPolicyChangedEvent) {
return func(e *LoginPolicyChangedEvent) {
e.ExternalLoginCheckLifetime = &externalLoginCheckLifetime
}
}
func ChangeMFAInitSkipLifetime(mfaInitSkipLifetime time.Duration) func(*LoginPolicyChangedEvent) {
return func(e *LoginPolicyChangedEvent) {
e.MFAInitSkipLifetime = &mfaInitSkipLifetime
}
}
func ChangeSecondFactorCheckLifetime(secondFactorCheckLifetime time.Duration) func(*LoginPolicyChangedEvent) {
return func(e *LoginPolicyChangedEvent) {
e.SecondFactorCheckLifetime = &secondFactorCheckLifetime
}
}
func ChangeMultiFactorCheckLifetime(multiFactorCheckLifetime time.Duration) func(*LoginPolicyChangedEvent) {
return func(e *LoginPolicyChangedEvent) {
e.MultiFactorCheckLifetime = &multiFactorCheckLifetime
}
}
func LoginPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { func LoginPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) {
e := &LoginPolicyChangedEvent{ e := &LoginPolicyChangedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event), BaseEvent: *eventstore.BaseEventFromRepo(event),

View File

@ -3459,6 +3459,11 @@ message UpdateLoginPolicyRequest {
description: "defines if password reset link should be shown in the login screen" description: "defines if password reset link should be shown in the login screen"
} }
]; ];
google.protobuf.Duration password_check_lifetime = 7;
google.protobuf.Duration external_login_check_lifetime = 8;
google.protobuf.Duration mfa_init_skip_lifetime = 9;
google.protobuf.Duration second_factor_check_lifetime = 10;
google.protobuf.Duration multi_factor_check_lifetime = 11;
} }
message UpdateLoginPolicyResponse { message UpdateLoginPolicyResponse {

View File

@ -4397,6 +4397,11 @@ message AddCustomLoginPolicyRequest {
bool force_mfa = 4; bool force_mfa = 4;
zitadel.policy.v1.PasswordlessType passwordless_type = 5 [(validate.rules).enum = {defined_only: true}]; zitadel.policy.v1.PasswordlessType passwordless_type = 5 [(validate.rules).enum = {defined_only: true}];
bool hide_password_reset = 6; bool hide_password_reset = 6;
google.protobuf.Duration password_check_lifetime = 7;
google.protobuf.Duration external_login_check_lifetime = 8;
google.protobuf.Duration mfa_init_skip_lifetime = 9;
google.protobuf.Duration second_factor_check_lifetime = 10;
google.protobuf.Duration multi_factor_check_lifetime = 11;
} }
message AddCustomLoginPolicyResponse { message AddCustomLoginPolicyResponse {
@ -4410,6 +4415,11 @@ message UpdateCustomLoginPolicyRequest {
bool force_mfa = 4; bool force_mfa = 4;
zitadel.policy.v1.PasswordlessType passwordless_type = 5 [(validate.rules).enum = {defined_only: true}]; zitadel.policy.v1.PasswordlessType passwordless_type = 5 [(validate.rules).enum = {defined_only: true}];
bool hide_password_reset = 6; bool hide_password_reset = 6;
google.protobuf.Duration password_check_lifetime = 7;
google.protobuf.Duration external_login_check_lifetime = 8;
google.protobuf.Duration mfa_init_skip_lifetime = 9;
google.protobuf.Duration second_factor_check_lifetime = 10;
google.protobuf.Duration multi_factor_check_lifetime = 11;
} }
message UpdateCustomLoginPolicyResponse { message UpdateCustomLoginPolicyResponse {

View File

@ -1,6 +1,7 @@
syntax = "proto3"; syntax = "proto3";
import "zitadel/object.proto"; import "zitadel/object.proto";
import "google/protobuf/duration.proto";
import "protoc-gen-openapiv2/options/annotations.proto"; import "protoc-gen-openapiv2/options/annotations.proto";
package zitadel.policy.v1; package zitadel.policy.v1;
@ -128,6 +129,12 @@ message LoginPolicy {
description: "defines if password reset link should be shown in the login screen" description: "defines if password reset link should be shown in the login screen"
} }
]; ];
google.protobuf.Duration password_check_lifetime = 9;
google.protobuf.Duration external_login_check_lifetime = 10;
google.protobuf.Duration mfa_init_skip_lifetime = 11;
google.protobuf.Duration second_factor_check_lifetime = 12;
google.protobuf.Duration multi_factor_check_lifetime = 13;
} }
enum SecondFactorType { enum SecondFactorType {