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

View File

@ -66,6 +66,15 @@
</mat-slide-toggle>
</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">
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYREGISTRATION' | translate}}</span>
<span class="fill-space"></span>
@ -132,4 +141,4 @@
type="submit" mat-raised-button>{{ 'ACTIONS.SAVE' | translate
}}</button>
</div>
</app-detail-layout>
</app-detail-layout>

View File

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

View File

@ -95,6 +95,26 @@
</cnsl-info-section>
</ng-template>
</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">
<cnsl-form-field class="form-field" label="Access Code" required="true">
@ -201,4 +221,4 @@
<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.setForceMfa(this.loginData.forceMfa);
mgmtreq.setPasswordlessType(this.loginData.passwordlessType);
mgmtreq.setHidePasswordReset(this.loginData.hidePasswordReset);
if ((this.loginData as LoginPolicy.AsObject).isDefault) {
return (this.service as ManagementService).addCustomLoginPolicy(mgmtreq);
} else {
@ -155,6 +156,7 @@ export class LoginPolicyComponent implements OnDestroy {
adminreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword);
adminreq.setForceMfa(this.loginData.forceMfa);
adminreq.setPasswordlessType(this.loginData.passwordlessType);
adminreq.setHidePasswordReset(this.loginData.hidePasswordReset);
return (this.service as AdminService).updateLoginPolicy(adminreq);
}

View File

@ -607,6 +607,7 @@
"DATA": {
"AUDITLOGRETENTION": "Audit Log Retention",
"LOGINPOLICYUSERNAMELOGIN": "Login Richtlinie: Login mit Username erlauben - benutzerdefiniert",
"LOGINPOLICYPASSWORDRESET": "Login Richtlinie: Passwort vergessen Link nicht anzeigen - benutzerdefiniert",
"LOGINPOLICYREGISTRATION": "Login Richtlinie: Registration erlauben - benutzerdefiniert",
"LOGINPOLICYIDP": "Login Richtlinie: Identity Providers - benutzerdefiniert",
"LOGINPOLICYFACTORS": "Login Richtlinie: Mltifaktoren - benutzerdefiniert",
@ -683,7 +684,9 @@
"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.",
"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",
"CREATECUSTOM": "Benutzerdefinierte Richtlinie erstellen",

View File

@ -607,6 +607,7 @@
"DATA": {
"AUDITLOGRETENTION": "Audit Log Retention",
"LOGINPOLICYUSERNAMELOGIN": "Login Policy: Allow login with Username - custom",
"LOGINPOLICYPASSWORDRESET": "Login Policy: Hide reset password link - custom",
"LOGINPOLICYREGISTRATION": "Login Policy: Allow self registration - custom",
"LOGINPOLICYIDP": "Login Policy: Identity Provider - custom",
"LOGINPOLICYFACTORS": "Login Policy: Multifactors - custom",
@ -683,7 +684,9 @@
"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.",
"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",
"CREATECUSTOM": "Create Custom Policy",

View File

@ -1421,6 +1421,7 @@ This is an empty response
| password_complexity_policy | bool | - | |
| label_policy | bool | - | |
| custom_domain | bool | - | |
| login_policy_password_reset | bool | - | |
@ -1456,6 +1457,7 @@ This is an empty response
| password_complexity_policy | bool | - | |
| label_policy | bool | - | |
| custom_domain | bool | - | |
| login_policy_password_reset | bool | - | |
@ -1696,6 +1698,7 @@ This is an empty response
| allow_external_idp | bool | - | |
| force_mfa | bool | - | |
| 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 | - | |
| force_mfa | bool | - | |
| 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 | - | |
| force_mfa | bool | - | |
| 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 | - | |
| passwordless_type | PasswordlessType | - | |
| is_default | bool | - | |
| hide_password_reset | bool | - | |

View File

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

View File

@ -15,6 +15,7 @@ func updateLoginPolicyToDomain(p *admin_pb.UpdateLoginPolicyRequest) *domain.Log
AllowExternalIDP: p.AllowExternalIdp,
ForceMFA: p.ForceMfa,
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,
LoginPolicyRegistration: features.LoginPolicyRegistration,
LoginPolicyUsernameLogin: features.LoginPolicyUsernameLogin,
LoginPolicyPasswordReset: features.LoginPolicyPasswordReset,
PasswordComplexityPolicy: features.PasswordComplexityPolicy,
LabelPolicy: features.LabelPolicy,
CustomDomain: features.CustomDomain,

View File

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

View File

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

View File

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

View File

@ -39,6 +39,7 @@ func writeModelToLoginPolicy(wm *LoginPolicyWriteModel) *domain.LoginPolicy {
AllowUsernamePassword: wm.AllowUserNamePassword,
AllowRegister: wm.AllowRegister,
AllowExternalIDP: wm.AllowExternalIDP,
HidePasswordReset: wm.HidePasswordReset,
ForceMFA: wm.ForceMFA,
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 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) {
@ -77,7 +77,7 @@ 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.PasswordlessType)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, iamAgg, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIDP, policy.ForceMFA, policy.HidePasswordReset, policy.PasswordlessType)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-5M9vdd", "Errors.IAM.LoginPolicy.NotChanged")
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -42,6 +42,7 @@ func (c *Commands) AddLoginPolicy(ctx context.Context, resourceOwner string, pol
policy.AllowRegister,
policy.AllowExternalIDP,
policy.ForceMFA,
policy.HidePasswordReset,
policy.PasswordlessType))
if err != nil {
return nil, err
@ -81,7 +82,16 @@ func (c *Commands) ChangeLoginPolicy(ctx context.Context, resourceOwner string,
}
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 {
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 {
requiredFeatures = append(requiredFeatures, domain.FeatureLoginPolicyUsernameLogin)
}
if defaultPolicy.HidePasswordReset != policy.HidePasswordReset {
requiredFeatures = append(requiredFeatures, domain.FeatureLoginPolicyPasswordReset)
}
return authz.CheckOrgFeatures(ctx, c.tokenVerifier, resourceOwner, requiredFeatures...)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -36,6 +36,7 @@ type FeaturesView struct {
LoginPolicyPasswordless bool `json:"loginPolicyPasswordless" gorm:"column:login_policy_passwordless"`
LoginPolicyRegistration bool `json:"loginPolicyRegistration" gorm:"column:login_policy_registration"`
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"`
LabelPolicy bool `json:"labelPolicy" gorm:"column:label_policy"`
CustomDomain bool `json:"customDomain" gorm:"column:custom_domain"`
@ -58,6 +59,7 @@ func FeaturesToModel(features *FeaturesView) *features_model.FeaturesView {
LoginPolicyPasswordless: features.LoginPolicyPasswordless,
LoginPolicyRegistration: features.LoginPolicyRegistration,
LoginPolicyUsernameLogin: features.LoginPolicyUsernameLogin,
LoginPolicyPasswordReset: features.LoginPolicyPasswordReset,
PasswordComplexityPolicy: features.PasswordComplexityPolicy,
LabelPolicy: features.LabelPolicy,
CustomDomain: features.CustomDomain,

View File

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

View File

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

View File

@ -29,6 +29,7 @@ type FeaturesSetEvent struct {
LoginPolicyPasswordless *bool `json:"loginPolicyPasswordless,omitempty"`
LoginPolicyRegistration *bool `json:"loginPolicyRegistration,omitempty"`
LoginPolicyUsernameLogin *bool `json:"loginPolicyUsernameLogin,omitempty"`
LoginPolicyPasswordReset *bool `json:"loginPolicyPasswordReset,omitempty"`
PasswordComplexityPolicy *bool `json:"passwordComplexityPolicy,omitempty"`
LabelPolicy *bool `json:"labelPolicy,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) {
return func(e *FeaturesSetEvent) {
e.PasswordComplexityPolicy = &passwordComplexityPolicy

View File

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

View File

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

View File

@ -2,6 +2,7 @@ package policy
import (
"encoding/json"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
@ -22,6 +23,7 @@ type LoginPolicyAddedEvent struct {
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"`
}
@ -38,7 +40,8 @@ func NewLoginPolicyAddedEvent(
allowUserNamePassword,
allowRegister,
allowExternalIDP,
forceMFA bool,
forceMFA,
hidePasswordReset bool,
passwordlessType domain.PasswordlessType,
) *LoginPolicyAddedEvent {
return &LoginPolicyAddedEvent{
@ -48,6 +51,7 @@ func NewLoginPolicyAddedEvent(
AllowUserNamePassword: allowUserNamePassword,
ForceMFA: forceMFA,
PasswordlessType: passwordlessType,
HidePasswordReset: hidePasswordReset,
}
}
@ -71,6 +75,7 @@ type LoginPolicyChangedEvent struct {
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"`
}
@ -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) {
e := &LoginPolicyChangedEvent{
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)
}
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) {

View File

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

View File

@ -21,9 +21,11 @@
{{template "error-message" .}}
{{ if showPasswordReset }}
<a class="block" href="{{ passwordResetUrl .AuthReqID }}">
{{t "Actions.ForgotPassword"}}
</a>
{{ end }}
<div class="lgn-actions">
<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 label_policy = 12;
bool custom_domain = 13;
bool login_policy_password_reset = 14;
}
message SetDefaultFeaturesResponse {
@ -2224,6 +2225,7 @@ message SetOrgFeaturesRequest {
bool password_complexity_policy = 12;
bool label_policy = 13;
bool custom_domain = 14;
bool login_policy_password_reset = 15;
}
message SetOrgFeaturesResponse {
@ -2421,6 +2423,11 @@ message UpdateLoginPolicyRequest {
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
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 {

View File

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

View File

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

View File

@ -77,6 +77,11 @@ message LoginPolicy {
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 {