feat: add hide password reset to login policy (#1806)

* feat: add hide password reset to login policy

* feat: tests

* feat: hide password reset in login

* feat: hide password reset to frontend

* feat: hide password reset to frontend

* feat: hide password reset to frontend

* feat: check feature

* feat: feature in frontend
This commit is contained in:
Fabi
2021-06-03 11:53:30 +02:00
committed by GitHub
parent 3dba12c0d4
commit 8d163163f1
45 changed files with 246 additions and 23 deletions

View File

@@ -66,6 +66,15 @@
</mat-slide-toggle> </mat-slide-toggle>
</div> </div>
<div class="row">
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYPASSWORDRESET' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
[(ngModel)]="features.loginPolicyPasswordReset"
[disabled]="(['iam.features.write'] | hasRole | async) == false">
</mat-slide-toggle>
</div>
<div class="row"> <div class="row">
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYREGISTRATION' | translate}}</span> <span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYREGISTRATION' | translate}}</span>
<span class="fill-space"></span> <span class="fill-space"></span>
@@ -132,4 +141,4 @@
type="submit" mat-raised-button>{{ 'ACTIONS.SAVE' | translate type="submit" mat-raised-button>{{ 'ACTIONS.SAVE' | translate
}}</button> }}</button>
</div> </div>
</app-detail-layout> </app-detail-layout>

View File

@@ -152,6 +152,7 @@ export class FeaturesComponent implements OnDestroy {
req.setOrgId(this.org.id); req.setOrgId(this.org.id);
req.setLoginPolicyUsernameLogin(this.features.loginPolicyUsernameLogin); req.setLoginPolicyUsernameLogin(this.features.loginPolicyUsernameLogin);
req.setLoginPolicyPasswordReset(this.features.loginPolicyPasswordReset);
req.setLoginPolicyRegistration(this.features.loginPolicyRegistration); req.setLoginPolicyRegistration(this.features.loginPolicyRegistration);
req.setLoginPolicyIdp(this.features.loginPolicyIdp); req.setLoginPolicyIdp(this.features.loginPolicyIdp);
req.setLoginPolicyFactors(this.features.loginPolicyFactors); req.setLoginPolicyFactors(this.features.loginPolicyFactors);
@@ -170,6 +171,7 @@ export class FeaturesComponent implements OnDestroy {
// update Default org iam policy? // update Default org iam policy?
const dreq = new SetDefaultFeaturesRequest(); const dreq = new SetDefaultFeaturesRequest();
dreq.setLoginPolicyUsernameLogin(this.features.loginPolicyUsernameLogin); dreq.setLoginPolicyUsernameLogin(this.features.loginPolicyUsernameLogin);
dreq.setLoginPolicyPasswordReset(this.features.loginPolicyPasswordReset);
dreq.setLoginPolicyRegistration(this.features.loginPolicyRegistration); dreq.setLoginPolicyRegistration(this.features.loginPolicyRegistration);
dreq.setLoginPolicyIdp(this.features.loginPolicyIdp); dreq.setLoginPolicyIdp(this.features.loginPolicyIdp);
dreq.setLoginPolicyFactors(this.features.loginPolicyFactors); dreq.setLoginPolicyFactors(this.features.loginPolicyFactors);

View File

@@ -95,6 +95,26 @@
</cnsl-info-section> </cnsl-info-section>
</ng-template> </ng-template>
</div> </div>
<div class="row">
<mat-slide-toggle class="toggle" color="primary" [disabled]="disabled" ngDefaultControl
[(ngModel)]="loginData.hidePasswordReset">
{{'POLICY.DATA.HIDEPASSWORDRESET' | translate}}
</mat-slide-toggle>
<ng-container
*ngIf="serviceType == PolicyComponentServiceType.MGMT && (['login_policy.password_reset'] | hasFeature | async) == false; else passwordResetInfo">
<cnsl-info-section type="WARN">{{'FEATURES.NOTAVAILABLE' | translate: ({value:
'login_policy.hide_password_reset'})}}
</cnsl-info-section>
</ng-container>
<ng-template #passwordResetInfo>
<cnsl-info-section class="info">
{{'POLICY.DATA.HIDEPASSWORDRESET_DESC' | translate}}
</cnsl-info-section>
</ng-template>
</div>
<div class="row"> <div class="row">
<cnsl-form-field class="form-field" label="Access Code" required="true"> <cnsl-form-field class="form-field" label="Access Code" required="true">
@@ -201,4 +221,4 @@
<cnsl-links *ngIf="nextLinks" [links]="nextLinks"></cnsl-links> <cnsl-links *ngIf="nextLinks" [links]="nextLinks"></cnsl-links>
</app-detail-layout> </app-detail-layout>

View File

@@ -143,6 +143,7 @@ export class LoginPolicyComponent implements OnDestroy {
mgmtreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword); mgmtreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword);
mgmtreq.setForceMfa(this.loginData.forceMfa); mgmtreq.setForceMfa(this.loginData.forceMfa);
mgmtreq.setPasswordlessType(this.loginData.passwordlessType); mgmtreq.setPasswordlessType(this.loginData.passwordlessType);
mgmtreq.setHidePasswordReset(this.loginData.hidePasswordReset);
if ((this.loginData as LoginPolicy.AsObject).isDefault) { if ((this.loginData as LoginPolicy.AsObject).isDefault) {
return (this.service as ManagementService).addCustomLoginPolicy(mgmtreq); return (this.service as ManagementService).addCustomLoginPolicy(mgmtreq);
} else { } else {
@@ -155,6 +156,7 @@ export class LoginPolicyComponent implements OnDestroy {
adminreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword); adminreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword);
adminreq.setForceMfa(this.loginData.forceMfa); adminreq.setForceMfa(this.loginData.forceMfa);
adminreq.setPasswordlessType(this.loginData.passwordlessType); adminreq.setPasswordlessType(this.loginData.passwordlessType);
adminreq.setHidePasswordReset(this.loginData.hidePasswordReset);
return (this.service as AdminService).updateLoginPolicy(adminreq); return (this.service as AdminService).updateLoginPolicy(adminreq);
} }

View File

@@ -607,6 +607,7 @@
"DATA": { "DATA": {
"AUDITLOGRETENTION": "Audit Log Retention", "AUDITLOGRETENTION": "Audit Log Retention",
"LOGINPOLICYUSERNAMELOGIN": "Login Richtlinie: Login mit Username erlauben - benutzerdefiniert", "LOGINPOLICYUSERNAMELOGIN": "Login Richtlinie: Login mit Username erlauben - benutzerdefiniert",
"LOGINPOLICYPASSWORDRESET": "Login Richtlinie: Passwort vergessen Link nicht anzeigen - benutzerdefiniert",
"LOGINPOLICYREGISTRATION": "Login Richtlinie: Registration erlauben - benutzerdefiniert", "LOGINPOLICYREGISTRATION": "Login Richtlinie: Registration erlauben - benutzerdefiniert",
"LOGINPOLICYIDP": "Login Richtlinie: Identity Providers - benutzerdefiniert", "LOGINPOLICYIDP": "Login Richtlinie: Identity Providers - benutzerdefiniert",
"LOGINPOLICYFACTORS": "Login Richtlinie: Mltifaktoren - benutzerdefiniert", "LOGINPOLICYFACTORS": "Login Richtlinie: Mltifaktoren - benutzerdefiniert",
@@ -683,7 +684,9 @@
"ALLOWEXTERNALIDP_DESC": "Der Login wird für die darunter liegenden Identity Provider erlaubt.", "ALLOWEXTERNALIDP_DESC": "Der Login wird für die darunter liegenden Identity Provider erlaubt.",
"ALLOWREGISTER_DESC": "Ist die Option gewählt, erscheint im Login ein zusätzlicher Schritt zum Registrieren eines Benutzers.", "ALLOWREGISTER_DESC": "Ist die Option gewählt, erscheint im Login ein zusätzlicher Schritt zum Registrieren eines Benutzers.",
"FORCEMFA": "Mfa erzwingen", "FORCEMFA": "Mfa erzwingen",
"FORCEMFA_DESC": "Ist die Option gewählt, müssen Benutzer einen zweiten Faktor für den Login verwenden." "FORCEMFA_DESC": "Ist die Option gewählt, müssen Benutzer einen zweiten Faktor für den Login verwenden.",
"HIDEPASSWORDRESET": "Passwort vergessen, nicht anzeigen",
"FORCEMFA_DESC": "Ist die Option gewählt, ist es nicht möglich im Login das Passwort zurück zusetzen via Passwort vergessen Link."
}, },
"RESET": "Richtlinie zurücksetzen", "RESET": "Richtlinie zurücksetzen",
"CREATECUSTOM": "Benutzerdefinierte Richtlinie erstellen", "CREATECUSTOM": "Benutzerdefinierte Richtlinie erstellen",

View File

@@ -607,6 +607,7 @@
"DATA": { "DATA": {
"AUDITLOGRETENTION": "Audit Log Retention", "AUDITLOGRETENTION": "Audit Log Retention",
"LOGINPOLICYUSERNAMELOGIN": "Login Policy: Allow login with Username - custom", "LOGINPOLICYUSERNAMELOGIN": "Login Policy: Allow login with Username - custom",
"LOGINPOLICYPASSWORDRESET": "Login Policy: Hide reset password link - custom",
"LOGINPOLICYREGISTRATION": "Login Policy: Allow self registration - custom", "LOGINPOLICYREGISTRATION": "Login Policy: Allow self registration - custom",
"LOGINPOLICYIDP": "Login Policy: Identity Provider - custom", "LOGINPOLICYIDP": "Login Policy: Identity Provider - custom",
"LOGINPOLICYFACTORS": "Login Policy: Multifactors - custom", "LOGINPOLICYFACTORS": "Login Policy: Multifactors - custom",
@@ -683,7 +684,9 @@
"ALLOWEXTERNALIDP_DESC": "The login is allowed for the underlying identity providers", "ALLOWEXTERNALIDP_DESC": "The login is allowed for the underlying identity providers",
"ALLOWREGISTER_DESC": "If the option is selected, an additional step for registering a user appears in the login.", "ALLOWREGISTER_DESC": "If the option is selected, an additional step for registering a user appears in the login.",
"FORCEMFA": "Force MFA", "FORCEMFA": "Force MFA",
"FORCEMFA_DESC": "If the option is selected, users have to configure a second factor for login." "FORCEMFA_DESC": "If the option is selected, users have to configure a second factor for login.",
"HIDEPASSWORDRESET": "Hide Password reset",
"FORCEMFA_DESC": "If the option is selected, the user can't reset his password in the login process."
}, },
"RESET": "Reset Policy", "RESET": "Reset Policy",
"CREATECUSTOM": "Create Custom Policy", "CREATECUSTOM": "Create Custom Policy",

View File

@@ -1421,6 +1421,7 @@ This is an empty response
| password_complexity_policy | bool | - | | | password_complexity_policy | bool | - | |
| label_policy | bool | - | | | label_policy | bool | - | |
| custom_domain | bool | - | | | custom_domain | bool | - | |
| login_policy_password_reset | bool | - | |
@@ -1456,6 +1457,7 @@ This is an empty response
| password_complexity_policy | bool | - | | | password_complexity_policy | bool | - | |
| label_policy | bool | - | | | label_policy | bool | - | |
| custom_domain | bool | - | | | custom_domain | bool | - | |
| login_policy_password_reset | bool | - | |
@@ -1696,6 +1698,7 @@ This is an empty response
| allow_external_idp | bool | - | | | allow_external_idp | bool | - | |
| force_mfa | bool | - | | | force_mfa | bool | - | |
| passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> | | passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> |
| hide_password_reset | bool | - | |

View File

@@ -1803,6 +1803,7 @@ Change OIDC identity provider configuration of the organisation
| allow_external_idp | bool | - | | | allow_external_idp | bool | - | |
| force_mfa | bool | - | | | force_mfa | bool | - | |
| passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> | | passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> |
| hide_password_reset | bool | - | |
@@ -4988,6 +4989,7 @@ This is an empty request
| allow_external_idp | bool | - | | | allow_external_idp | bool | - | |
| force_mfa | bool | - | | | force_mfa | bool | - | |
| passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> | | passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> |
| hide_password_reset | bool | - | |

View File

@@ -37,6 +37,7 @@ title: zitadel/policy.proto
| force_mfa | bool | - | | | force_mfa | bool | - | |
| passwordless_type | PasswordlessType | - | | | passwordless_type | PasswordlessType | - | |
| is_default | bool | - | | | is_default | bool | - | |
| hide_password_reset | bool | - | |

View File

@@ -69,6 +69,7 @@ func setDefaultFeaturesRequestToDomain(req *admin_pb.SetDefaultFeaturesRequest)
LoginPolicyPasswordless: req.LoginPolicyPasswordless, LoginPolicyPasswordless: req.LoginPolicyPasswordless,
LoginPolicyRegistration: req.LoginPolicyRegistration, LoginPolicyRegistration: req.LoginPolicyRegistration,
LoginPolicyUsernameLogin: req.LoginPolicyUsernameLogin, LoginPolicyUsernameLogin: req.LoginPolicyUsernameLogin,
LoginPolicyPasswordReset: req.LoginPolicyPasswordReset,
PasswordComplexityPolicy: req.PasswordComplexityPolicy, PasswordComplexityPolicy: req.PasswordComplexityPolicy,
LabelPolicy: req.LabelPolicy, LabelPolicy: req.LabelPolicy,
CustomDomain: req.CustomDomain, CustomDomain: req.CustomDomain,
@@ -87,6 +88,7 @@ func setOrgFeaturesRequestToDomain(req *admin_pb.SetOrgFeaturesRequest) *domain.
LoginPolicyPasswordless: req.LoginPolicyPasswordless, LoginPolicyPasswordless: req.LoginPolicyPasswordless,
LoginPolicyRegistration: req.LoginPolicyRegistration, LoginPolicyRegistration: req.LoginPolicyRegistration,
LoginPolicyUsernameLogin: req.LoginPolicyUsernameLogin, LoginPolicyUsernameLogin: req.LoginPolicyUsernameLogin,
LoginPolicyPasswordReset: req.LoginPolicyPasswordReset,
PasswordComplexityPolicy: req.PasswordComplexityPolicy, PasswordComplexityPolicy: req.PasswordComplexityPolicy,
LabelPolicy: req.LabelPolicy, LabelPolicy: req.LabelPolicy,
CustomDomain: req.CustomDomain, CustomDomain: req.CustomDomain,

View File

@@ -15,6 +15,7 @@ func updateLoginPolicyToDomain(p *admin_pb.UpdateLoginPolicyRequest) *domain.Log
AllowExternalIDP: p.AllowExternalIdp, AllowExternalIDP: p.AllowExternalIdp,
ForceMFA: p.ForceMfa, ForceMFA: p.ForceMfa,
PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType), PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType),
HidePasswordReset: p.HidePasswordReset,
} }
} }

View File

@@ -21,6 +21,7 @@ func FeaturesFromModel(features *features_model.FeaturesView) *features_pb.Featu
LoginPolicyPasswordless: features.LoginPolicyPasswordless, LoginPolicyPasswordless: features.LoginPolicyPasswordless,
LoginPolicyRegistration: features.LoginPolicyRegistration, LoginPolicyRegistration: features.LoginPolicyRegistration,
LoginPolicyUsernameLogin: features.LoginPolicyUsernameLogin, LoginPolicyUsernameLogin: features.LoginPolicyUsernameLogin,
LoginPolicyPasswordReset: features.LoginPolicyPasswordReset,
PasswordComplexityPolicy: features.PasswordComplexityPolicy, PasswordComplexityPolicy: features.PasswordComplexityPolicy,
LabelPolicy: features.LabelPolicy, LabelPolicy: features.LabelPolicy,
CustomDomain: features.CustomDomain, CustomDomain: features.CustomDomain,

View File

@@ -15,6 +15,7 @@ func addLoginPolicyToDomain(p *mgmt_pb.AddCustomLoginPolicyRequest) *domain.Logi
AllowExternalIDP: p.AllowExternalIdp, AllowExternalIDP: p.AllowExternalIdp,
ForceMFA: p.ForceMfa, ForceMFA: p.ForceMfa,
PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType), PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType),
HidePasswordReset: p.HidePasswordReset,
} }
} }
@@ -25,6 +26,7 @@ func updateLoginPolicyToDomain(p *mgmt_pb.UpdateCustomLoginPolicyRequest) *domai
AllowExternalIDP: p.AllowExternalIdp, AllowExternalIDP: p.AllowExternalIdp,
ForceMFA: p.ForceMfa, ForceMFA: p.ForceMfa,
PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType), PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType),
HidePasswordReset: p.HidePasswordReset,
} }
} }

View File

@@ -14,6 +14,7 @@ func ModelLoginPolicyToPb(policy *model.LoginPolicyView) *policy_pb.LoginPolicy
AllowExternalIdp: policy.AllowExternalIDP, AllowExternalIdp: policy.AllowExternalIDP,
ForceMfa: policy.ForceMFA, ForceMfa: policy.ForceMFA,
PasswordlessType: ModelPasswordlessTypeToPb(policy.PasswordlessType), PasswordlessType: ModelPasswordlessTypeToPb(policy.PasswordlessType),
HidePasswordReset: policy.HidePasswordReset,
} }
} }

View File

@@ -163,6 +163,10 @@ func checkLoginPolicyFeatures(features *features_view_model.FeaturesView, requir
if !features.LoginPolicyUsernameLogin { if !features.LoginPolicyUsernameLogin {
return MissingFeatureErr(requiredFeature) return MissingFeatureErr(requiredFeature)
} }
case domain.FeatureLoginPolicyPasswordReset:
if !features.LoginPolicyPasswordReset {
return MissingFeatureErr(requiredFeature)
}
default: default:
if !features.LoginPolicyFactors && !features.LoginPolicyIDP && !features.LoginPolicyPasswordless && !features.LoginPolicyRegistration && !features.LoginPolicyUsernameLogin { if !features.LoginPolicyFactors && !features.LoginPolicyIDP && !features.LoginPolicyPasswordless && !features.LoginPolicyRegistration && !features.LoginPolicyUsernameLogin {
return MissingFeatureErr(requiredFeature) return MissingFeatureErr(requiredFeature)

View File

@@ -21,6 +21,7 @@ type FeaturesWriteModel struct {
LoginPolicyPasswordless bool LoginPolicyPasswordless bool
LoginPolicyRegistration bool LoginPolicyRegistration bool
LoginPolicyUsernameLogin bool LoginPolicyUsernameLogin bool
LoginPolicyPasswordReset bool
PasswordComplexityPolicy bool PasswordComplexityPolicy bool
LabelPolicy bool LabelPolicy bool
CustomDomain bool CustomDomain bool
@@ -61,6 +62,9 @@ func (wm *FeaturesWriteModel) Reduce() error {
if e.LoginPolicyUsernameLogin != nil { if e.LoginPolicyUsernameLogin != nil {
wm.LoginPolicyUsernameLogin = *e.LoginPolicyUsernameLogin wm.LoginPolicyUsernameLogin = *e.LoginPolicyUsernameLogin
} }
if e.LoginPolicyPasswordReset != nil {
wm.LoginPolicyPasswordReset = *e.LoginPolicyPasswordReset
}
if e.PasswordComplexityPolicy != nil { if e.PasswordComplexityPolicy != nil {
wm.PasswordComplexityPolicy = *e.PasswordComplexityPolicy wm.PasswordComplexityPolicy = *e.PasswordComplexityPolicy
} }

View File

@@ -39,6 +39,7 @@ func writeModelToLoginPolicy(wm *LoginPolicyWriteModel) *domain.LoginPolicy {
AllowUsernamePassword: wm.AllowUserNamePassword, AllowUsernamePassword: wm.AllowUserNamePassword,
AllowRegister: wm.AllowRegister, AllowRegister: wm.AllowRegister,
AllowExternalIDP: wm.AllowExternalIDP, AllowExternalIDP: wm.AllowExternalIDP,
HidePasswordReset: wm.HidePasswordReset,
ForceMFA: wm.ForceMFA, ForceMFA: wm.ForceMFA,
PasswordlessType: wm.PasswordlessType, PasswordlessType: wm.PasswordlessType,
} }

View File

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

View File

@@ -58,7 +58,8 @@ func (wm *IAMLoginPolicyWriteModel) NewChangedEvent(
allowUsernamePassword, allowUsernamePassword,
allowRegister, allowRegister,
allowExternalIDP, allowExternalIDP,
forceMFA bool, forceMFA,
hidePasswordReset bool,
passwordlessType domain.PasswordlessType, passwordlessType domain.PasswordlessType,
) (*iam.LoginPolicyChangedEvent, bool) { ) (*iam.LoginPolicyChangedEvent, bool) {
@@ -78,6 +79,9 @@ func (wm *IAMLoginPolicyWriteModel) NewChangedEvent(
if passwordlessType.Valid() && wm.PasswordlessType != passwordlessType { if passwordlessType.Valid() && wm.PasswordlessType != passwordlessType {
changes = append(changes, policy.ChangePasswordlessType(passwordlessType)) changes = append(changes, policy.ChangePasswordlessType(passwordlessType))
} }
if wm.HidePasswordReset != hidePasswordReset {
changes = append(changes, policy.ChangeHidePasswordReset(hidePasswordReset))
}
if len(changes) == 0 { if len(changes) == 0 {
return nil, false return nil, false
} }

View File

@@ -2,6 +2,8 @@ package command
import ( import (
"context" "context"
"testing"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore"
@@ -12,7 +14,6 @@ import (
"github.com/caos/zitadel/internal/repository/user" "github.com/caos/zitadel/internal/repository/user"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"testing"
) )
func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) { func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) {
@@ -46,6 +47,7 @@ func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) {
true, true,
false, false,
false, false,
false,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -79,6 +81,7 @@ func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -93,6 +96,7 @@ func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) {
AllowUsernamePassword: true, AllowUsernamePassword: true,
AllowExternalIDP: true, AllowExternalIDP: true,
ForceMFA: true, ForceMFA: true,
HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed, PasswordlessType: domain.PasswordlessTypeAllowed,
}, },
}, },
@@ -106,6 +110,7 @@ func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) {
AllowUsernamePassword: true, AllowUsernamePassword: true,
AllowExternalIDP: true, AllowExternalIDP: true,
ForceMFA: true, ForceMFA: true,
HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed, PasswordlessType: domain.PasswordlessTypeAllowed,
}, },
}, },
@@ -180,6 +185,7 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -193,6 +199,7 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
AllowUsernamePassword: true, AllowUsernamePassword: true,
AllowExternalIDP: true, AllowExternalIDP: true,
ForceMFA: true, ForceMFA: true,
HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed, PasswordlessType: domain.PasswordlessTypeAllowed,
}, },
}, },
@@ -213,6 +220,7 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -220,7 +228,7 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
expectPush( expectPush(
[]*repository.Event{ []*repository.Event{
eventFromEventPusher( eventFromEventPusher(
newDefaultLoginPolicyChangedEvent(context.Background(), false, false, false, false, domain.PasswordlessTypeNotAllowed), newDefaultLoginPolicyChangedEvent(context.Background(), false, false, false, false, false, domain.PasswordlessTypeNotAllowed),
), ),
}, },
), ),
@@ -233,6 +241,7 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
AllowUsernamePassword: false, AllowUsernamePassword: false,
AllowExternalIDP: false, AllowExternalIDP: false,
ForceMFA: false, ForceMFA: false,
HidePasswordReset: false,
PasswordlessType: domain.PasswordlessTypeNotAllowed, PasswordlessType: domain.PasswordlessTypeNotAllowed,
}, },
}, },
@@ -246,6 +255,7 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
AllowUsernamePassword: false, AllowUsernamePassword: false,
AllowExternalIDP: false, AllowExternalIDP: false,
ForceMFA: false, ForceMFA: false,
HidePasswordReset: false,
PasswordlessType: domain.PasswordlessTypeNotAllowed, PasswordlessType: domain.PasswordlessTypeNotAllowed,
}, },
}, },
@@ -345,6 +355,7 @@ func TestCommandSide_AddIDPProviderDefaultLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -496,6 +507,7 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -537,6 +549,7 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -583,6 +596,7 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -637,6 +651,7 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -1181,7 +1196,7 @@ func TestCommandSide_RemoveMultiFactorDefaultLoginPolicy(t *testing.T) {
} }
} }
func newDefaultLoginPolicyChangedEvent(ctx context.Context, allowRegister, allowUsernamePassword, allowExternalIDP, forceMFA bool, passwordlessType domain.PasswordlessType) *iam.LoginPolicyChangedEvent { func newDefaultLoginPolicyChangedEvent(ctx context.Context, allowRegister, allowUsernamePassword, allowExternalIDP, forceMFA, hidePasswordReset bool, passwordlessType domain.PasswordlessType) *iam.LoginPolicyChangedEvent {
event, _ := iam.NewLoginPolicyChangedEvent(ctx, event, _ := iam.NewLoginPolicyChangedEvent(ctx,
&iam.NewAggregate().Aggregate, &iam.NewAggregate().Aggregate,
[]policy.LoginPolicyChanges{ []policy.LoginPolicyChanges{
@@ -1189,6 +1204,7 @@ func newDefaultLoginPolicyChangedEvent(ctx context.Context, allowRegister, allow
policy.ChangeAllowExternalIDP(allowExternalIDP), policy.ChangeAllowExternalIDP(allowExternalIDP),
policy.ChangeForceMFA(forceMFA), policy.ChangeForceMFA(forceMFA),
policy.ChangeAllowUserNamePassword(allowUsernamePassword), policy.ChangeAllowUserNamePassword(allowUsernamePassword),
policy.ChangeHidePasswordReset(hidePasswordReset),
policy.ChangePasswordlessType(passwordlessType), policy.ChangePasswordlessType(passwordlessType),
}, },
) )

View File

@@ -31,6 +31,7 @@ func (c *Commands) SetOrgFeatures(ctx context.Context, resourceOwner string, fea
features.LoginPolicyPasswordless, features.LoginPolicyPasswordless,
features.LoginPolicyRegistration, features.LoginPolicyRegistration,
features.LoginPolicyUsernameLogin, features.LoginPolicyUsernameLogin,
features.LoginPolicyPasswordReset,
features.PasswordComplexityPolicy, features.PasswordComplexityPolicy,
features.LabelPolicy, features.LabelPolicy,
features.CustomDomain, features.CustomDomain,
@@ -165,7 +166,10 @@ func (c *Commands) setAllowedLoginPolicy(ctx context.Context, orgID string, feat
if !features.LoginPolicyUsernameLogin && defaultPolicy.AllowUsernamePassword != existingPolicy.AllowUserNamePassword { if !features.LoginPolicyUsernameLogin && defaultPolicy.AllowUsernamePassword != existingPolicy.AllowUserNamePassword {
policy.AllowUserNamePassword = defaultPolicy.AllowUsernamePassword policy.AllowUserNamePassword = defaultPolicy.AllowUsernamePassword
} }
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, OrgAggregateFromWriteModel(&existingPolicy.WriteModel), policy.AllowUserNamePassword, policy.AllowRegister, policy.AllowExternalIDP, policy.ForceMFA, policy.PasswordlessType) 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)
if hasChanged { if hasChanged {
events = append(events, changedEvent) events = append(events, changedEvent)
} }

View File

@@ -67,6 +67,7 @@ func (wm *OrgFeaturesWriteModel) NewSetEvent(
loginPolicyPasswordless, loginPolicyPasswordless,
loginPolicyRegistration, loginPolicyRegistration,
loginPolicyUsernameLogin, loginPolicyUsernameLogin,
loginPolicyPasswordReset,
passwordComplexityPolicy, passwordComplexityPolicy,
labelPolicy, labelPolicy,
customDomain bool, customDomain bool,
@@ -104,6 +105,9 @@ func (wm *OrgFeaturesWriteModel) NewSetEvent(
if wm.LoginPolicyUsernameLogin != loginPolicyUsernameLogin { if wm.LoginPolicyUsernameLogin != loginPolicyUsernameLogin {
changes = append(changes, features.ChangeLoginPolicyUsernameLogin(loginPolicyUsernameLogin)) changes = append(changes, features.ChangeLoginPolicyUsernameLogin(loginPolicyUsernameLogin))
} }
if wm.LoginPolicyPasswordReset != loginPolicyPasswordReset {
changes = append(changes, features.ChangeLoginPolicyPasswordReset(loginPolicyPasswordReset))
}
if wm.PasswordComplexityPolicy != passwordComplexityPolicy { if wm.PasswordComplexityPolicy != passwordComplexityPolicy {
changes = append(changes, features.ChangePasswordComplexityPolicy(passwordComplexityPolicy)) changes = append(changes, features.ChangePasswordComplexityPolicy(passwordComplexityPolicy))
} }

View File

@@ -54,6 +54,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
LoginPolicyPasswordless: false, LoginPolicyPasswordless: false,
LoginPolicyRegistration: false, LoginPolicyRegistration: false,
LoginPolicyUsernameLogin: false, LoginPolicyUsernameLogin: false,
LoginPolicyPasswordReset: false,
PasswordComplexityPolicy: false, PasswordComplexityPolicy: false,
LabelPolicy: false, LabelPolicy: false,
CustomDomain: false, CustomDomain: false,
@@ -87,6 +88,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
LoginPolicyPasswordless: false, LoginPolicyPasswordless: false,
LoginPolicyRegistration: false, LoginPolicyRegistration: false,
LoginPolicyUsernameLogin: false, LoginPolicyUsernameLogin: false,
LoginPolicyPasswordReset: false,
PasswordComplexityPolicy: false, PasswordComplexityPolicy: false,
LabelPolicy: false, LabelPolicy: false,
CustomDomain: false, CustomDomain: false,
@@ -111,6 +113,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
false, false,
false, false,
false, false,
false,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -191,6 +194,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
LoginPolicyPasswordless: false, LoginPolicyPasswordless: false,
LoginPolicyRegistration: false, LoginPolicyRegistration: false,
LoginPolicyUsernameLogin: false, LoginPolicyUsernameLogin: false,
LoginPolicyPasswordReset: false,
PasswordComplexityPolicy: false, PasswordComplexityPolicy: false,
LabelPolicy: false, LabelPolicy: false,
CustomDomain: false, CustomDomain: false,
@@ -217,6 +221,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
false, false,
false, false,
false, false,
false,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -325,6 +330,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
LoginPolicyPasswordless: false, LoginPolicyPasswordless: false,
LoginPolicyRegistration: false, LoginPolicyRegistration: false,
LoginPolicyUsernameLogin: false, LoginPolicyUsernameLogin: false,
LoginPolicyPasswordReset: false,
PasswordComplexityPolicy: false, PasswordComplexityPolicy: false,
LabelPolicy: false, LabelPolicy: false,
CustomDomain: false, CustomDomain: false,
@@ -351,6 +357,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
false, false,
false, false,
false, false,
false,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -469,6 +476,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
LoginPolicyPasswordless: false, LoginPolicyPasswordless: false,
LoginPolicyRegistration: false, LoginPolicyRegistration: false,
LoginPolicyUsernameLogin: false, LoginPolicyUsernameLogin: false,
LoginPolicyPasswordReset: false,
PasswordComplexityPolicy: false, PasswordComplexityPolicy: false,
LabelPolicy: false, LabelPolicy: false,
CustomDomain: false, CustomDomain: false,
@@ -495,6 +503,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
false, false,
false, false,
false, false,
false,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -623,6 +632,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
LoginPolicyPasswordless: false, LoginPolicyPasswordless: false,
LoginPolicyRegistration: false, LoginPolicyRegistration: false,
LoginPolicyUsernameLogin: false, LoginPolicyUsernameLogin: false,
LoginPolicyPasswordReset: false,
PasswordComplexityPolicy: false, PasswordComplexityPolicy: false,
LabelPolicy: false, LabelPolicy: false,
CustomDomain: false, CustomDomain: false,
@@ -653,6 +663,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -664,6 +675,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
false, false,
false, false,
false, false,
false,
domain.PasswordlessTypeNotAllowed, domain.PasswordlessTypeNotAllowed,
), ),
), ),
@@ -678,6 +690,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -790,7 +803,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
org.NewLoginPolicyMultiFactorAddedEvent(context.Background(), &org.NewAggregate("org1", "org1").Aggregate, domain.MultiFactorTypeU2FWithPIN), org.NewLoginPolicyMultiFactorAddedEvent(context.Background(), &org.NewAggregate("org1", "org1").Aggregate, domain.MultiFactorTypeU2FWithPIN),
), ),
eventFromEventPusher( eventFromEventPusher(
newLoginPolicyChangedEvent(context.Background(), "org1", true, true, true, true, domain.PasswordlessTypeAllowed), newLoginPolicyChangedEvent(context.Background(), "org1", true, true, true, true, true, domain.PasswordlessTypeAllowed),
), ),
eventFromEventPusher( eventFromEventPusher(
org.NewPasswordComplexityPolicyRemovedEvent(context.Background(), &org.NewAggregate("org1", "org1").Aggregate), org.NewPasswordComplexityPolicyRemovedEvent(context.Background(), &org.NewAggregate("org1", "org1").Aggregate),
@@ -920,6 +933,7 @@ func TestCommandSide_RemoveOrgFeatures(t *testing.T) {
false, false,
false, false,
false, false,
false,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),

View File

@@ -42,6 +42,7 @@ func (c *Commands) AddLoginPolicy(ctx context.Context, resourceOwner string, pol
policy.AllowRegister, policy.AllowRegister,
policy.AllowExternalIDP, policy.AllowExternalIDP,
policy.ForceMFA, policy.ForceMFA,
policy.HidePasswordReset,
policy.PasswordlessType)) policy.PasswordlessType))
if err != nil { if err != nil {
return nil, err return nil, err
@@ -81,7 +82,16 @@ func (c *Commands) ChangeLoginPolicy(ctx context.Context, resourceOwner string,
} }
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LoginPolicyWriteModel.WriteModel) orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LoginPolicyWriteModel.WriteModel)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, orgAgg, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIDP, policy.ForceMFA, policy.PasswordlessType) changedEvent, hasChanged := existingPolicy.NewChangedEvent(
ctx,
orgAgg,
policy.AllowUsernamePassword,
policy.AllowRegister,
policy.AllowExternalIDP,
policy.ForceMFA,
policy.HidePasswordReset,
policy.PasswordlessType)
if !hasChanged { if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-5M9vdd", "Errors.Org.LoginPolicy.NotChanged") return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-5M9vdd", "Errors.Org.LoginPolicy.NotChanged")
} }
@@ -118,6 +128,9 @@ func (c *Commands) checkLoginPolicyAllowed(ctx context.Context, resourceOwner st
if defaultPolicy.AllowUsernamePassword != policy.AllowUsernamePassword { if defaultPolicy.AllowUsernamePassword != policy.AllowUsernamePassword {
requiredFeatures = append(requiredFeatures, domain.FeatureLoginPolicyUsernameLogin) requiredFeatures = append(requiredFeatures, domain.FeatureLoginPolicyUsernameLogin)
} }
if defaultPolicy.HidePasswordReset != policy.HidePasswordReset {
requiredFeatures = append(requiredFeatures, domain.FeatureLoginPolicyPasswordReset)
}
return authz.CheckOrgFeatures(ctx, c.tokenVerifier, resourceOwner, requiredFeatures...) return authz.CheckOrgFeatures(ctx, c.tokenVerifier, resourceOwner, requiredFeatures...)
} }

View File

@@ -61,7 +61,8 @@ func (wm *OrgLoginPolicyWriteModel) NewChangedEvent(
allowUsernamePassword, allowUsernamePassword,
allowRegister, allowRegister,
allowExternalIDP, allowExternalIDP,
forceMFA bool, forceMFA,
hidePasswordReset bool,
passwordlessType domain.PasswordlessType, passwordlessType domain.PasswordlessType,
) (*org.LoginPolicyChangedEvent, bool) { ) (*org.LoginPolicyChangedEvent, bool) {
@@ -78,6 +79,9 @@ func (wm *OrgLoginPolicyWriteModel) NewChangedEvent(
if wm.ForceMFA != forceMFA { if wm.ForceMFA != forceMFA {
changes = append(changes, policy.ChangeForceMFA(forceMFA)) changes = append(changes, policy.ChangeForceMFA(forceMFA))
} }
if wm.HidePasswordReset != hidePasswordReset {
changes = append(changes, policy.ChangeHidePasswordReset(hidePasswordReset))
}
if passwordlessType.Valid() && wm.PasswordlessType != passwordlessType { if passwordlessType.Valid() && wm.PasswordlessType != passwordlessType {
changes = append(changes, policy.ChangePasswordlessType(passwordlessType)) changes = append(changes, policy.ChangePasswordlessType(passwordlessType))
} }

View File

@@ -70,6 +70,7 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -105,6 +106,7 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -141,6 +143,7 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -154,6 +157,7 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -170,6 +174,7 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
AllowUsernamePassword: true, AllowUsernamePassword: true,
AllowExternalIDP: true, AllowExternalIDP: true,
ForceMFA: true, ForceMFA: true,
HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed, PasswordlessType: domain.PasswordlessTypeAllowed,
}, },
}, },
@@ -183,6 +188,7 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
AllowUsernamePassword: true, AllowUsernamePassword: true,
AllowExternalIDP: true, AllowExternalIDP: true,
ForceMFA: true, ForceMFA: true,
HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed, PasswordlessType: domain.PasswordlessTypeAllowed,
}, },
}, },
@@ -285,6 +291,7 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -297,6 +304,7 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -332,6 +340,7 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -344,6 +353,7 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -359,6 +369,7 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
AllowUsernamePassword: true, AllowUsernamePassword: true,
AllowExternalIDP: true, AllowExternalIDP: true,
ForceMFA: true, ForceMFA: true,
HidePasswordReset: true,
PasswordlessType: domain.PasswordlessTypeAllowed, PasswordlessType: domain.PasswordlessTypeAllowed,
}, },
}, },
@@ -379,6 +390,7 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -391,6 +403,7 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
false, false,
false, false,
false, false,
false,
domain.PasswordlessTypeNotAllowed, domain.PasswordlessTypeNotAllowed,
), ),
), ),
@@ -398,7 +411,7 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
expectPush( expectPush(
[]*repository.Event{ []*repository.Event{
eventFromEventPusher( eventFromEventPusher(
newLoginPolicyChangedEvent(context.Background(), "org1", false, false, false, false, domain.PasswordlessTypeNotAllowed), newLoginPolicyChangedEvent(context.Background(), "org1", false, false, false, false, false, domain.PasswordlessTypeNotAllowed),
), ),
}, },
), ),
@@ -426,6 +439,7 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
AllowUsernamePassword: false, AllowUsernamePassword: false,
AllowExternalIDP: false, AllowExternalIDP: false,
ForceMFA: false, ForceMFA: false,
HidePasswordReset: false,
PasswordlessType: domain.PasswordlessTypeNotAllowed, PasswordlessType: domain.PasswordlessTypeNotAllowed,
}, },
}, },
@@ -512,6 +526,7 @@ func TestCommandSide_RemoveLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -655,6 +670,7 @@ func TestCommandSide_AddIDPProviderLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -839,6 +855,7 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -882,6 +899,7 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -932,6 +950,7 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -990,6 +1009,7 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
true, true,
true, true,
true, true,
true,
domain.PasswordlessTypeAllowed, domain.PasswordlessTypeAllowed,
), ),
), ),
@@ -1600,7 +1620,7 @@ func TestCommandSide_RemoveMultiFactorLoginPolicy(t *testing.T) {
} }
} }
func newLoginPolicyChangedEvent(ctx context.Context, orgID string, usernamePassword, register, externalIDP, mfa bool, passwordlessType domain.PasswordlessType) *org.LoginPolicyChangedEvent { func newLoginPolicyChangedEvent(ctx context.Context, orgID string, usernamePassword, register, externalIDP, mfa, passwordReset bool, passwordlessType domain.PasswordlessType) *org.LoginPolicyChangedEvent {
event, _ := org.NewLoginPolicyChangedEvent(ctx, event, _ := org.NewLoginPolicyChangedEvent(ctx,
&org.NewAggregate(orgID, orgID).Aggregate, &org.NewAggregate(orgID, orgID).Aggregate,
[]policy.LoginPolicyChanges{ []policy.LoginPolicyChanges{
@@ -1608,6 +1628,7 @@ func newLoginPolicyChangedEvent(ctx context.Context, orgID string, usernamePassw
policy.ChangeAllowRegister(register), policy.ChangeAllowRegister(register),
policy.ChangeAllowExternalIDP(externalIDP), policy.ChangeAllowExternalIDP(externalIDP),
policy.ChangeForceMFA(mfa), policy.ChangeForceMFA(mfa),
policy.ChangeHidePasswordReset(passwordReset),
policy.ChangePasswordlessType(passwordlessType), policy.ChangePasswordlessType(passwordlessType),
}, },
) )

View File

@@ -13,6 +13,7 @@ type LoginPolicyWriteModel struct {
AllowRegister bool AllowRegister bool
AllowExternalIDP bool AllowExternalIDP bool
ForceMFA bool ForceMFA bool
HidePasswordReset bool
PasswordlessType domain.PasswordlessType PasswordlessType domain.PasswordlessType
State domain.PolicyState State domain.PolicyState
} }
@@ -26,6 +27,7 @@ func (wm *LoginPolicyWriteModel) Reduce() error {
wm.AllowExternalIDP = e.AllowExternalIDP wm.AllowExternalIDP = e.AllowExternalIDP
wm.ForceMFA = e.ForceMFA wm.ForceMFA = e.ForceMFA
wm.PasswordlessType = e.PasswordlessType wm.PasswordlessType = e.PasswordlessType
wm.HidePasswordReset = e.HidePasswordReset
wm.State = domain.PolicyStateActive wm.State = domain.PolicyStateActive
case *policy.LoginPolicyChangedEvent: case *policy.LoginPolicyChangedEvent:
if e.AllowRegister != nil { if e.AllowRegister != nil {
@@ -40,6 +42,9 @@ func (wm *LoginPolicyWriteModel) Reduce() error {
if e.ForceMFA != nil { if e.ForceMFA != nil {
wm.ForceMFA = *e.ForceMFA wm.ForceMFA = *e.ForceMFA
} }
if e.HidePasswordReset != nil {
wm.HidePasswordReset = *e.HidePasswordReset
}
if e.PasswordlessType != nil { if e.PasswordlessType != nil {
wm.PasswordlessType = *e.PasswordlessType wm.PasswordlessType = *e.PasswordlessType
} }

View File

@@ -13,6 +13,7 @@ const (
FeatureLoginPolicyPasswordless = FeatureLoginPolicy + ".passwordless" FeatureLoginPolicyPasswordless = FeatureLoginPolicy + ".passwordless"
FeatureLoginPolicyRegistration = FeatureLoginPolicy + ".registration" FeatureLoginPolicyRegistration = FeatureLoginPolicy + ".registration"
FeatureLoginPolicyUsernameLogin = FeatureLoginPolicy + ".username_login" FeatureLoginPolicyUsernameLogin = FeatureLoginPolicy + ".username_login"
FeatureLoginPolicyPasswordReset = FeatureLoginPolicy + ".password_reset"
FeaturePasswordComplexityPolicy = "password_complexity_policy" FeaturePasswordComplexityPolicy = "password_complexity_policy"
FeatureLabelPolicy = "label_policy" FeatureLabelPolicy = "label_policy"
FeatureCustomDomain = "custom_domain" FeatureCustomDomain = "custom_domain"
@@ -33,6 +34,7 @@ type Features struct {
LoginPolicyPasswordless bool LoginPolicyPasswordless bool
LoginPolicyRegistration bool LoginPolicyRegistration bool
LoginPolicyUsernameLogin bool LoginPolicyUsernameLogin bool
LoginPolicyPasswordReset bool
PasswordComplexityPolicy bool PasswordComplexityPolicy bool
LabelPolicy bool LabelPolicy bool
CustomDomain bool CustomDomain bool

View File

@@ -14,6 +14,7 @@ type LoginPolicy struct {
SecondFactors []SecondFactorType SecondFactors []SecondFactorType
MultiFactors []MultiFactorType MultiFactors []MultiFactorType
PasswordlessType PasswordlessType PasswordlessType PasswordlessType
HidePasswordReset bool
} }
type IDPProvider struct { type IDPProvider struct {

View File

@@ -23,6 +23,7 @@ type FeaturesView struct {
LoginPolicyPasswordless bool LoginPolicyPasswordless bool
LoginPolicyRegistration bool LoginPolicyRegistration bool
LoginPolicyUsernameLogin bool LoginPolicyUsernameLogin bool
LoginPolicyPasswordReset bool
PasswordComplexityPolicy bool PasswordComplexityPolicy bool
LabelPolicy bool LabelPolicy bool
CustomDomain bool CustomDomain bool
@@ -45,6 +46,9 @@ func (f *FeaturesView) FeatureList() []string {
if f.LoginPolicyUsernameLogin { if f.LoginPolicyUsernameLogin {
list = append(list, domain.FeatureLoginPolicyUsernameLogin) list = append(list, domain.FeatureLoginPolicyUsernameLogin)
} }
if f.LoginPolicyPasswordReset {
list = append(list, domain.FeatureLoginPolicyPasswordReset)
}
if f.PasswordComplexityPolicy { if f.PasswordComplexityPolicy {
list = append(list, domain.FeaturePasswordComplexityPolicy) list = append(list, domain.FeaturePasswordComplexityPolicy)
} }

View File

@@ -36,6 +36,7 @@ type FeaturesView struct {
LoginPolicyPasswordless bool `json:"loginPolicyPasswordless" gorm:"column:login_policy_passwordless"` LoginPolicyPasswordless bool `json:"loginPolicyPasswordless" gorm:"column:login_policy_passwordless"`
LoginPolicyRegistration bool `json:"loginPolicyRegistration" gorm:"column:login_policy_registration"` LoginPolicyRegistration bool `json:"loginPolicyRegistration" gorm:"column:login_policy_registration"`
LoginPolicyUsernameLogin bool `json:"loginPolicyUsernameLogin" gorm:"column:login_policy_username_login"` LoginPolicyUsernameLogin bool `json:"loginPolicyUsernameLogin" gorm:"column:login_policy_username_login"`
LoginPolicyPasswordReset bool `json:"loginPolicyPasswordReset" gorm:"column:login_policy_password_reset"`
PasswordComplexityPolicy bool `json:"passwordComplexityPolicy" gorm:"column:password_complexity_policy"` PasswordComplexityPolicy bool `json:"passwordComplexityPolicy" gorm:"column:password_complexity_policy"`
LabelPolicy bool `json:"labelPolicy" gorm:"column:label_policy"` LabelPolicy bool `json:"labelPolicy" gorm:"column:label_policy"`
CustomDomain bool `json:"customDomain" gorm:"column:custom_domain"` CustomDomain bool `json:"customDomain" gorm:"column:custom_domain"`
@@ -58,6 +59,7 @@ func FeaturesToModel(features *FeaturesView) *features_model.FeaturesView {
LoginPolicyPasswordless: features.LoginPolicyPasswordless, LoginPolicyPasswordless: features.LoginPolicyPasswordless,
LoginPolicyRegistration: features.LoginPolicyRegistration, LoginPolicyRegistration: features.LoginPolicyRegistration,
LoginPolicyUsernameLogin: features.LoginPolicyUsernameLogin, LoginPolicyUsernameLogin: features.LoginPolicyUsernameLogin,
LoginPolicyPasswordReset: features.LoginPolicyPasswordReset,
PasswordComplexityPolicy: features.PasswordComplexityPolicy, PasswordComplexityPolicy: features.PasswordComplexityPolicy,
LabelPolicy: features.LabelPolicy, LabelPolicy: features.LabelPolicy,
CustomDomain: features.CustomDomain, CustomDomain: features.CustomDomain,

View File

@@ -1,9 +1,10 @@
package model package model
import ( import (
"time"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/models"
"time"
) )
type LoginPolicyView struct { type LoginPolicyView struct {
@@ -12,6 +13,7 @@ type LoginPolicyView struct {
AllowRegister bool AllowRegister bool
AllowExternalIDP bool AllowExternalIDP bool
ForceMFA bool ForceMFA bool
HidePasswordReset bool
PasswordlessType PasswordlessType PasswordlessType PasswordlessType
SecondFactors []SecondFactorType SecondFactors []SecondFactorType
MultiFactors []MultiFactorType MultiFactors []MultiFactorType
@@ -80,6 +82,7 @@ func (p *LoginPolicyView) ToLoginPolicyDomain() *domain.LoginPolicy {
AllowRegister: p.AllowRegister, AllowRegister: p.AllowRegister,
AllowExternalIDP: p.AllowExternalIDP, AllowExternalIDP: p.AllowExternalIDP,
ForceMFA: p.ForceMFA, ForceMFA: p.ForceMFA,
HidePasswordReset: p.HidePasswordReset,
PasswordlessType: passwordLessTypeToDomain(p.PasswordlessType), PasswordlessType: passwordLessTypeToDomain(p.PasswordlessType),
SecondFactors: secondFactorsToDomain(p.SecondFactors), SecondFactors: secondFactorsToDomain(p.SecondFactors),
MultiFactors: multiFactorsToDomain(p.MultiFactors), MultiFactors: multiFactorsToDomain(p.MultiFactors),

View File

@@ -2,13 +2,16 @@ package model
import ( import (
"encoding/json" "encoding/json"
org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
"github.com/lib/pq"
"time" "time"
"github.com/lib/pq"
org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model"
"github.com/caos/logging" "github.com/caos/logging"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/iam/model" "github.com/caos/zitadel/internal/iam/model"
@@ -29,6 +32,7 @@ type LoginPolicyView struct {
AllowUsernamePassword bool `json:"allowUsernamePassword" gorm:"column:allow_username_password"` AllowUsernamePassword bool `json:"allowUsernamePassword" gorm:"column:allow_username_password"`
AllowExternalIDP bool `json:"allowExternalIdp" gorm:"column:allow_external_idp"` AllowExternalIDP bool `json:"allowExternalIdp" gorm:"column:allow_external_idp"`
ForceMFA bool `json:"forceMFA" gorm:"column:force_mfa"` ForceMFA bool `json:"forceMFA" gorm:"column:force_mfa"`
HidePasswordReset bool `json:"hidePasswordReset" gorm:"column:hide_password_reset"`
PasswordlessType int32 `json:"passwordlessType" gorm:"column:passwordless_type"` PasswordlessType int32 `json:"passwordlessType" gorm:"column:passwordless_type"`
SecondFactors pq.Int64Array `json:"-" gorm:"column:second_factors"` SecondFactors pq.Int64Array `json:"-" gorm:"column:second_factors"`
MultiFactors pq.Int64Array `json:"-" gorm:"column:multi_factors"` MultiFactors pq.Int64Array `json:"-" gorm:"column:multi_factors"`
@@ -47,6 +51,7 @@ func LoginPolicyViewFromModel(policy *model.LoginPolicyView) *LoginPolicyView {
AllowExternalIDP: policy.AllowExternalIDP, AllowExternalIDP: policy.AllowExternalIDP,
AllowUsernamePassword: policy.AllowUsernamePassword, AllowUsernamePassword: policy.AllowUsernamePassword,
ForceMFA: policy.ForceMFA, ForceMFA: policy.ForceMFA,
HidePasswordReset: policy.HidePasswordReset,
PasswordlessType: int32(policy.PasswordlessType), PasswordlessType: int32(policy.PasswordlessType),
SecondFactors: secondFactorsFromModel(policy.SecondFactors), SecondFactors: secondFactorsFromModel(policy.SecondFactors),
MultiFactors: multiFactorsFromModel(policy.MultiFactors), MultiFactors: multiFactorsFromModel(policy.MultiFactors),
@@ -80,6 +85,7 @@ func LoginPolicyViewToModel(policy *LoginPolicyView) *model.LoginPolicyView {
AllowExternalIDP: policy.AllowExternalIDP, AllowExternalIDP: policy.AllowExternalIDP,
AllowUsernamePassword: policy.AllowUsernamePassword, AllowUsernamePassword: policy.AllowUsernamePassword,
ForceMFA: policy.ForceMFA, ForceMFA: policy.ForceMFA,
HidePasswordReset: policy.HidePasswordReset,
PasswordlessType: model.PasswordlessType(policy.PasswordlessType), PasswordlessType: model.PasswordlessType(policy.PasswordlessType),
SecondFactors: secondFactorsToModel(policy.SecondFactors), SecondFactors: secondFactorsToModel(policy.SecondFactors),
MultiFactors: multiFactorsToToModel(policy.MultiFactors), MultiFactors: multiFactorsToToModel(policy.MultiFactors),

View File

@@ -29,6 +29,7 @@ type FeaturesSetEvent struct {
LoginPolicyPasswordless *bool `json:"loginPolicyPasswordless,omitempty"` LoginPolicyPasswordless *bool `json:"loginPolicyPasswordless,omitempty"`
LoginPolicyRegistration *bool `json:"loginPolicyRegistration,omitempty"` LoginPolicyRegistration *bool `json:"loginPolicyRegistration,omitempty"`
LoginPolicyUsernameLogin *bool `json:"loginPolicyUsernameLogin,omitempty"` LoginPolicyUsernameLogin *bool `json:"loginPolicyUsernameLogin,omitempty"`
LoginPolicyPasswordReset *bool `json:"loginPolicyPasswordReset,omitempty"`
PasswordComplexityPolicy *bool `json:"passwordComplexityPolicy,omitempty"` PasswordComplexityPolicy *bool `json:"passwordComplexityPolicy,omitempty"`
LabelPolicy *bool `json:"labelPolicy,omitempty"` LabelPolicy *bool `json:"labelPolicy,omitempty"`
CustomDomain *bool `json:"customDomain,omitempty"` CustomDomain *bool `json:"customDomain,omitempty"`
@@ -120,6 +121,12 @@ func ChangeLoginPolicyUsernameLogin(loginPolicyUsernameLogin bool) func(event *F
} }
} }
func ChangeLoginPolicyPasswordReset(loginPolicyPasswordReset bool) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.LoginPolicyPasswordReset = &loginPolicyPasswordReset
}
}
func ChangePasswordComplexityPolicy(passwordComplexityPolicy bool) func(event *FeaturesSetEvent) { func ChangePasswordComplexityPolicy(passwordComplexityPolicy bool) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) { return func(e *FeaturesSetEvent) {
e.PasswordComplexityPolicy = &passwordComplexityPolicy e.PasswordComplexityPolicy = &passwordComplexityPolicy

View File

@@ -24,7 +24,8 @@ func NewLoginPolicyAddedEvent(
allowUsernamePassword, allowUsernamePassword,
allowRegister, allowRegister,
allowExternalIDP, allowExternalIDP,
forceMFA bool, forceMFA,
hidePasswordReset bool,
passwordlessType domain.PasswordlessType, passwordlessType domain.PasswordlessType,
) *LoginPolicyAddedEvent { ) *LoginPolicyAddedEvent {
return &LoginPolicyAddedEvent{ return &LoginPolicyAddedEvent{
@@ -37,6 +38,7 @@ func NewLoginPolicyAddedEvent(
allowRegister, allowRegister,
allowExternalIDP, allowExternalIDP,
forceMFA, forceMFA,
hidePasswordReset,
passwordlessType), passwordlessType),
} }
} }

View File

@@ -25,7 +25,8 @@ func NewLoginPolicyAddedEvent(
allowUsernamePassword, allowUsernamePassword,
allowRegister, allowRegister,
allowExternalIDP, allowExternalIDP,
forceMFA bool, forceMFA,
hidePasswordReset bool,
passwordlessType domain.PasswordlessType, passwordlessType domain.PasswordlessType,
) *LoginPolicyAddedEvent { ) *LoginPolicyAddedEvent {
return &LoginPolicyAddedEvent{ return &LoginPolicyAddedEvent{
@@ -38,6 +39,7 @@ func NewLoginPolicyAddedEvent(
allowRegister, allowRegister,
allowExternalIDP, allowExternalIDP,
forceMFA, forceMFA,
hidePasswordReset,
passwordlessType), passwordlessType),
} }
} }

View File

@@ -2,6 +2,7 @@ package policy
import ( import (
"encoding/json" "encoding/json"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore"
@@ -22,6 +23,7 @@ type LoginPolicyAddedEvent struct {
AllowRegister bool `json:"allowRegister,omitempty"` AllowRegister bool `json:"allowRegister,omitempty"`
AllowExternalIDP bool `json:"allowExternalIdp,omitempty"` AllowExternalIDP bool `json:"allowExternalIdp,omitempty"`
ForceMFA bool `json:"forceMFA,omitempty"` ForceMFA bool `json:"forceMFA,omitempty"`
HidePasswordReset bool `json:"hidePasswordReset,omitempty"`
PasswordlessType domain.PasswordlessType `json:"passwordlessType,omitempty"` PasswordlessType domain.PasswordlessType `json:"passwordlessType,omitempty"`
} }
@@ -38,7 +40,8 @@ func NewLoginPolicyAddedEvent(
allowUserNamePassword, allowUserNamePassword,
allowRegister, allowRegister,
allowExternalIDP, allowExternalIDP,
forceMFA bool, forceMFA,
hidePasswordReset bool,
passwordlessType domain.PasswordlessType, passwordlessType domain.PasswordlessType,
) *LoginPolicyAddedEvent { ) *LoginPolicyAddedEvent {
return &LoginPolicyAddedEvent{ return &LoginPolicyAddedEvent{
@@ -48,6 +51,7 @@ func NewLoginPolicyAddedEvent(
AllowUserNamePassword: allowUserNamePassword, AllowUserNamePassword: allowUserNamePassword,
ForceMFA: forceMFA, ForceMFA: forceMFA,
PasswordlessType: passwordlessType, PasswordlessType: passwordlessType,
HidePasswordReset: hidePasswordReset,
} }
} }
@@ -71,6 +75,7 @@ type LoginPolicyChangedEvent struct {
AllowRegister *bool `json:"allowRegister,omitempty"` AllowRegister *bool `json:"allowRegister,omitempty"`
AllowExternalIDP *bool `json:"allowExternalIdp,omitempty"` AllowExternalIDP *bool `json:"allowExternalIdp,omitempty"`
ForceMFA *bool `json:"forceMFA,omitempty"` ForceMFA *bool `json:"forceMFA,omitempty"`
HidePasswordReset *bool `json:"hidePasswordReset,omitempty"`
PasswordlessType *domain.PasswordlessType `json:"passwordlessType,omitempty"` PasswordlessType *domain.PasswordlessType `json:"passwordlessType,omitempty"`
} }
@@ -133,6 +138,12 @@ func ChangePasswordlessType(passwordlessType domain.PasswordlessType) func(*Logi
} }
} }
func ChangeHidePasswordReset(hidePasswordReset bool) func(*LoginPolicyChangedEvent) {
return func(e *LoginPolicyChangedEvent) {
e.HidePasswordReset = &hidePasswordReset
}
}
func LoginPolicyChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) { func LoginPolicyChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e := &LoginPolicyChangedEvent{ e := &LoginPolicyChangedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event), BaseEvent: *eventstore.BaseEventFromRepo(event),

View File

@@ -21,7 +21,15 @@ func (l *Login) renderPassword(w http.ResponseWriter, r *http.Request, authReq *
errMessage = l.getErrorMessage(r, err) errMessage = l.getErrorMessage(r, err)
} }
data := l.getUserData(r, authReq, "Password", errType, errMessage) data := l.getUserData(r, authReq, "Password", errType, errMessage)
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplPassword], data, nil) funcs := map[string]interface{}{
"showPasswordReset": func() bool {
if authReq.LoginPolicy != nil {
return !authReq.LoginPolicy.HidePasswordReset
}
return true
},
}
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplPassword], data, funcs)
} }
func (l *Login) handlePasswordCheck(w http.ResponseWriter, r *http.Request) { func (l *Login) handlePasswordCheck(w http.ResponseWriter, r *http.Request) {

View File

@@ -153,6 +153,9 @@ func CreateRenderer(pathPrefix string, staticDir http.FileSystem, cookieName str
"hasUsernamePasswordLogin": func() bool { "hasUsernamePasswordLogin": func() bool {
return false return false
}, },
"showPasswordReset": func() bool {
return true
},
"hasExternalLogin": func() bool { "hasExternalLogin": func() bool {
return false return false
}, },

View File

@@ -21,9 +21,11 @@
{{template "error-message" .}} {{template "error-message" .}}
{{ if showPasswordReset }}
<a class="block" href="{{ passwordResetUrl .AuthReqID }}"> <a class="block" href="{{ passwordResetUrl .AuthReqID }}">
{{t "Actions.ForgotPassword"}} {{t "Actions.ForgotPassword"}}
</a> </a>
{{ end }}
<div class="lgn-actions"> <div class="lgn-actions">
<a href="{{ loginNameChangeUrl .AuthReqID }}"> <a href="{{ loginNameChangeUrl .AuthReqID }}">

View File

@@ -0,0 +1,9 @@
ALTER TABLE adminapi.features ADD COLUMN login_policy_password_reset BOOLEAN;
ALTER TABLE auth.features ADD COLUMN login_policy_password_reset BOOLEAN;
ALTER TABLE authz.features ADD COLUMN login_policy_password_reset BOOLEAN;
ALTER TABLE management.features ADD COLUMN login_policy_password_reset BOOLEAN;
ALTER TABLE auth.login_policies ADD COLUMN hide_password_reset BOOLEAN;
ALTER TABLE adminapi.login_policies ADD COLUMN hide_password_reset BOOLEAN;
ALTER TABLE management.login_policies ADD COLUMN hide_password_reset BOOLEAN;

View File

@@ -2194,6 +2194,7 @@ message SetDefaultFeaturesRequest {
bool password_complexity_policy = 11; bool password_complexity_policy = 11;
bool label_policy = 12; bool label_policy = 12;
bool custom_domain = 13; bool custom_domain = 13;
bool login_policy_password_reset = 14;
} }
message SetDefaultFeaturesResponse { message SetDefaultFeaturesResponse {
@@ -2224,6 +2225,7 @@ message SetOrgFeaturesRequest {
bool password_complexity_policy = 12; bool password_complexity_policy = 12;
bool label_policy = 13; bool label_policy = 13;
bool custom_domain = 14; bool custom_domain = 14;
bool login_policy_password_reset = 15;
} }
message SetOrgFeaturesResponse { message SetOrgFeaturesResponse {
@@ -2421,6 +2423,11 @@ message UpdateLoginPolicyRequest {
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "defines if passwordless is allowed for users" description: "defines if passwordless is allowed for users"
}]; }];
bool hide_password_reset = 6 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "defines if password reset link should be shown in the login screen"
}
];
} }
message UpdateLoginPolicyResponse { message UpdateLoginPolicyResponse {

View File

@@ -21,6 +21,7 @@ message Features {
bool password_complexity_policy = 10; bool password_complexity_policy = 10;
bool label_policy = 11; bool label_policy = 11;
bool custom_domain = 12; bool custom_domain = 12;
bool login_policy_password_reset = 13;
} }
message FeatureTier { message FeatureTier {

View File

@@ -3373,6 +3373,7 @@ message AddCustomLoginPolicyRequest {
bool allow_external_idp = 3; bool allow_external_idp = 3;
bool force_mfa = 4; bool force_mfa = 4;
zitadel.policy.v1.PasswordlessType passwordless_type = 5 [(validate.rules).enum = {defined_only: true}]; zitadel.policy.v1.PasswordlessType passwordless_type = 5 [(validate.rules).enum = {defined_only: true}];
bool hide_password_reset = 6;
} }
message AddCustomLoginPolicyResponse { message AddCustomLoginPolicyResponse {
@@ -3385,6 +3386,7 @@ message UpdateCustomLoginPolicyRequest {
bool allow_external_idp = 3; bool allow_external_idp = 3;
bool force_mfa = 4; bool force_mfa = 4;
zitadel.policy.v1.PasswordlessType passwordless_type = 5 [(validate.rules).enum = {defined_only: true}]; zitadel.policy.v1.PasswordlessType passwordless_type = 5 [(validate.rules).enum = {defined_only: true}];
bool hide_password_reset = 6;
} }
message UpdateCustomLoginPolicyResponse { message UpdateCustomLoginPolicyResponse {

View File

@@ -77,6 +77,11 @@ message LoginPolicy {
description: "defines if the organisation's admin changed the policy" description: "defines if the organisation's admin changed the policy"
} }
]; ];
bool hide_password_reset = 8 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "defines if password reset link should be shown in the login screen"
}
];
} }
enum SecondFactorType { enum SecondFactorType {