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 /> |
| 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 | - | |
| passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> |
| 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 /> |
| 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 | - | |
| passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> |
| 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 | - | |
| passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> |
| 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 | - | |
| is_default | 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 {
return &domain.LoginPolicy{
AllowUsernamePassword: p.AllowUsernamePassword,
AllowRegister: p.AllowRegister,
AllowExternalIDP: p.AllowExternalIdp,
ForceMFA: p.ForceMfa,
PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType),
HidePasswordReset: p.HidePasswordReset,
AllowUsernamePassword: p.AllowUsernamePassword,
AllowRegister: p.AllowRegister,
AllowExternalIDP: p.AllowExternalIdp,
ForceMFA: p.ForceMfa,
PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType),
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 {
return &domain.LoginPolicy{
AllowUsernamePassword: p.AllowUsernamePassword,
AllowRegister: p.AllowRegister,
AllowExternalIDP: p.AllowExternalIdp,
ForceMFA: p.ForceMfa,
PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType),
HidePasswordReset: p.HidePasswordReset,
AllowUsernamePassword: p.AllowUsernamePassword,
AllowRegister: p.AllowRegister,
AllowExternalIDP: p.AllowExternalIdp,
ForceMFA: p.ForceMfa,
PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType),
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 {
return &domain.LoginPolicy{
AllowUsernamePassword: p.AllowUsernamePassword,
AllowRegister: p.AllowRegister,
AllowExternalIDP: p.AllowExternalIdp,
ForceMFA: p.ForceMfa,
PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType),
HidePasswordReset: p.HidePasswordReset,
AllowUsernamePassword: p.AllowUsernamePassword,
AllowRegister: p.AllowRegister,
AllowExternalIDP: p.AllowExternalIdp,
ForceMFA: p.ForceMfa,
PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType),
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/pkg/grpc/object"
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"
)
func ModelLoginPolicyToPb(policy *query.LoginPolicy) *policy_pb.LoginPolicy {
return &policy_pb.LoginPolicy{
IsDefault: policy.IsDefault,
AllowUsernamePassword: policy.AllowUsernamePassword,
AllowRegister: policy.AllowRegister,
AllowExternalIdp: policy.AllowExternalIDPs,
ForceMfa: policy.ForceMFA,
PasswordlessType: ModelPasswordlessTypeToPb(policy.PasswordlessType),
HidePasswordReset: policy.HidePasswordReset,
IsDefault: policy.IsDefault,
AllowUsernamePassword: policy.AllowUsernamePassword,
AllowRegister: policy.AllowRegister,
AllowExternalIdp: policy.AllowExternalIDPs,
ForceMfa: policy.ForceMFA,
PasswordlessType: ModelPasswordlessTypeToPb(policy.PasswordlessType),
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{
Sequence: policy.Sequence,
CreationDate: timestamp_pb.New(policy.CreationDate),

View File

@ -50,12 +50,6 @@ type AuthRequestRepo struct {
ApplicationProvider applicationProvider
IdGenerator id.Generator
PasswordCheckLifeTime time.Duration
ExternalLoginCheckLifeTime time.Duration
MFAInitSkippedLifeTime time.Duration
SecondFactorCheckLifeTime time.Duration
MultiFactorCheckLifeTime time.Duration
}
type labelPolicyProvider interface {
@ -761,7 +755,7 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *domain.Auth
}
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
if selectedIDPConfigID == "" {
selectedIDPConfigID = userSession.SelectedIDPConfigID
@ -858,7 +852,7 @@ func (repo *AuthRequestRepo) firstFactorChecked(request *domain.AuthRequest, use
var step domain.NextStep
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
return nil
}
@ -875,7 +869,7 @@ func (repo *AuthRequestRepo) firstFactorChecked(request *domain.AuthRequest, use
return &domain.InitPasswordStep{}
}
if checkVerificationTimeMaxAge(userSession.PasswordVerification, repo.PasswordCheckLifeTime, request) {
if checkVerificationTimeMaxAge(userSession.PasswordVerification, request.LoginPolicy.PasswordCheckLifetime, request) {
request.PasswordVerified = true
request.AuthTime = userSession.PasswordVerification
return nil
@ -890,7 +884,7 @@ func (repo *AuthRequestRepo) mfaChecked(userSession *user_model.UserSessionView,
mfaLevel := request.MFALevel()
allowedProviders, required := user.MFATypesAllowed(mfaLevel, request.LoginPolicy)
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)
if promptRequired && len(types) == 0 {
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
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.AuthTime = userSession.SecondFactorVerification
return nil, true, nil
}
fallthrough
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.AuthTime = userSession.MultiFactorVerification
return nil, true, nil
@ -930,11 +924,11 @@ func (repo *AuthRequestRepo) mfaChecked(userSession *user_model.UserSessionView,
}, 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 {
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) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,6 +7,7 @@ import (
"fmt"
"regexp"
"testing"
"time"
"github.com/caos/zitadel/internal/domain"
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.passwordless_type,`+
` 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`),
nil,
nil,
@ -72,7 +78,12 @@ func Test_LoginPolicyPrepares(t *testing.T) {
` zitadel.projections.login_policies.multi_factors,`+
` zitadel.projections.login_policies.passwordless_type,`+
` 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`),
[]string{
"aggregate_id",
@ -88,6 +99,11 @@ func Test_LoginPolicyPrepares(t *testing.T) {
"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",
},
[]driver.Value{
"ro",
@ -103,23 +119,33 @@ func Test_LoginPolicyPrepares(t *testing.T) {
domain.PasswordlessTypeAllowed,
true,
true,
time.Hour * 2,
time.Hour * 2,
time.Hour * 2,
time.Hour * 2,
time.Hour * 2,
},
),
},
object: &LoginPolicy{
OrgID: "ro",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211109,
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDPs: true,
ForceMFA: true,
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
MultiFactors: []domain.MultiFactorType{domain.MultiFactorTypeU2FWithPIN},
PasswordlessType: domain.PasswordlessTypeAllowed,
IsDefault: true,
HidePasswordReset: true,
OrgID: "ro",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211109,
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDPs: true,
ForceMFA: true,
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
MultiFactors: []domain.MultiFactorType{domain.MultiFactorTypeU2FWithPIN},
PasswordlessType: domain.PasswordlessTypeAllowed,
IsDefault: 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.passwordless_type,`+
` 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`),
sql.ErrConnDone,
),

View File

@ -111,6 +111,11 @@ const (
LoginPolicyPasswordlessTypeCol = "passwordless_type"
LoginPolicyIsDefaultCol = "is_default"
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) {
@ -140,6 +145,11 @@ func (p *LoginPolicyProjection) reduceLoginPolicyAdded(event eventstore.Event) (
handler.NewCol(LoginPolicyPasswordlessTypeCol, policyEvent.PasswordlessType),
handler.NewCol(LoginPolicyIsDefaultCol, isDefault),
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
}
@ -177,6 +187,22 @@ func (p *LoginPolicyProjection) reduceLoginPolicyChanged(event eventstore.Event)
if policyEvent.HidePasswordReset != nil {
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(
&policyEvent,
cols,

View File

@ -2,6 +2,7 @@ package projection
import (
"testing"
"time"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
@ -29,13 +30,18 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
repository.EventType(org.LoginPolicyAddedEventType),
org.AggregateType,
[]byte(`{
"allowUsernamePassword": true,
"allowRegister": true,
"allowExternalIdp": false,
"forceMFA": false,
"hidePasswordReset": true,
"passwordlessType": 1
}`),
"allowUsernamePassword": true,
"allowRegister": true,
"allowExternalIdp": false,
"forceMFA": false,
"hidePasswordReset": true,
"passwordlessType": 1,
"passwordCheckLifetime": 10000000,
"externalLoginCheckLifetime": 10000000,
"mfaInitSkipLifetime": 10000000,
"secondFactorCheckLifetime": 10000000,
"multiFactorCheckLifetime": 10000000
}`),
), org.LoginPolicyAddedEventMapper),
},
reduce: (&LoginPolicyProjection{}).reduceLoginPolicyAdded,
@ -47,7 +53,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{
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{}{
"agg-id",
anyArg{},
@ -60,6 +66,11 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
domain.PasswordlessTypeAllowed,
false,
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),
org.AggregateType,
[]byte(`{
"allowUsernamePassword": true,
"allowRegister": true,
"allowExternalIdp": true,
"forceMFA": true,
"hidePasswordReset": true,
"passwordlessType": 1
}`),
"allowUsernamePassword": true,
"allowRegister": true,
"allowExternalIdp": true,
"forceMFA": true,
"hidePasswordReset": true,
"passwordlessType": 1,
"passwordCheckLifetime": 10000000,
"externalLoginCheckLifetime": 10000000,
"mfaInitSkipLifetime": 10000000,
"secondFactorCheckLifetime": 10000000,
"multiFactorCheckLifetime": 10000000
}`),
), org.LoginPolicyChangedEventMapper),
},
want: wantReduce{
@ -91,7 +107,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{
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{}{
anyArg{},
uint64(15),
@ -101,6 +117,11 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
true,
domain.PasswordlessTypeAllowed,
true,
time.Millisecond * 10,
time.Millisecond * 10,
time.Millisecond * 10,
time.Millisecond * 10,
time.Millisecond * 10,
"agg-id",
},
},
@ -271,12 +292,17 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
repository.EventType(iam.LoginPolicyAddedEventType),
iam.AggregateType,
[]byte(`{
"allowUsernamePassword": true,
"allowRegister": true,
"allowExternalIdp": false,
"forceMFA": false,
"hidePasswordReset": true,
"passwordlessType": 1
"allowUsernamePassword": true,
"allowRegister": true,
"allowExternalIdp": false,
"forceMFA": false,
"hidePasswordReset": true,
"passwordlessType": 1,
"passwordCheckLifetime": 10000000,
"externalLoginCheckLifetime": 10000000,
"mfaInitSkipLifetime": 10000000,
"secondFactorCheckLifetime": 10000000,
"multiFactorCheckLifetime": 10000000
}`),
), iam.LoginPolicyAddedEventMapper),
},
@ -288,7 +314,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{
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{}{
"agg-id",
anyArg{},
@ -301,6 +327,11 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
domain.PasswordlessTypeAllowed,
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 (
"context"
"time"
"github.com/caos/zitadel/internal/eventstore"
@ -28,6 +29,11 @@ func NewLoginPolicyAddedEvent(
forceMFA,
hidePasswordReset bool,
passwordlessType domain.PasswordlessType,
passwordCheckLifetime,
externalLoginCheckLifetime,
mfaInitSkipLifetime,
secondFactorCheckLifetime,
multiFactorCheckLifetime time.Duration,
) *LoginPolicyAddedEvent {
return &LoginPolicyAddedEvent{
LoginPolicyAddedEvent: *policy.NewLoginPolicyAddedEvent(
@ -40,7 +46,12 @@ func NewLoginPolicyAddedEvent(
allowExternalIDP,
forceMFA,
hidePasswordReset,
passwordlessType),
passwordlessType,
passwordCheckLifetime,
externalLoginCheckLifetime,
mfaInitSkipLifetime,
secondFactorCheckLifetime,
multiFactorCheckLifetime),
}
}

View File

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

View File

@ -2,6 +2,7 @@ package policy
import (
"encoding/json"
"time"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
@ -19,12 +20,17 @@ const (
type LoginPolicyAddedEvent struct {
eventstore.BaseEvent `json:"-"`
AllowUserNamePassword bool `json:"allowUsernamePassword,omitempty"`
AllowRegister bool `json:"allowRegister,omitempty"`
AllowExternalIDP bool `json:"allowExternalIdp,omitempty"`
ForceMFA bool `json:"forceMFA,omitempty"`
HidePasswordReset bool `json:"hidePasswordReset,omitempty"`
PasswordlessType domain.PasswordlessType `json:"passwordlessType,omitempty"`
AllowUserNamePassword bool `json:"allowUsernamePassword,omitempty"`
AllowRegister bool `json:"allowRegister,omitempty"`
AllowExternalIDP bool `json:"allowExternalIdp,omitempty"`
ForceMFA bool `json:"forceMFA,omitempty"`
HidePasswordReset bool `json:"hidePasswordReset,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{} {
@ -43,15 +49,25 @@ func NewLoginPolicyAddedEvent(
forceMFA,
hidePasswordReset bool,
passwordlessType domain.PasswordlessType,
passwordCheckLifetime,
externalLoginCheckLifetime,
mfaInitSkipLifetime,
secondFactorCheckLifetime,
multiFactorCheckLifetime time.Duration,
) *LoginPolicyAddedEvent {
return &LoginPolicyAddedEvent{
BaseEvent: *base,
AllowExternalIDP: allowExternalIDP,
AllowRegister: allowRegister,
AllowUserNamePassword: allowUserNamePassword,
ForceMFA: forceMFA,
PasswordlessType: passwordlessType,
HidePasswordReset: hidePasswordReset,
BaseEvent: *base,
AllowExternalIDP: allowExternalIDP,
AllowRegister: allowRegister,
AllowUserNamePassword: allowUserNamePassword,
ForceMFA: forceMFA,
PasswordlessType: passwordlessType,
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 {
eventstore.BaseEvent `json:"-"`
AllowUserNamePassword *bool `json:"allowUsernamePassword,omitempty"`
AllowRegister *bool `json:"allowRegister,omitempty"`
AllowExternalIDP *bool `json:"allowExternalIdp,omitempty"`
ForceMFA *bool `json:"forceMFA,omitempty"`
HidePasswordReset *bool `json:"hidePasswordReset,omitempty"`
PasswordlessType *domain.PasswordlessType `json:"passwordlessType,omitempty"`
AllowUserNamePassword *bool `json:"allowUsernamePassword,omitempty"`
AllowRegister *bool `json:"allowRegister,omitempty"`
AllowExternalIDP *bool `json:"allowExternalIdp,omitempty"`
ForceMFA *bool `json:"forceMFA,omitempty"`
HidePasswordReset *bool `json:"hidePasswordReset,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{} {
@ -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) {
e := &LoginPolicyChangedEvent{
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"
}
];
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 {

View File

@ -4397,6 +4397,11 @@ message AddCustomLoginPolicyRequest {
bool force_mfa = 4;
zitadel.policy.v1.PasswordlessType passwordless_type = 5 [(validate.rules).enum = {defined_only: true}];
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 {
@ -4410,6 +4415,11 @@ message UpdateCustomLoginPolicyRequest {
bool force_mfa = 4;
zitadel.policy.v1.PasswordlessType passwordless_type = 5 [(validate.rules).enum = {defined_only: true}];
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 {

View File

@ -1,6 +1,7 @@
syntax = "proto3";
import "zitadel/object.proto";
import "google/protobuf/duration.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
package zitadel.policy.v1;
@ -128,6 +129,12 @@ message LoginPolicy {
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 {