feat: password age policy (#8132)

# Which Problems Are Solved

Some organizations / customers have the requirement, that there users
regularly need to change their password.
ZITADEL already had the possibility to manage a `password age policy` (
thought the API) with the maximum amount of days a password should be
valid, resp. days after with the user should be warned of the upcoming
expiration.
The policy could not be managed though the Console UI and was not
checked in the Login UI.

# How the Problems Are Solved

- The policy can be managed in the Console UI's settings sections on an
instance and organization level.
- During an authentication in the Login UI, if a policy is set with an
expiry (>0) and the user's last password change exceeds the amount of
days set, the user will be prompted to change their password.
- The prompt message of the Login UI can be customized in the Custom
Login Texts though the Console and API on the instance and each
organization.
- The information when the user last changed their password is returned
in the Auth, Management and User V2 API.
- The policy can be retrieved in the settings service as `password
expiry settings`.

# Additional Changes

None.

# Additional Context

- closes #8081

---------

Co-authored-by: Tim Möhlmann <tim+github@zitadel.com>
This commit is contained in:
Livio Spring
2024-06-18 13:27:44 +02:00
committed by GitHub
parent 65f787cc02
commit fb8cd18f93
93 changed files with 1250 additions and 487 deletions

View File

@@ -188,6 +188,7 @@ export function mapRequestValues(map: Partial<Map>, req: Req): Req {
const r17 = new PasswordChangeScreenText();
r17.setDescription(map.passwordChangeText?.description ?? '');
r17.setExpiredDescription(map.passwordChangeText?.expiredDescription ?? '');
r17.setNextButtonText(map.passwordChangeText?.nextButtonText ?? '');
r17.setTitle(map.passwordChangeText?.title ?? '');
r17.setNewPasswordLabel(map.passwordChangeText?.newPasswordLabel ?? '');

View File

@@ -0,0 +1,20 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { PasswordAgePolicyComponent } from './password-age-policy.component';
const routes: Routes = [
{
path: '',
component: PasswordAgePolicyComponent,
data: {
animation: 'DetailPage',
},
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class PasswordAgePolicyRoutingModule {}

View File

@@ -0,0 +1,50 @@
<h2>{{ 'POLICY.PWD_AGE.TITLE' | translate }}</h2>
<p class="cnsl-secondary-text">{{ 'POLICY.PWD_AGE.DESCRIPTION' | translate }}</p>
<div *ngIf="loading" class="spinner-wr">
<mat-spinner diameter="30" color="primary"></mat-spinner>
</div>
<ng-template cnslHasRole [hasRole]="['policy.delete']">
<button
*ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
matTooltip="{{ 'POLICY.RESET' | translate }}"
color="warn"
(click)="resetPolicy()"
mat-stroked-button
>
{{ 'POLICY.RESET' | translate }}
</button>
</ng-template>
<form class="lifetime-form" (ngSubmit)="savePolicy()" [formGroup]="passwordAgeForm" autocomplete="off">
<cnsl-card *ngIf="passwordAgeData">
<div class="age-content">
<div class="row">
<cnsl-form-field class="pwd-age-form-field" required="true">
<cnsl-label>{{ 'POLICY.DATA.MAXAGEDAYS' | translate }}</cnsl-label>
<input cnslInput type="number" name="maxAgeDays" formControlName="maxAgeDays" />
</cnsl-form-field>
</div>
<div class="row">
<cnsl-form-field class="pwd-age-form-field" required="true">
<cnsl-label>{{ 'POLICY.DATA.EXPIREWARNDAYS' | translate }}</cnsl-label>
<input cnslInput type="number" name="expireWarnDays" formControlName="expireWarnDays" />
</cnsl-form-field>
</div>
</div>
</cnsl-card>
</form>
<div class="btn-container">
<button
(click)="savePolicy()"
[disabled]="(['policy.write'] | hasRole | async) === false"
color="primary"
type="submit"
mat-raised-button
>
{{ 'ACTIONS.SAVE' | translate }}
</button>
</div>

View File

@@ -0,0 +1,37 @@
.default {
display: block;
margin-bottom: 1rem;
}
.policy-applied-to {
margin: -1rem 0 0 0;
font-size: 14px;
}
.age-content {
display: flex;
flex-direction: column;
width: 100%;
.row {
display: flex;
align-items: center;
padding: 0.3rem 0;
.pwd-age-form-field {
width: 100%;
max-width: 300px;
}
}
}
.btn-container {
display: flex;
justify-content: flex-start;
width: 100%;
button {
display: block;
margin-right: 1rem;
}
}

View File

@@ -0,0 +1,24 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { PasswordAgePolicyComponent } from './password-age-policy.component';
describe('PasswordLockoutPolicyComponent', () => {
let component: PasswordAgePolicyComponent;
let fixture: ComponentFixture<PasswordAgePolicyComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [PasswordAgePolicyComponent],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PasswordAgePolicyComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,178 @@
import { Component, Injector, Input, OnInit, Type } from '@angular/core';
import { AbstractControl, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { GetPasswordAgePolicyResponse as AdminGetPasswordAgePolicyResponse } from 'src/app/proto/generated/zitadel/admin_pb';
import { GetPasswordAgePolicyResponse as MgmtGetPasswordAgePolicyResponse } from 'src/app/proto/generated/zitadel/management_pb';
import { PasswordAgePolicy } from 'src/app/proto/generated/zitadel/policy_pb';
import { AdminService } from 'src/app/services/admin.service';
import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service';
import { InfoSectionType } from '../../info-section/info-section.component';
import { WarnDialogComponent } from '../../warn-dialog/warn-dialog.component';
import { PolicyComponentServiceType } from '../policy-component-types.enum';
import { requiredValidator } from '../../form-field/validators/validators';
import { Observable } from 'rxjs';
import { GrpcAuthService } from '../../../services/grpc-auth.service';
import { take } from 'rxjs/operators';
@Component({
selector: 'cnsl-password-age-policy',
templateUrl: './password-age-policy.component.html',
styleUrls: ['./password-age-policy.component.scss'],
})
export class PasswordAgePolicyComponent implements OnInit {
@Input() public service!: ManagementService | AdminService;
@Input() public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
public passwordAgeForm!: UntypedFormGroup;
public passwordAgeData?: PasswordAgePolicy.AsObject;
public PolicyComponentServiceType: any = PolicyComponentServiceType;
public InfoSectionType: any = InfoSectionType;
public loading: boolean = false;
public canWrite$: Observable<boolean> = this.authService.isAllowed([
this.serviceType === PolicyComponentServiceType.ADMIN
? 'iam.policy.write'
: this.serviceType === PolicyComponentServiceType.MGMT
? 'policy.write'
: '',
]);
constructor(
private authService: GrpcAuthService,
private toast: ToastService,
private injector: Injector,
private dialog: MatDialog,
private fb: UntypedFormBuilder,
) {
this.passwordAgeForm = this.fb.group({
maxAgeDays: ['', []],
expireWarnDays: ['', []],
});
this.canWrite$.pipe(take(1)).subscribe((canWrite) => {
if (canWrite) {
this.passwordAgeForm.enable();
} else {
this.passwordAgeForm.disable();
}
});
}
public ngOnInit(): void {
switch (this.serviceType) {
case PolicyComponentServiceType.MGMT:
this.service = this.injector.get(ManagementService as Type<ManagementService>);
break;
case PolicyComponentServiceType.ADMIN:
this.service = this.injector.get(AdminService as Type<AdminService>);
break;
}
this.fetchData();
}
private fetchData(): void {
this.loading = true;
this.getData().then((resp) => {
if (resp.policy) {
this.passwordAgeData = resp.policy;
this.passwordAgeForm.patchValue(this.passwordAgeData);
this.loading = false;
}
});
}
private getData(): Promise<AdminGetPasswordAgePolicyResponse.AsObject | MgmtGetPasswordAgePolicyResponse.AsObject> {
switch (this.serviceType) {
case PolicyComponentServiceType.MGMT:
return (this.service as ManagementService).getPasswordAgePolicy();
case PolicyComponentServiceType.ADMIN:
return (this.service as AdminService).getPasswordAgePolicy();
}
}
public resetPolicy(): void {
if (this.service instanceof ManagementService) {
const dialogRef = this.dialog.open(WarnDialogComponent, {
data: {
confirmKey: 'ACTIONS.RESET',
cancelKey: 'ACTIONS.CANCEL',
titleKey: 'SETTING.DIALOG.RESET.DEFAULTTITLE',
descriptionKey: 'SETTING.DIALOG.RESET.DEFAULTDESCRIPTION',
},
width: '400px',
});
dialogRef.afterClosed().subscribe((resp) => {
if (resp) {
(this.service as ManagementService)
.resetPasswordAgePolicyToDefault()
.then(() => {
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
this.fetchData();
})
.catch((error) => {
this.toast.showError(error);
});
}
});
}
}
public savePolicy(): void {
let promise: Promise<any>;
if (this.passwordAgeData) {
if (this.service instanceof AdminService) {
promise = this.service
.updatePasswordAgePolicy(this.maxAgeDays?.value ?? 0, this.expireWarnDays?.value ?? 0)
.then(() => {
this.toast.showInfo('POLICY.TOAST.SET', true);
this.fetchData();
})
.catch((error) => {
this.toast.showError(error);
});
} else {
if ((this.passwordAgeData as PasswordAgePolicy.AsObject).isDefault) {
promise = (this.service as ManagementService)
.addCustomPasswordAgePolicy(this.maxAgeDays?.value ?? 0, this.expireWarnDays?.value ?? 0)
.then(() => {
this.toast.showInfo('POLICY.TOAST.SET', true);
this.fetchData();
})
.catch((error) => {
this.toast.showError(error);
});
} else {
promise = (this.service as ManagementService)
.updateCustomPasswordAgePolicy(this.maxAgeDays?.value ?? 0, this.expireWarnDays?.value ?? 0)
.then(() => {
this.toast.showInfo('POLICY.TOAST.SET', true);
this.fetchData();
})
.catch((error) => {
this.toast.showError(error);
});
}
}
}
}
public get isDefault(): boolean {
if (this.passwordAgeData && this.serviceType === PolicyComponentServiceType.MGMT) {
return (this.passwordAgeData as PasswordAgePolicy.AsObject).isDefault;
} else {
return false;
}
}
public get maxAgeDays(): AbstractControl | null {
return this.passwordAgeForm.get('maxAgeDays');
}
public get expireWarnDays(): AbstractControl | null {
return this.passwordAgeForm.get('expireWarnDays');
}
}

View File

@@ -0,0 +1,46 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatDialogModule } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { CardModule } from '../../card/card.module';
import { InfoSectionModule } from '../../info-section/info-section.module';
import { WarnDialogModule } from '../../warn-dialog/warn-dialog.module';
import { PasswordAgePolicyRoutingModule } from './password-age-policy-routing.module';
import { PasswordAgePolicyComponent } from './password-age-policy.component';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
@NgModule({
declarations: [PasswordAgePolicyComponent],
imports: [
PasswordAgePolicyRoutingModule,
CommonModule,
FormsModule,
InputModule,
MatButtonModule,
MatSlideToggleModule,
HasRolePipeModule,
MatDialogModule,
WarnDialogModule,
MatIconModule,
HasRoleModule,
MatTooltipModule,
CardModule,
TranslateModule,
DetailLayoutModule,
InfoSectionModule,
ReactiveFormsModule,
MatProgressSpinnerModule,
],
exports: [PasswordAgePolicyComponent],
})
export class PasswordAgePolicyModule {}

View File

@@ -18,6 +18,9 @@
<ng-container *ngIf="currentSetting === 'complexity'">
<cnsl-password-complexity-policy [serviceType]="serviceType"></cnsl-password-complexity-policy>
</ng-container>
<ng-container *ngIf="currentSetting === 'age'">
<cnsl-password-age-policy [serviceType]="serviceType"></cnsl-password-age-policy>
</ng-container>
<ng-container *ngIf="currentSetting === 'lockout'">
<cnsl-password-lockout-policy [serviceType]="serviceType"></cnsl-password-lockout-policy>
</ng-container>

View File

@@ -16,6 +16,7 @@ import { NotificationPolicyModule } from '../policies/notification-policy/notifi
import { NotificationSMSProviderModule } from '../policies/notification-sms-provider/notification-sms-provider.module';
import { OIDCConfigurationModule } from '../policies/oidc-configuration/oidc-configuration.module';
import { PasswordComplexityPolicyModule } from '../policies/password-complexity-policy/password-complexity-policy.module';
import { PasswordAgePolicyModule } from '../policies/password-age-policy/password-age-policy.module';
import { PasswordLockoutPolicyModule } from '../policies/password-lockout-policy/password-lockout-policy.module';
import { PrivacyPolicyModule } from '../policies/privacy-policy/privacy-policy.module';
import { PrivateLabelingPolicyModule } from '../policies/private-labeling-policy/private-labeling-policy.module';
@@ -40,6 +41,7 @@ import OrgListModule from 'src/app/pages/org-list/org-list.module';
LoginPolicyModule,
CardModule,
PasswordComplexityPolicyModule,
PasswordAgePolicyModule,
PasswordLockoutPolicyModule,
PrivateLabelingPolicyModule,
LanguageSettingsModule,

View File

@@ -117,6 +117,16 @@ export const LOCKOUT: SidenavSetting = {
},
};
export const AGE: SidenavSetting = {
id: 'age',
i18nKey: 'SETTINGS.LIST.AGE',
groupI18nKey: 'SETTINGS.GROUPS.LOGIN',
requiredRoles: {
[PolicyComponentServiceType.MGMT]: ['policy.read'],
[PolicyComponentServiceType.ADMIN]: ['iam.policy.read'],
},
};
export const COMPLEXITY: SidenavSetting = {
id: 'complexity',
i18nKey: 'SETTINGS.LIST.COMPLEXITY',

View File

@@ -18,6 +18,7 @@ import {
LANGUAGES,
IDP,
LOCKOUT,
AGE,
LOGIN,
LOGINTEXTS,
MESSAGETEXTS,
@@ -64,6 +65,7 @@ export class InstanceComponent implements OnInit, OnDestroy {
LOGIN,
IDP,
COMPLEXITY,
AGE,
LOCKOUT,
DOMAIN,

View File

@@ -7,6 +7,7 @@ import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
import {
AGE,
BRANDING,
COMPLEXITY,
DOMAIN,
@@ -33,6 +34,7 @@ export class OrgSettingsComponent implements OnInit {
LOGIN,
IDP,
COMPLEXITY,
AGE,
LOCKOUT,
NOTIFICATIONS,
VERIFIED_DOMAINS,

View File

@@ -1319,6 +1319,7 @@
"LANGUAGES": "Езици",
"LOGIN": "Поведение при влизане и сигурност",
"LOCKOUT": "Блокиране",
"AGE": "Изтичане на паролата",
"COMPLEXITY": "Сложност на паролата",
"NOTIFICATIONS": "Настройки за известията",
"SMTP_PROVIDER": "SMTP доставчик",
@@ -1539,8 +1540,8 @@
}
},
"PWD_AGE": {
"TITLE": "Остаряване на паролата",
"DESCRIPTION": "Можете да зададете политика за остаряването на паролите. "
"TITLE": "Изтичане на паролата",
"DESCRIPTION": "Можете да зададете политика за изтичане на паролите. Тази политика ще принуди потребителя да смени паролата при следващото влизане след изтичането. Няма автоматични предупреждения и известия."
},
"PWD_LOCKOUT": {
"TITLE": "Политика за блокиране",
@@ -1693,8 +1694,8 @@
"SHOWLOCKOUTFAILURES": "показва грешки при блокиране",
"MAXPASSWORDATTEMPTS": "Максимален брой опити за парола",
"MAXOTPATTEMPTS": "Максимален брой опити за OTP",
"EXPIREWARNDAYS": "Предупреждение за изтичане след ден",
"MAXAGEDAYS": "Максимална възраст в дни",
"EXPIREWARNDAYS": "Предупреждение за изтичане след дни",
"MAXAGEDAYS": "Максимална валидност в дни",
"USERLOGINMUSTBEDOMAIN": "Добавяне на домейн на организация като суфикс към имената за вход",
"USERLOGINMUSTBEDOMAIN_DESCRIPTION": "Ако активирате тази настройка, всички имена за вход ще имат суфикс с домейна на организацията. ",
"VALIDATEORGDOMAINS": "Верификация на домейна на организацията е необходима (DNS или HTTP предизвикателство)",

View File

@@ -1326,6 +1326,7 @@
"LANGUAGES": "Jazyky",
"LOGIN": "Chování při přihlášení a bezpečnost",
"LOCKOUT": "Blokování",
"AGE": "Expirace hesla",
"COMPLEXITY": "Složitost hesla",
"NOTIFICATIONS": "Oznámení",
"SMTP_PROVIDER": "Poskytovatel SMTP",
@@ -1546,8 +1547,8 @@
}
},
"PWD_AGE": {
"TITLE": "Stáří hesla",
"DESCRIPTION": "Můžete nastavit politiku pro stáří hesel. Tato politika vydá varování po uplynutí konkrétního času stáří."
"TITLE": "Expirace hesla",
"DESCRIPTION": "Můžete nastavit pravidlo pro vypršení platnosti hesel. Toto pravidlo donutí uživatele změnit heslo při dalším přihlášení po uplynutí platnosti. Neexistují žádná automatická varování a upozornění."
},
"PWD_LOCKOUT": {
"TITLE": "Politika uzamčení",
@@ -1700,8 +1701,8 @@
"SHOWLOCKOUTFAILURES": "zobrazit neúspěšné pokusy o uzamčení",
"MAXPASSWORDATTEMPTS": "Maximální počet pokusů o heslo",
"MAXOTPATTEMPTS": "Maximální počet pokusů o OTP",
"EXPIREWARNDAYS": "Upozornění na expiraci po dni",
"MAXAGEDAYS": "Maximální stáří v dnech",
"EXPIREWARNDAYS": "Upozornění na uplynutí po dnech",
"MAXAGEDAYS": "Maximální platnost v dnech",
"USERLOGINMUSTBEDOMAIN": "Přidat doménu organizace jako příponu k přihlašovacím jménům",
"USERLOGINMUSTBEDOMAIN_DESCRIPTION": "Pokud povolíte toto nastavení, všechna přihlašovací jména budou doplněna o doménu organizace. Pokud je toto nastavení zakázáno, musíte zajistit, aby byla uživatelská jména jedinečná napříč všemi organizacemi.",
"VALIDATEORGDOMAINS": "Požadováno ověření domény organizace (DNS nebo HTTP výzva)",

View File

@@ -1325,6 +1325,7 @@
"LANGUAGES": "Sprachen",
"LOGIN": "Loginverhalten und Sicherheit",
"LOCKOUT": "Sperrmechanismen",
"AGE": "Passwortgültigkeitsdauer",
"COMPLEXITY": "Passwordkomplexität",
"NOTIFICATIONS": "Benachrichtigungseinstellungen",
"SMTP_PROVIDER": "SMTP-Anbieter",
@@ -1546,7 +1547,7 @@
},
"PWD_AGE": {
"TITLE": "Gültigkeitsdauer für Passwörter",
"DESCRIPTION": "Du kannst eine Richtlinie für die maximale Gültigkeitsdauer von Passwörtern festlegen. Diese Richtlinie löst eine Warnung nach Ablauf einer festgelegten Gültigkeitsdauer aus."
"DESCRIPTION": "Du kannst eine Richtlinie für die maximale Gültigkeitsdauer von Passwörtern festlegen. Diese Richtlinie zwingt den Benutzer dazu, das Passwort bei der nächsten Anmeldung nach dem Ablauf zu ändern. Es gibt keine automatischen Warnungen und Benachrichtigungen."
},
"PWD_LOCKOUT": {
"TITLE": "Passwortsperre",

View File

@@ -1326,6 +1326,7 @@
"LANGUAGES": "Languages",
"LOGIN": "Login Behavior and Security",
"LOCKOUT": "Lockout",
"AGE": "Password expiry",
"COMPLEXITY": "Password complexity",
"NOTIFICATIONS": "Notifications",
"SMTP_PROVIDER": "SMTP Provider",
@@ -1546,8 +1547,8 @@
}
},
"PWD_AGE": {
"TITLE": "Password Aging",
"DESCRIPTION": "You can set a policy for the aging of passwords. This policy emits a warning after the specific aging time has elapsed."
"TITLE": "Password Expiry",
"DESCRIPTION": "You can set a policy for the expiry of passwords. This policy will force the user to change the password on the next login after the expiration. There are no automatic warnings and notifications."
},
"PWD_LOCKOUT": {
"TITLE": "Lockout Policy",
@@ -1700,8 +1701,8 @@
"SHOWLOCKOUTFAILURES": "show lockout failures",
"MAXPASSWORDATTEMPTS": "Password maximum attempts",
"MAXOTPATTEMPTS": "OTP maximum attempts",
"EXPIREWARNDAYS": "Expiration Warning after day",
"MAXAGEDAYS": "Max Age in days",
"EXPIREWARNDAYS": "Expiration Warning after days",
"MAXAGEDAYS": "Maximum validity in days",
"USERLOGINMUSTBEDOMAIN": "Add organization domain as suffix to loginnames",
"USERLOGINMUSTBEDOMAIN_DESCRIPTION": "If you enable this setting, all loginnames will be suffixed with the organization domain. If this settings is disabled, you have to ensure that usernames are unique over all organizations.",
"VALIDATEORGDOMAINS": "Organization domain verification required (DNS or HTTP challenge)",

View File

@@ -1327,6 +1327,7 @@
"LANGUAGES": "Idiomas",
"LOGIN": "Comportamiento del inicio de sesión y de la seguridad",
"LOCKOUT": "Bloqueo",
"AGE": "Caducidad de la contraseña",
"COMPLEXITY": "Complejidad de contraseña",
"NOTIFICATIONS": "Ajustes de notificación",
"SMTP_PROVIDER": "Proveedor SMTP",
@@ -1547,8 +1548,8 @@
}
},
"PWD_AGE": {
"TITLE": "Antigüedad de la contraseña",
"DESCRIPTION": "Puedes establecer una política para la antigüedad de las contraseñas. Esta política emite un aviso después de que la antigüedad máxima se haya superado."
"TITLE": "Caducidad de la contraseña",
"DESCRIPTION": "Puedes establecer una política para la caducidad de las contraseñas. Esta política obligará al usuario a cambiar la contraseña en el próximo inicio de sesión después de la caducidad. No hay avisos ni notificaciones automáticos."
},
"PWD_LOCKOUT": {
"TITLE": "Política de bloqueo",
@@ -1701,8 +1702,8 @@
"SHOWLOCKOUTFAILURES": "mostrar fallos de bloqueo",
"MAXPASSWORDATTEMPTS": "Intentos máximos de contraseña",
"MAXOTPATTEMPTS": "Intentos máximos de OTP",
"EXPIREWARNDAYS": "Aviso de expiración después de estos días: ",
"MAXAGEDAYS": "Antigüedad máxima en días",
"EXPIREWARNDAYS": "Aviso de caducidad después de días",
"MAXAGEDAYS": "Validez máxima en días",
"USERLOGINMUSTBEDOMAIN": "Añadir el dominio de la organización como sufijo de los nombres de inicio de sesión",
"USERLOGINMUSTBEDOMAIN_DESCRIPTION": "Si activas esta opción, todos los nombres de inicio de sesión tendrán como sufijo el dominio de esta organización. Si esta opción está desactivada, tendrás que asegurarte de que los nombres de usuario son únicos para todas las organizaciones.",
"VALIDATEORGDOMAINS": "Verificación de dominio de la organización requerida (desafío DNS o HTTP)",

View File

@@ -1325,6 +1325,7 @@
"LANGUAGES": "Langues",
"LOGIN": "Comportement de connexion et sécurité",
"LOCKOUT": "Verrouillage",
"AGE": "Expiration du mot de passe",
"COMPLEXITY": "Complexité du mot de passe",
"NOTIFICATIONS": "Paramètres de notification",
"SMTP_PROVIDER": "Fournisseur SMTP",
@@ -1545,8 +1546,8 @@
}
},
"PWD_AGE": {
"TITLE": "Vieillissement des mots de passe",
"DESCRIPTION": "Vous pouvez définir une politique pour le vieillissement des mots de passe. Cette politique émet un avertissement après que le temps de vieillissement spécifique se soit écoulé."
"TITLE": "Expiration du mot de passe",
"DESCRIPTION": "Vous pouvez définir une politique d'expiration des mots de passe. Cette politique obligera l'utilisateur à changer son mot de passe lors de la prochaine connexion après l'expiration. Il n'y a pas d'avertissements ni de notifications automatiques."
},
"PWD_LOCKOUT": {
"TITLE": "Politique de verrouillage",
@@ -1699,8 +1700,8 @@
"SHOWLOCKOUTFAILURES": "montrer les échecs de verrouillage",
"MAXPASSWORDATTEMPTS": "Tentatives maximales du mot de passe",
"MAXOTPATTEMPTS": "Tentatives maximales de l'OTP",
"EXPIREWARNDAYS": "Avertissement d'expiration après le jour",
"MAXAGEDAYS": "Âge maximum en jours",
"EXPIREWARNDAYS": "Avertissement d'expiration après jours",
"MAXAGEDAYS": "Validité maximale en jours",
"USERLOGINMUSTBEDOMAIN": "Le nom de connexion de l'utilisateur doit contenir le nom de domaine de l'organisation",
"USERLOGINMUSTBEDOMAIN_DESCRIPTION": "Si vous activez ce paramètre, tous les noms de connexion seront suffixés avec le domaine de l'organisation. Si ce paramètre est désactivé, vous devez vous assurer que les noms d'utilisateur sont uniques pour toutes les organisations.",
"VALIDATEORGDOMAINS": "Vérification du domaine de l'organisation requise (challenge DNS ou HTTP)",

View File

@@ -1325,6 +1325,7 @@
"LANGUAGES": "Lingue",
"LOGIN": "Comportamento login e sicurezza",
"LOCKOUT": "Meccanismi di bloccaggio",
"AGE": "Scadenza password",
"COMPLEXITY": "Complessità della password",
"NOTIFICATIONS": "Impostazioni di notifica",
"SMTP_PROVIDER": "Fornitore SMTP",
@@ -1545,8 +1546,8 @@
}
},
"PWD_AGE": {
"TITLE": "Impostazioni di validità della password",
"DESCRIPTION": "È possibile impostare una impostazone per la validità delle password. Questa emette un avviso dopo che il tempo di invecchiamento specifico è trascorso."
"TITLE": "Scadenza password",
"DESCRIPTION": "Puoi impostare una policy per la scadenza delle password. Questa policy obbligherà l'utente a cambiare la password al prossimo accesso dopo la scadenza. Non ci sono avvisi e notifiche automatiche."
},
"PWD_LOCKOUT": {
"TITLE": "Impostazioni di blocco",
@@ -1699,8 +1700,8 @@
"SHOWLOCKOUTFAILURES": "mostra i fallimenti del blocco",
"MAXPASSWORDATTEMPTS": "Massimo numero di tentativi di password",
"MAXOTPATTEMPTS": "Massimo numero di tentativi di OTP",
"EXPIREWARNDAYS": "Avviso scadenza dopo il giorno",
"MAXAGEDAYS": "Lunghezza massima in giorni",
"EXPIREWARNDAYS": "Avviso di scadenza dopo giorni",
"MAXAGEDAYS": "Validità massima in giorni",
"USERLOGINMUSTBEDOMAIN": "Nome utente deve contenere il dominio dell' organizzazione",
"USERLOGINMUSTBEDOMAIN_DESCRIPTION": "Se abiliti questa impostazione, a tutti i nomi di accesso verrà aggiunto il suffisso del dominio dell'organizzazione. Se questa impostazione è disabilitata, devi assicurarti che i nomi utente siano univoci per tutte le organizzazioni.",
"VALIDATEORGDOMAINS": "Verifica del dominio dell'organizzazione richiesta (challenge DNS o HTTP)",

View File

@@ -1326,6 +1326,7 @@
"LANGUAGES": "一般設定",
"LOGIN": "ログイン動作とセキュリティ",
"LOCKOUT": "ロックアウト",
"AGE": "パスワードの有効期限",
"COMPLEXITY": "パスワードの複雑さ",
"NOTIFICATIONS": "通知設定",
"SMTP_PROVIDER": "SMTPプロバイダー",
@@ -1542,8 +1543,8 @@
}
},
"PWD_AGE": {
"TITLE": "パスワードエージング",
"DESCRIPTION": "パスワードエージングに関するポリシーを設定できます。このポリシーは、特定のエージング時間が経過した後に警告を発します。"
"TITLE": "パスワードの有効期限",
"DESCRIPTION": "パスワードの有効期限ポリシーを設定できます。 このポリシーにより、有効期限が切れた後にユーザーは次回のログイン時にパスワードを変更することを求められます。 自動的な警告や通知はない。"
},
"PWD_LOCKOUT": {
"TITLE": "ロックアウトポリシー",
@@ -1696,8 +1697,8 @@
"SHOWLOCKOUTFAILURES": "ロックアウトの失敗を表示する",
"MAXPASSWORDATTEMPTS": "パスワードの最大試行",
"MAXOTPATTEMPTS": "最大OTP試行回数",
"EXPIREWARNDAYS": "有効期限の翌日以降の警告",
"MAXAGEDAYS": "最大有効期限",
"EXPIREWARNDAYS": "数日後に有効期限が切れます",
"MAXAGEDAYS": "最大有効期限 (日数)",
"USERLOGINMUSTBEDOMAIN": "ログイン名の接尾辞として組織ドメインを追加する",
"USERLOGINMUSTBEDOMAIN_DESCRIPTION": "この設定を有効にすると、すべてのログイン名が組織ドメインで接尾辞が付けられます。この設定が無効になっている場合、ユーザー名がすべての組織で一意であることを確認する必要があります。",
"VALIDATEORGDOMAINS": "組織のドメイン検証が必要です (DNSまたはHTTPチャレンジ)",

View File

@@ -1327,6 +1327,7 @@
"LANGUAGES": "Општо",
"LOGIN": "Правила и безбедност при најава",
"LOCKOUT": "Забрана на пристап",
"AGE": "Истекување на лозинката",
"COMPLEXITY": "Сложеност на лозинката",
"NOTIFICATIONS": "Подесувања за известувања",
"SMTP_PROVIDER": "SMTP провајдер",
@@ -1547,8 +1548,8 @@
}
},
"PWD_AGE": {
"TITLE": "Важност на лозинка",
"DESCRIPTION": "Можете да поставите политика за истекување на лозинките. Оваа политика издава предупредување откако ќе помени одреденото време на истекување."
"TITLE": "Истекување на лозинката",
"DESCRIPTION": "Можете да поставите политика за истекување на лозинките. Оваа политика ќе го принуди корисникот да ја смени лозинката при следлогото влегување по истекувањето. Нема автоматски предупредувања и известувања."
},
"PWD_LOCKOUT": {
"TITLE": "Политика за забрана на пристап",
@@ -1701,8 +1702,8 @@
"SHOWLOCKOUTFAILURES": "прикажи неуспешни заклучувања",
"MAXPASSWORDATTEMPTS": "Максимален број на обиди за лозинка",
"MAXOTPATTEMPTS": "Максимални обиди за OTP",
"EXPIREWARNDAYS": "Предупредување за истекување по ден",
"MAXAGEDAYS": "Максимална возраст во денови",
"EXPIREWARNDAYS": "Предупредување за истекување по денови",
"MAXAGEDAYS": "Максимална валидност во денови",
"USERLOGINMUSTBEDOMAIN": "Додади организациски домен како суфикс на корисничките имиња",
"USERLOGINMUSTBEDOMAIN_DESCRIPTION": "Ако го овозможите ова подесување, сите кориснички имиња ќе имаат суфикс на организацискиот домен. Доколку ова подесување е оневозможено, морате да се осигурате дека корисничките имиња се уникатни низ сите организации.",
"VALIDATEORGDOMAINS": "Потврда на доменот на организацијата е неопходна (DNS или HTTP предизвик)",

View File

@@ -1326,6 +1326,7 @@
"LANGUAGES": "Talen",
"LOGIN": "Login Gedrag en Beveiliging",
"LOCKOUT": "Lockout",
"AGE": "Wachtwoord verloopt",
"COMPLEXITY": "Wachtwoord complexiteit",
"NOTIFICATIONS": "Notificaties",
"SMTP_PROVIDER": "SMTP Provider",
@@ -1546,8 +1547,8 @@
}
},
"PWD_AGE": {
"TITLE": "Wachtwoord Veroudering",
"DESCRIPTION": "U kunt een beleid instellen voor de veroudering van wachtwoorden. Dit beleid geeft een waarschuwing nadat de specifieke verouderingstijd is verstreken."
"TITLE": "Wachtwoord verloopt",
"DESCRIPTION": "U kunt een beleid instellen voor het verlopen van wachtwoorden. Dit beleid dwingt de gebruiker om het wachtwoord te wijzigen bij de volgende aanmelding na het verlopen. Er zijn geen automatische waarschuwingen en meldingen."
},
"PWD_LOCKOUT": {
"TITLE": "Lockout Beleid",
@@ -1700,8 +1701,8 @@
"SHOWLOCKOUTFAILURES": "toon lockout mislukkingen",
"MAXPASSWORDATTEMPTS": "Maximum pogingen voor wachtwoord",
"MAXOTPATTEMPTS": "Maximale OTP-pogingen",
"EXPIREWARNDAYS": "Vervaldatum Waarschuwing na dag",
"MAXAGEDAYS": "Maximale Leeftijd in dagen",
"EXPIREWARNDAYS": "Waarschuwing voor verlopen na dagen",
"MAXAGEDAYS": "Maximale geldigheid in dagen",
"USERLOGINMUSTBEDOMAIN": "Voeg organisatie domein toe als achtervoegsel aan inlognamen",
"USERLOGINMUSTBEDOMAIN_DESCRIPTION": "Als u deze instelling inschakelt, worden alle inlognamen voorzien van een achtervoegsel met het domein van de organisatie. Als deze instelling is uitgeschakeld, moet u ervoor zorgen dat gebruikersnamen uniek zijn over alle organisaties.",
"VALIDATEORGDOMAINS": "Verificatie van organisatiedomeinen vereist (DNS of HTTP-uitdaging)",

View File

@@ -1325,6 +1325,7 @@
"LANGUAGES": "Języki",
"LOGIN": "Zachowanie logowania i bezpieczeństwo",
"LOCKOUT": "Blokada",
"AGE": "Wygaśnięcie hasła",
"COMPLEXITY": "Złożoność hasła",
"NOTIFICATIONS": "Ustawienia powiadomień",
"SMTP_PROVIDER": "Dostawca SMTP",
@@ -1545,8 +1546,8 @@
}
},
"PWD_AGE": {
"TITLE": "Starzenie się hasła",
"DESCRIPTION": "Możesz ustawić politykę dotyczącą starzenia się haseł. Ta polityka emituje ostrzeżenie po upływie określonego czasu starzenia."
"TITLE": "Wygaśnięcie hasła",
"DESCRIPTION": "Możesz ustawić zasady wygasania haseł. Ta zasada zmusi użytkownika do zmiany hasła przy następnym logowaniu po jego wygaśnięciu. Nie ma automatycznych ostrzeżeń i powiadomień."
},
"PWD_LOCKOUT": {
"TITLE": "Polityka blokowania",
@@ -1699,8 +1700,8 @@
"SHOWLOCKOUTFAILURES": "pokaż blokady nieudanych prób",
"MAXPASSWORDATTEMPTS": "Maksymalna liczba prób wprowadzenia hasła",
"MAXOTPATTEMPTS": "Maksymalna liczba prób OTP",
"EXPIREWARNDAYS": "Ostrzeżenie o wygaśnięciu po dniu",
"MAXAGEDAYS": "Maksymalny wiek w dniach",
"EXPIREWARNDAYS": "Ostrzeżenie o wygaśnięciu po dniach",
"MAXAGEDAYS": "Maksymalna ważność w dniach",
"USERLOGINMUSTBEDOMAIN": "Dodaj domenę organizacji jako przyrostek do nazw logowania",
"USERLOGINMUSTBEDOMAIN_DESCRIPTION": "Jeśli włączysz to ustawienie, wszystkie nazwy logowania będą miały przyrostek z domeną organizacji. Jeśli to ustawienie jest wyłączone, musisz zapewnić unikalność nazw użytkowników we wszystkich organizacjach.",
"VALIDATEORGDOMAINS": "Weryfikacja domeny organizacji jest wymagana (wyzwanie DNS lub HTTP)",

View File

@@ -1327,6 +1327,7 @@
"LANGUAGES": "Idiomas",
"LOGIN": "Comportamento de Login e Segurança",
"LOCKOUT": "Bloqueio",
"AGE": "Expiração da senha",
"COMPLEXITY": "Complexidade de Senha",
"NOTIFICATIONS": "Configurações de Notificação",
"SMTP_PROVIDER": "Provedor SMTP",
@@ -1547,8 +1548,8 @@
}
},
"PWD_AGE": {
"TITLE": "Envelhecimento de senha",
"DESCRIPTION": "Você pode definir uma política para o envelhecimento de senhas. Essa política emite um aviso após o tempo de envelhecimento específico ter passado."
"TITLE": "Expiração da senha",
"DESCRIPTION": "Você pode definir uma política para a expiração de senhas. Esta política forçará o usuário a alterar a senha no próximo login após a expiração. Não existem avisos e notificações automáticas."
},
"PWD_LOCKOUT": {
"TITLE": "Política de bloqueio",
@@ -1702,7 +1703,7 @@
"MAXPASSWORDATTEMPTS": "Máximo de tentativas de senha",
"MAXOTPATTEMPTS": "Máximo de tentativas de OTP",
"EXPIREWARNDAYS": "Aviso de expiração após dias",
"MAXAGEDAYS": "Idade máxima em dias",
"MAXAGEDAYS": "Validade máxima em dias",
"USERLOGINMUSTBEDOMAIN": "Adicionar domínio da organização como sufixo aos nomes de login",
"USERLOGINMUSTBEDOMAIN_DESCRIPTION": "Se você habilitar essa configuração, todos os nomes de login serão sufixados com o domínio da organização. Se essa configuração estiver desabilitada, você deve garantir que os nomes de usuário sejam exclusivos em todas as organizações.",
"VALIDATEORGDOMAINS": "Verificação de domínio da organização necessária (desafio DNS ou HTTP)",

View File

@@ -1373,6 +1373,7 @@
"LANGUAGES": "Языки",
"LOGIN": "Действия при входе и безопасность",
"LOCKOUT": "Блокировка",
"AGE": "Срок действия пароля",
"COMPLEXITY": "Сложность пароля",
"NOTIFICATIONS": "Настройки уведомлений",
"NOTIFICATIONS_DESC": "Настройки SMTP и SMS",
@@ -1596,7 +1597,7 @@
},
"PWD_AGE": {
"TITLE": "Срок действия пароля",
"DESCRIPTION": "Вы можете установить политику срока действия паролей. Данная политика предупреждает об истечении определённого времени срока действия."
"DESCRIPTION": "Вы можете установить политику истечения срока действия паролей. Эта политика вынудит пользователя изменить пароль при следующем входе в систему после истечения срока его действия. Нет никаких автоматических предупреждений и уведомлений."
},
"PWD_LOCKOUT": {
"TITLE": "Политика блокировки",
@@ -1767,8 +1768,8 @@
"SHOWLOCKOUTFAILURES": "Показать ошибки блокировки",
"MAXPASSWORDATTEMPTS": "Максимальное количество попыток пароля",
"MAXOTPATTEMPTS": "Максимальное количество попыток OTP",
"EXPIREWARNDAYS": "Предупреждение об истечении срока действия после дня",
"MAXAGEDAYS": "Максимальный возраст в днях",
"EXPIREWARNDAYS": "Предупреждение об истечении срока действия через несколько дней",
"MAXAGEDAYS": "Максимальная продолжительность действия (дни)",
"USERLOGINMUSTBEDOMAIN": "Добавить домен организации в качестве суффикса к именам логина",
"USERLOGINMUSTBEDOMAIN_DESCRIPTION": "Если вы включите данный параметр, все имена входа будут иметь суффикс домена организации. Если данный параметр отключен, вы должны убедиться, что имена пользователей уникальны для всех организаций.",
"VALIDATEORGDOMAINS": "Проверка доменов организации",

View File

@@ -1326,6 +1326,7 @@
"LANGUAGES": "Språk",
"LOGIN": "Inloggningsbeteende och säkerhet",
"LOCKOUT": "Låsning",
"AGE": "Lösenordets utgång",
"COMPLEXITY": "Lösenordskomplexitet",
"NOTIFICATIONS": "Meddelanden",
"SMTP_PROVIDER": "SMTP-leverantör",
@@ -1546,8 +1547,8 @@
}
},
"PWD_AGE": {
"TITLE": "Lösenordsåldrande",
"DESCRIPTION": "Du kan ställa in en policy för lösenordsåldrande. Denna policy avger en varning efter den specifika åldringstiden har passerat."
"TITLE": "Lösenordets utgång",
"DESCRIPTION": "Du kan ställa in en policy för utgångsdatum för lösenord. Den här policyn tvingar användaren att byta lösenord vid nästa inloggning efter att lösenordet har löpt ut. Det finns inga automatiska varningar eller meddelanden."
},
"PWD_LOCKOUT": {
"TITLE": "Låsning av lösenordspolicy",
@@ -1700,8 +1701,8 @@
"SHOWLOCKOUTFAILURES": "visa låsning misslyckanden",
"MAXPASSWORDATTEMPTS": "Maximalt antal lösenordsförsök",
"MAXOTPATTEMPTS": "Maximalt antal OTP-försök",
"EXPIREWARNDAYS": "Utgångsvarning efter dag",
"MAXAGEDAYS": "Max ålder i dagar",
"EXPIREWARNDAYS": "Utgångsvarning efter dagar",
"MAXAGEDAYS": "Maximal giltighetstid i dagar",
"USERLOGINMUSTBEDOMAIN": "Lägg till organisationsdomän som suffix till inloggningsnamn",
"USERLOGINMUSTBEDOMAIN_DESCRIPTION": "Om du aktiverar denna inställning kommer alla inloggningsnamn att ha organisationsdomänen som suffix. Om denna inställning är inaktiverad måste du säkerställa att användarnamn är unika över alla organisationer.",
"VALIDATEORGDOMAINS": "Organisationsdomänverifiering krävs (DNS eller HTTP-utmaning)",

View File

@@ -1325,6 +1325,7 @@
"LANGUAGES": "语言",
"LOGIN": "登录行为和安全",
"LOCKOUT": "安全锁策略",
"AGE": "密码过期",
"COMPLEXITY": "密码复杂性",
"NOTIFICATIONS": "通知设置",
"SMTP_PROVIDER": "SMTP 提供商",
@@ -1545,7 +1546,7 @@
},
"PWD_AGE": {
"TITLE": "密码过期",
"DESCRIPTION": "您可以设置密码过期策略。此策略会在特定过期时间过后发出警告。"
"DESCRIPTION": "您可以设置密码过期策略。此策略将强制用户在密码过期后下次登录时更改密码。没有自动警告和通知。"
},
"PWD_LOCKOUT": {
"TITLE": "锁定策略",
@@ -1698,8 +1699,8 @@
"SHOWLOCKOUTFAILURES": "显示锁定失败",
"MAXPASSWORDATTEMPTS": "密码最大尝试次数",
"MAXOTPATTEMPTS": "最多尝试 OTP 次数",
"EXPIREWARNDAYS": "密码过期警告",
"MAXAGEDAYS": "Max Age in days",
"EXPIREWARNDAYS": "密码将在几天后过期",
"MAXAGEDAYS": "最大有效期 (天)",
"USERLOGINMUSTBEDOMAIN": "用户名必须包含组织域名",
"USERLOGINMUSTBEDOMAIN_DESCRIPTION": "如果启用此设置,所有登录名都将以组织域为后缀。如果禁用此设置,您必须确保用户名在所有组织中都是唯一的。",
"VALIDATEORGDOMAINS": "组织域名验证需要 (DNS 或 HTTP 挑战)",