diff --git a/docs/docs/apis/proto/admin.md b/docs/docs/apis/proto/admin.md
index b6bd61d5f9..36539a2558 100644
--- a/docs/docs/apis/proto/admin.md
+++ b/docs/docs/apis/proto/admin.md
@@ -1514,7 +1514,7 @@ This is an empty request
| ----- | ---- | ----------- | ----------- |
| sid | string | - | string.min_len: 1
string.max_len: 200
|
| token | string | - | string.min_len: 1
string.max_len: 200
|
-| from | string | - | string.min_len: 1
string.max_len: 200
|
+| sender_number | string | - | string.min_len: 1
string.max_len: 200
|
@@ -3593,6 +3593,11 @@ This is an empty request
| force_mfa | bool | - | |
| passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true
|
| 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
string.max_len: 200
|
| sid | string | - | string.min_len: 1
string.max_len: 200
|
-| from | string | - | string.min_len: 1
string.max_len: 200
|
+| sender_number | string | - | string.min_len: 1
string.max_len: 200
|
diff --git a/docs/docs/apis/proto/management.md b/docs/docs/apis/proto/management.md
index 502ecf3d7a..bd5484afe6 100644
--- a/docs/docs/apis/proto/management.md
+++ b/docs/docs/apis/proto/management.md
@@ -3036,6 +3036,11 @@ This is an empty request
| force_mfa | bool | - | |
| passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true
|
| 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
|
| 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 | - | |
diff --git a/docs/docs/apis/proto/policy.md b/docs/docs/apis/proto/policy.md
index 37961ca41b..cd482b7ff6 100644
--- a/docs/docs/apis/proto/policy.md
+++ b/docs/docs/apis/proto/policy.md
@@ -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 | - | |
diff --git a/internal/api/grpc/admin/login_policy_converter.go b/internal/api/grpc/admin/login_policy_converter.go
index 900e88dbdb..3f6c26215a 100644
--- a/internal/api/grpc/admin/login_policy_converter.go
+++ b/internal/api/grpc/admin/login_policy_converter.go
@@ -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(),
}
}
diff --git a/internal/api/grpc/management/policy_login_converter.go b/internal/api/grpc/management/policy_login_converter.go
index 706a0f941a..1a393cbbf9 100644
--- a/internal/api/grpc/management/policy_login_converter.go
+++ b/internal/api/grpc/management/policy_login_converter.go
@@ -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(),
}
}
diff --git a/internal/api/grpc/policy/login_policy.go b/internal/api/grpc/policy/login_policy.go
index f40a090dba..fb1f456604 100644
--- a/internal/api/grpc/policy/login_policy.go
+++ b/internal/api/grpc/policy/login_policy.go
@@ -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),
diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go
index 0aedc1f26f..82b1099160 100644
--- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go
+++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go
@@ -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) {
diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go
index 2659f77bcd..315dbcf727 100644
--- a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go
+++ b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go
@@ -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)
}
})
diff --git a/internal/auth/repository/eventsourcing/repository.go b/internal/auth/repository/eventsourcing/repository.go
index d3c635d9b3..3ebb4d1b98 100644
--- a/internal/auth/repository/eventsourcing/repository.go
+++ b/internal/auth/repository/eventsourcing/repository.go
@@ -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,
diff --git a/internal/command/iam_converter.go b/internal/command/iam_converter.go
index 9d03c7bc2b..e9bab1285e 100644
--- a/internal/command/iam_converter.go
+++ b/internal/command/iam_converter.go
@@ -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,
}
}
diff --git a/internal/command/iam_policy_login.go b/internal/command/iam_policy_login.go
index 97f0f1442b..add92ef0f1 100644
--- a/internal/command/iam_policy_login.go
+++ b/internal/command/iam_policy_login.go
@@ -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")
}
diff --git a/internal/command/iam_policy_login_model.go b/internal/command/iam_policy_login_model.go
index aebb0f11a5..ac32f7fbc3 100644
--- a/internal/command/iam_policy_login_model.go
+++ b/internal/command/iam_policy_login_model.go
@@ -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
}
diff --git a/internal/command/iam_policy_login_test.go b/internal/command/iam_policy_login_test.go
index 4a5511065c..5be9c8c626 100644
--- a/internal/command/iam_policy_login_test.go
+++ b/internal/command/iam_policy_login_test.go
@@ -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
diff --git a/internal/command/org_features.go b/internal/command/org_features.go
index 9dc00b0a49..78f0252f30 100644
--- a/internal/command/org_features.go
+++ b/internal/command/org_features.go
@@ -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)
}
diff --git a/internal/command/org_features_test.go b/internal/command/org_features_test.go
index 1d0352998e..917fdfde52 100644
--- a/internal/command/org_features_test.go
+++ b/internal/command/org_features_test.go
@@ -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,
),
),
),
diff --git a/internal/command/org_policy_login.go b/internal/command/org_policy_login.go
index 02dd6edd71..e2be5f94cb 100644
--- a/internal/command/org_policy_login.go
+++ b/internal/command/org_policy_login.go
@@ -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")
diff --git a/internal/command/org_policy_login_model.go b/internal/command/org_policy_login_model.go
index 12b8734829..8cfa5df15e 100644
--- a/internal/command/org_policy_login_model.go
+++ b/internal/command/org_policy_login_model.go
@@ -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))
}
diff --git a/internal/command/org_policy_login_test.go b/internal/command/org_policy_login_test.go
index e01af8d3dd..f327d72652 100644
--- a/internal/command/org_policy_login_test.go
+++ b/internal/command/org_policy_login_test.go
@@ -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
}
diff --git a/internal/command/policy_login_model.go b/internal/command/policy_login_model.go
index 3b3a8bfcfe..ed3a2faefb 100644
--- a/internal/command/policy_login_model.go
+++ b/internal/command/policy_login_model.go
@@ -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
}
diff --git a/internal/command/user_human_password_test.go b/internal/command/user_human_password_test.go
index fa95a9c3ec..41b75e935c 100644
--- a/internal/command/user_human_password_test.go
+++ b/internal/command/user_human_password_test.go
@@ -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,
),
),
),
diff --git a/internal/command/user_human_test.go b/internal/command/user_human_test.go
index f84a0c8a3a..2d1ac193e2 100644
--- a/internal/command/user_human_test.go
+++ b/internal/command/user_human_test.go
@@ -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,
),
),
),
diff --git a/internal/config/systemdefaults/system_defaults.go b/internal/config/systemdefaults/system_defaults.go
index d6ba150f07..5e598f4901 100644
--- a/internal/config/systemdefaults/system_defaults.go
+++ b/internal/config/systemdefaults/system_defaults.go
@@ -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
diff --git a/internal/domain/policy_login.go b/internal/domain/policy_login.go
index 0ad43afb0d..db2ad78e7e 100644
--- a/internal/domain/policy_login.go
+++ b/internal/domain/policy_login.go
@@ -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 {
diff --git a/internal/query/login_policy.go b/internal/query/login_policy.go
index 75d78dc580..55ec82d36f 100644
--- a/internal/query/login_policy.go
+++ b/internal/query/login_policy.go
@@ -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) {
diff --git a/internal/query/login_policy_test.go b/internal/query/login_policy_test.go
index 24c76abfb0..ade38bdc17 100644
--- a/internal/query/login_policy_test.go
+++ b/internal/query/login_policy_test.go
@@ -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,
),
diff --git a/internal/query/projection/login_policy.go b/internal/query/projection/login_policy.go
index 19acde3a64..5f2d8fa5a7 100644
--- a/internal/query/projection/login_policy.go
+++ b/internal/query/projection/login_policy.go
@@ -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,
diff --git a/internal/query/projection/login_policy_test.go b/internal/query/projection/login_policy_test.go
index e9ff4633a7..928766521a 100644
--- a/internal/query/projection/login_policy_test.go
+++ b/internal/query/projection/login_policy_test.go
@@ -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,
},
},
},
diff --git a/internal/repository/iam/policy_login.go b/internal/repository/iam/policy_login.go
index 330edf48cc..22c0def52d 100644
--- a/internal/repository/iam/policy_login.go
+++ b/internal/repository/iam/policy_login.go
@@ -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),
}
}
diff --git a/internal/repository/org/policy_login.go b/internal/repository/org/policy_login.go
index 9b6fa0f9ac..dd915fd1b7 100644
--- a/internal/repository/org/policy_login.go
+++ b/internal/repository/org/policy_login.go
@@ -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),
}
}
diff --git a/internal/repository/policy/login.go b/internal/repository/policy/login.go
index d089540b18..4161c7ad9e 100644
--- a/internal/repository/policy/login.go
+++ b/internal/repository/policy/login.go
@@ -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),
diff --git a/proto/zitadel/admin.proto b/proto/zitadel/admin.proto
index 0e3dc4cd36..1a5a3d9024 100644
--- a/proto/zitadel/admin.proto
+++ b/proto/zitadel/admin.proto
@@ -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 {
diff --git a/proto/zitadel/management.proto b/proto/zitadel/management.proto
index 0e83dc66d6..3be4a3ed61 100644
--- a/proto/zitadel/management.proto
+++ b/proto/zitadel/management.proto
@@ -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 {
diff --git a/proto/zitadel/policy.proto b/proto/zitadel/policy.proto
index 227214c763..b4f9f75ff5 100644
--- a/proto/zitadel/policy.proto
+++ b/proto/zitadel/policy.proto
@@ -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 {