mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 19:17:32 +00:00
feat: add notification policy and password change message (#5065)
Implementation of new notification policy with functionality to send email when a password is changed
This commit is contained in:
@@ -3,31 +3,39 @@ import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
|
||||
import { MatLegacySelectChange as MatSelectChange } from '@angular/material/legacy-select';
|
||||
import { BehaviorSubject, from, Observable, of, Subscription } from 'rxjs';
|
||||
import {
|
||||
GetCustomPasswordResetMessageTextRequest as AdminGetCustomPasswordResetMessageTextRequest,
|
||||
GetDefaultInitMessageTextRequest as AdminGetDefaultInitMessageTextRequest,
|
||||
GetDefaultVerifyEmailMessageTextRequest as AdminGetDefaultVerifyEmailMessageTextRequest,
|
||||
GetDefaultVerifyPhoneMessageTextRequest as AdminGetDefaultVerifyPhoneMessageTextRequest,
|
||||
SetDefaultDomainClaimedMessageTextRequest,
|
||||
SetDefaultInitMessageTextRequest,
|
||||
SetDefaultPasswordChangeMessageTextRequest,
|
||||
SetDefaultPasswordlessRegistrationMessageTextRequest,
|
||||
SetDefaultPasswordResetMessageTextRequest,
|
||||
SetDefaultVerifyEmailMessageTextRequest,
|
||||
SetDefaultVerifyPhoneMessageTextRequest,
|
||||
GetDefaultPasswordChangeMessageTextRequest as AdminGetDefaultPasswordChangeMessageTextRequest,
|
||||
GetDefaultInitMessageTextRequest as AdminGetDefaultInitMessageTextRequest,
|
||||
GetDefaultVerifyEmailMessageTextRequest as AdminGetDefaultVerifyEmailMessageTextRequest,
|
||||
GetDefaultVerifyPhoneMessageTextRequest as AdminGetDefaultVerifyPhoneMessageTextRequest,
|
||||
GetDefaultPasswordResetMessageTextRequest as AdminGetDefaultPasswordResetMessageTextRequest,
|
||||
GetDefaultDomainClaimedMessageTextRequest as AdminGetDefaultDomainClaimedMessageTextRequest,
|
||||
GetDefaultPasswordlessRegistrationMessageTextRequest as AdminGetDefaultPasswordlessRegistrationMessageTextRequest,
|
||||
} from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import {
|
||||
GetCustomDomainClaimedMessageTextRequest,
|
||||
GetCustomInitMessageTextRequest,
|
||||
GetCustomPasswordChangeMessageTextRequest,
|
||||
GetCustomPasswordlessRegistrationMessageTextRequest,
|
||||
GetCustomPasswordResetMessageTextRequest,
|
||||
GetCustomVerifyEmailMessageTextRequest,
|
||||
GetCustomVerifyPhoneMessageTextRequest,
|
||||
GetDefaultDomainClaimedMessageTextRequest,
|
||||
GetDefaultInitMessageTextRequest,
|
||||
GetDefaultPasswordChangeMessageTextRequest,
|
||||
GetDefaultPasswordlessRegistrationMessageTextRequest,
|
||||
GetDefaultPasswordResetMessageTextRequest,
|
||||
GetDefaultVerifyEmailMessageTextRequest,
|
||||
GetDefaultVerifyPhoneMessageTextRequest,
|
||||
SetCustomDomainClaimedMessageTextRequest,
|
||||
SetCustomInitMessageTextRequest,
|
||||
SetCustomPasswordChangeMessageTextRequest,
|
||||
SetCustomPasswordlessRegistrationMessageTextRequest,
|
||||
SetCustomPasswordResetMessageTextRequest,
|
||||
SetCustomVerifyEmailMessageTextRequest,
|
||||
@@ -50,12 +58,30 @@ enum MESSAGETYPES {
|
||||
PASSWORDRESET = 'PR',
|
||||
DOMAINCLAIMED = 'DC',
|
||||
PASSWORDLESS = 'PL',
|
||||
PASSWORDCHANGE = 'PC',
|
||||
}
|
||||
|
||||
const REQUESTMAP = {
|
||||
[PolicyComponentServiceType.MGMT]: {
|
||||
[MESSAGETYPES.PASSWORDCHANGE]: {
|
||||
get: new GetCustomPasswordChangeMessageTextRequest(),
|
||||
set: new SetCustomPasswordChangeMessageTextRequest(),
|
||||
getDefault: new GetDefaultPasswordChangeMessageTextRequest(),
|
||||
setFcn: (map: Partial<MessageCustomText.AsObject>): SetCustomPasswordChangeMessageTextRequest => {
|
||||
const req = new SetCustomPasswordChangeMessageTextRequest();
|
||||
req.setButtonText(map.buttonText ?? '');
|
||||
req.setFooterText(map.footerText ?? '');
|
||||
req.setGreeting(map.greeting ?? '');
|
||||
req.setPreHeader(map.preHeader ?? '');
|
||||
req.setSubject(map.subject ?? '');
|
||||
req.setText(map.text ?? '');
|
||||
req.setTitle(map.title ?? '');
|
||||
|
||||
return req;
|
||||
},
|
||||
},
|
||||
[MESSAGETYPES.INIT]: {
|
||||
get: new GetDefaultInitMessageTextRequest(),
|
||||
get: new GetCustomInitMessageTextRequest(),
|
||||
set: new SetCustomInitMessageTextRequest(),
|
||||
getDefault: new GetDefaultInitMessageTextRequest(),
|
||||
setFcn: (map: Partial<MessageCustomText.AsObject>): SetCustomInitMessageTextRequest => {
|
||||
@@ -164,6 +190,22 @@ const REQUESTMAP = {
|
||||
},
|
||||
},
|
||||
[PolicyComponentServiceType.ADMIN]: {
|
||||
[MESSAGETYPES.PASSWORDCHANGE]: {
|
||||
get: new AdminGetDefaultPasswordChangeMessageTextRequest(),
|
||||
set: new SetDefaultPasswordChangeMessageTextRequest(),
|
||||
setFcn: (map: Partial<MessageCustomText.AsObject>): SetDefaultPasswordChangeMessageTextRequest => {
|
||||
const req = new SetDefaultPasswordChangeMessageTextRequest();
|
||||
req.setButtonText(map.buttonText ?? '');
|
||||
req.setFooterText(map.footerText ?? '');
|
||||
req.setGreeting(map.greeting ?? '');
|
||||
req.setPreHeader(map.preHeader ?? '');
|
||||
req.setSubject(map.subject ?? '');
|
||||
req.setText(map.text ?? '');
|
||||
req.setTitle(map.title ?? '');
|
||||
|
||||
return req;
|
||||
},
|
||||
},
|
||||
[MESSAGETYPES.INIT]: {
|
||||
get: new AdminGetDefaultInitMessageTextRequest(),
|
||||
set: new SetDefaultInitMessageTextRequest(),
|
||||
@@ -213,7 +255,7 @@ const REQUESTMAP = {
|
||||
},
|
||||
},
|
||||
[MESSAGETYPES.PASSWORDRESET]: {
|
||||
get: new AdminGetCustomPasswordResetMessageTextRequest(),
|
||||
get: new AdminGetDefaultPasswordResetMessageTextRequest(),
|
||||
set: new SetDefaultPasswordResetMessageTextRequest(),
|
||||
setFcn: (
|
||||
map: Partial<SetDefaultPasswordResetMessageTextRequest.AsObject>,
|
||||
@@ -231,7 +273,7 @@ const REQUESTMAP = {
|
||||
},
|
||||
},
|
||||
[MESSAGETYPES.DOMAINCLAIMED]: {
|
||||
get: new GetDefaultDomainClaimedMessageTextRequest(),
|
||||
get: new AdminGetDefaultDomainClaimedMessageTextRequest(),
|
||||
set: new SetDefaultDomainClaimedMessageTextRequest(),
|
||||
setFcn: (
|
||||
map: Partial<SetDefaultDomainClaimedMessageTextRequest.AsObject>,
|
||||
@@ -249,7 +291,7 @@ const REQUESTMAP = {
|
||||
},
|
||||
},
|
||||
[MESSAGETYPES.PASSWORDLESS]: {
|
||||
get: new GetDefaultPasswordlessRegistrationMessageTextRequest(),
|
||||
get: new AdminGetDefaultPasswordlessRegistrationMessageTextRequest(),
|
||||
set: new SetDefaultPasswordlessRegistrationMessageTextRequest(),
|
||||
setFcn: (
|
||||
map: Partial<SetDefaultPasswordlessRegistrationMessageTextRequest.AsObject>,
|
||||
@@ -382,6 +424,20 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.loginnames', value: '{{.LoginNames}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.changedate', value: '{{.ChangeDate}}' },
|
||||
],
|
||||
[MESSAGETYPES.PASSWORDCHANGE]: [
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.preferredLoginName', value: '{{.PreferredLoginName}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.username', value: '{{.UserName}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.firstname', value: '{{.FirstName}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.lastname', value: '{{.Lastname}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.nickName', value: '{{.NickName}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.displayName', value: '{{.DisplayName}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.lastEmail', value: '{{.LastEmail}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.verifiedEmail', value: '{{.VerifiedEmail}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.lastPhone', value: '{{.LastPhone}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.verifiedPhone', value: '{{.VerifiedPhone}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.loginnames', value: '{{.LoginNames}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.changedate', value: '{{.ChangeDate}}' },
|
||||
],
|
||||
};
|
||||
|
||||
public locale: string = 'en';
|
||||
@@ -435,6 +491,8 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
return this.stripDetails(this.service.getDefaultDomainClaimedMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDLESS:
|
||||
return this.stripDetails(this.service.getDefaultPasswordlessRegistrationMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDCHANGE:
|
||||
return this.stripDetails(this.service.getDefaultPasswordChangeMessageText(req));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -453,6 +511,9 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
return this.stripDetails((this.service as ManagementService).getCustomDomainClaimedMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDLESS:
|
||||
return this.stripDetails((this.service as ManagementService).getCustomPasswordlessRegistrationMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDCHANGE:
|
||||
return this.stripDetails((this.service as ManagementService).getCustomPasswordChangeMessageText(req));
|
||||
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
@@ -470,6 +531,8 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
return this.stripDetails((this.service as AdminService).getCustomDomainClaimedMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDLESS:
|
||||
return this.stripDetails((this.service as AdminService).getCustomPasswordlessRegistrationMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDCHANGE:
|
||||
return this.stripDetails((this.service as AdminService).getCustomPasswordChangeMessageText(req));
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
@@ -535,6 +598,8 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
return handler(
|
||||
(this.service as ManagementService).getCustomPasswordlessRegistrationMessageText(this.updateRequest),
|
||||
);
|
||||
case MESSAGETYPES.PASSWORDCHANGE:
|
||||
return handler((this.service as ManagementService).getCustomPasswordChangeMessageText(this.updateRequest));
|
||||
}
|
||||
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
|
||||
switch (this.currentType) {
|
||||
@@ -550,6 +615,8 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
return handler((this.service as AdminService).setDefaultDomainClaimedMessageText(this.updateRequest));
|
||||
case MESSAGETYPES.PASSWORDLESS:
|
||||
return handler((this.service as AdminService).setDefaultPasswordlessRegistrationMessageText(this.updateRequest));
|
||||
case MESSAGETYPES.PASSWORDCHANGE:
|
||||
return handler((this.service as AdminService).setDefaultPasswordChangeMessageText(this.updateRequest));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -595,6 +662,8 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
return handler(
|
||||
(this.service as ManagementService).resetCustomPasswordlessRegistrationMessageTextToDefault(this.locale),
|
||||
);
|
||||
case MESSAGETYPES.PASSWORDCHANGE:
|
||||
return handler((this.service as ManagementService).resetCustomPasswordChangeMessageTextToDefault(this.locale));
|
||||
default:
|
||||
return Promise.reject();
|
||||
}
|
||||
|
@@ -0,0 +1,49 @@
|
||||
<h2>{{ 'POLICY.NOTIFICATION.TITLE' | translate }}</h2>
|
||||
<p class="cnsl-secondary-text">{{ 'POLICY.NOTIFICATION.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)="removePolicy()"
|
||||
mat-stroked-button
|
||||
>
|
||||
{{ 'POLICY.RESET' | translate }}
|
||||
</button>
|
||||
</ng-template>
|
||||
|
||||
<div class="notification-policy-card">
|
||||
<cnsl-card *ngIf="notificationData">
|
||||
<div class="notification-policy-content">
|
||||
<div class="row">
|
||||
<mat-checkbox
|
||||
class="slide-toggle"
|
||||
color="primary"
|
||||
name="hasUppercase"
|
||||
ngDefaultControl
|
||||
[(ngModel)]="notificationData.passwordChange"
|
||||
[disabled]="(['policy.write'] | hasRole | async) === false"
|
||||
>
|
||||
{{ 'POLICY.NOTIFICATION.PASSWORDCHANGE' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</cnsl-card>
|
||||
</div>
|
||||
|
||||
<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>
|
@@ -0,0 +1,32 @@
|
||||
.spinner-wr {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.policy-applied-to {
|
||||
margin: -1rem 0 0 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.notification-policy-card {
|
||||
max-width: 400px;
|
||||
|
||||
.notification-policy-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-container {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
|
||||
button {
|
||||
display: block;
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { PasswordComplexityPolicyComponent } from './password-complexity-policy.component';
|
||||
|
||||
describe('PasswordComplexityPolicyComponent', () => {
|
||||
let component: PasswordComplexityPolicyComponent;
|
||||
let fixture: ComponentFixture<PasswordComplexityPolicyComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [PasswordComplexityPolicyComponent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PasswordComplexityPolicyComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,177 @@
|
||||
import { Component, Injector, Input, OnInit, Type } from '@angular/core';
|
||||
import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
|
||||
import {
|
||||
AddNotificationPolicyRequest,
|
||||
GetNotificationPolicyResponse as AdminGetNotificationPolicyResponse,
|
||||
UpdateNotificationPolicyRequest,
|
||||
} from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import {
|
||||
AddCustomNotificationPolicyRequest,
|
||||
GetNotificationPolicyResponse as MgmtGetNotificationPolicyResponse,
|
||||
} from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { NotificationPolicy } 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';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-notification-policy',
|
||||
templateUrl: './notification-policy.component.html',
|
||||
styleUrls: ['./notification-policy.component.scss'],
|
||||
})
|
||||
export class NotificationPolicyComponent implements OnInit {
|
||||
@Input() public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
public service!: ManagementService | AdminService;
|
||||
|
||||
public notificationData?: NotificationPolicy.AsObject = { isDefault: false, passwordChange: false };
|
||||
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
|
||||
public loading: boolean = false;
|
||||
public InfoSectionType: any = InfoSectionType;
|
||||
|
||||
public isDefault: boolean = false;
|
||||
private hasNotificationPolicy: boolean = false;
|
||||
constructor(private toast: ToastService, private injector: Injector, private dialog: MatDialog) {}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
public fetchData(): void {
|
||||
this.loading = true;
|
||||
|
||||
this.getData()
|
||||
.then((data) => {
|
||||
if (data.policy) {
|
||||
this.hasNotificationPolicy = true;
|
||||
this.notificationData = data.policy;
|
||||
this.isDefault = data.policy.isDefault;
|
||||
this.loading = false;
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.loading = false;
|
||||
if (error && error.code === 5) {
|
||||
console.log(error);
|
||||
this.hasNotificationPolicy = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async getData(): Promise<
|
||||
MgmtGetNotificationPolicyResponse.AsObject | AdminGetNotificationPolicyResponse.AsObject
|
||||
> {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return (this.service as ManagementService).getNotificationPolicy();
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return (this.service as AdminService).getNotificationPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
public removePolicy(): 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)
|
||||
.resetNotificationPolicyToDefault()
|
||||
.then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
|
||||
this.isDefault = true;
|
||||
setTimeout(() => {
|
||||
this.fetchData();
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public savePolicy(): void {
|
||||
if (this.notificationData) {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
if ((this.notificationData as NotificationPolicy.AsObject).isDefault) {
|
||||
const req = new AddCustomNotificationPolicyRequest();
|
||||
req.setPasswordChange(this.notificationData.passwordChange);
|
||||
(this.service as ManagementService)
|
||||
.addCustomNotificationPolicy(req)
|
||||
.then(() => {
|
||||
this.isDefault = false;
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else {
|
||||
const req = new UpdateNotificationPolicyRequest();
|
||||
req.setPasswordChange(this.notificationData.passwordChange);
|
||||
(this.service as ManagementService)
|
||||
.updateCustomNotificationPolicy(req)
|
||||
.then(() => {
|
||||
this.isDefault = false;
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
if (this.hasNotificationPolicy) {
|
||||
const req = new UpdateNotificationPolicyRequest();
|
||||
req.setPasswordChange(this.notificationData.passwordChange);
|
||||
(this.service as AdminService)
|
||||
.updateNotificationPolicy(req)
|
||||
.then(() => {
|
||||
this.isDefault = false;
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else {
|
||||
const req = new AddNotificationPolicyRequest();
|
||||
req.setPasswordChange(this.notificationData.passwordChange);
|
||||
(this.service as AdminService)
|
||||
.addNotificationPolicy(req)
|
||||
.then(() => {
|
||||
this.isDefault = false;
|
||||
this.hasNotificationPolicy = true;
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
|
||||
import { MatLegacyCheckboxModule as MatCheckboxModule } from '@angular/material/legacy-checkbox';
|
||||
import { MatLegacyDialogModule as MatDialogModule } from '@angular/material/legacy-dialog';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatLegacyProgressSpinnerModule as MatProgressSpinnerModule } from '@angular/material/legacy-progress-spinner';
|
||||
import { MatLegacyTooltipModule as MatTooltipModule } from '@angular/material/legacy-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 { NotificationPolicyComponent } from './notification-policy.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [NotificationPolicyComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
InputModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
HasRoleModule,
|
||||
MatDialogModule,
|
||||
MatTooltipModule,
|
||||
MatCheckboxModule,
|
||||
HasRolePipeModule,
|
||||
TranslateModule,
|
||||
WarnDialogModule,
|
||||
DetailLayoutModule,
|
||||
CardModule,
|
||||
MatProgressSpinnerModule,
|
||||
InfoSectionModule,
|
||||
],
|
||||
exports: [NotificationPolicyComponent],
|
||||
})
|
||||
export class NotificationPolicyModule {}
|
@@ -1,20 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { PasswordComplexityPolicyComponent } from './password-complexity-policy.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: PasswordComplexityPolicyComponent,
|
||||
data: {
|
||||
animation: 'DetailPage',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class PasswordComplexityPolicyRoutingModule {}
|
@@ -16,13 +16,11 @@ import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.mod
|
||||
import { CardModule } from '../../card/card.module';
|
||||
import { InfoSectionModule } from '../../info-section/info-section.module';
|
||||
import { WarnDialogModule } from '../../warn-dialog/warn-dialog.module';
|
||||
import { PasswordComplexityPolicyRoutingModule } from './password-complexity-policy-routing.module';
|
||||
import { PasswordComplexityPolicyComponent } from './password-complexity-policy.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [PasswordComplexityPolicyComponent],
|
||||
imports: [
|
||||
PasswordComplexityPolicyRoutingModule,
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
InputModule,
|
||||
|
@@ -25,9 +25,14 @@
|
||||
<cnsl-idp-settings [serviceType]="serviceType"></cnsl-idp-settings>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="currentSetting === 'notifications' && serviceType === PolicyComponentServiceType.ADMIN">
|
||||
<cnsl-notification-policy [serviceType]="serviceType"></cnsl-notification-policy>
|
||||
<cnsl-notification-settings [serviceType]="serviceType"></cnsl-notification-settings>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="currentSetting === 'notifications' && serviceType === PolicyComponentServiceType.MGMT">
|
||||
<cnsl-notification-policy [serviceType]="serviceType"></cnsl-notification-policy
|
||||
></ng-container>
|
||||
|
||||
<ng-container *ngIf="currentSetting === 'oidc' && serviceType === PolicyComponentServiceType.ADMIN">
|
||||
<cnsl-oidc-configuration></cnsl-oidc-configuration>
|
||||
</ng-container>
|
||||
|
@@ -11,6 +11,7 @@ import { IdpSettingsModule } from '../policies/idp-settings/idp-settings.module'
|
||||
import { LoginPolicyModule } from '../policies/login-policy/login-policy.module';
|
||||
import { LoginTextsPolicyModule } from '../policies/login-texts/login-texts.module';
|
||||
import { MessageTextsPolicyModule } from '../policies/message-texts/message-texts.module';
|
||||
import { NotificationPolicyModule } from '../policies/notification-policy/notification-policy.module';
|
||||
import { NotificationSettingsModule } from '../policies/notification-settings/notification-settings.module';
|
||||
import { OIDCConfigurationModule } from '../policies/oidc-configuration/oidc-configuration.module';
|
||||
import { PasswordComplexityPolicyModule } from '../policies/password-complexity-policy/password-complexity-policy.module';
|
||||
@@ -34,6 +35,7 @@ import { SettingsListComponent } from './settings-list.component';
|
||||
PasswordLockoutPolicyModule,
|
||||
PrivateLabelingPolicyModule,
|
||||
GeneralSettingsModule,
|
||||
NotificationPolicyModule,
|
||||
IdpSettingsModule,
|
||||
PrivacyPolicyModule,
|
||||
MessageTextsPolicyModule,
|
||||
|
@@ -92,6 +92,15 @@ export const NOTIFICATIONS: SidenavSetting = {
|
||||
},
|
||||
};
|
||||
|
||||
export const NOTIFICATION_POLICY: SidenavSetting = {
|
||||
id: 'notifications',
|
||||
i18nKey: 'SETTINGS.LIST.NOTIFICATIONS',
|
||||
groupI18nKey: 'SETTINGS.GROUPS.NOTIFICATIONS',
|
||||
requiredRoles: {
|
||||
[PolicyComponentServiceType.MGMT]: ['policy.read'],
|
||||
},
|
||||
};
|
||||
|
||||
export const MESSAGETEXTS: SidenavSetting = {
|
||||
id: 'messagetexts',
|
||||
i18nKey: 'SETTINGS.LIST.MESSAGETEXTS',
|
||||
|
@@ -11,6 +11,7 @@ import {
|
||||
DOMAIN,
|
||||
IDP,
|
||||
LOCKOUT,
|
||||
NOTIFICATION_POLICY,
|
||||
LOGIN,
|
||||
LOGINTEXTS,
|
||||
MESSAGETEXTS,
|
||||
@@ -30,6 +31,7 @@ export class OrgSettingsComponent {
|
||||
IDP,
|
||||
COMPLEXITY,
|
||||
LOCKOUT,
|
||||
NOTIFICATION_POLICY,
|
||||
DOMAIN,
|
||||
BRANDING,
|
||||
MESSAGETEXTS,
|
||||
|
@@ -204,6 +204,18 @@ import {
|
||||
GetSecurityPolicyResponse,
|
||||
SetSecurityPolicyRequest,
|
||||
SetSecurityPolicyResponse,
|
||||
GetNotificationPolicyRequest,
|
||||
GetNotificationPolicyResponse,
|
||||
UpdateNotificationPolicyRequest,
|
||||
UpdateNotificationPolicyResponse,
|
||||
GetDefaultPasswordChangeMessageTextResponse,
|
||||
GetDefaultPasswordChangeMessageTextRequest,
|
||||
GetCustomPasswordChangeMessageTextResponse,
|
||||
SetDefaultPasswordChangeMessageTextRequest,
|
||||
SetDefaultPasswordChangeMessageTextResponse,
|
||||
GetCustomPasswordChangeMessageTextRequest,
|
||||
AddNotificationPolicyRequest,
|
||||
AddNotificationPolicyResponse,
|
||||
} from '../proto/generated/zitadel/admin_pb';
|
||||
import { SearchQuery } from '../proto/generated/zitadel/member_pb';
|
||||
import { ListQuery } from '../proto/generated/zitadel/object_pb';
|
||||
@@ -346,6 +358,24 @@ export class AdminService {
|
||||
return this.grpcService.admin.setDefaultPasswordlessRegistrationMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getDefaultPasswordChangeMessageText(
|
||||
req: GetDefaultPasswordChangeMessageTextRequest,
|
||||
): Promise<GetDefaultPasswordChangeMessageTextResponse.AsObject> {
|
||||
return this.grpcService.admin.getDefaultPasswordChangeMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getCustomPasswordChangeMessageText(
|
||||
req: GetCustomPasswordChangeMessageTextRequest,
|
||||
): Promise<GetCustomPasswordChangeMessageTextResponse.AsObject> {
|
||||
return this.grpcService.admin.getCustomPasswordChangeMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public setDefaultPasswordChangeMessageText(
|
||||
req: SetDefaultPasswordChangeMessageTextRequest,
|
||||
): Promise<SetDefaultPasswordChangeMessageTextResponse.AsObject> {
|
||||
return this.grpcService.admin.setDefaultPasswordChangeMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public SetUpOrg(org: SetUpOrgRequest.Org, human: SetUpOrgRequest.Human): Promise<SetUpOrgResponse.AsObject> {
|
||||
const req = new SetUpOrgRequest();
|
||||
|
||||
@@ -484,6 +514,21 @@ export class AdminService {
|
||||
return this.grpcService.admin.setDefaultLanguage(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
/* notification policy */
|
||||
|
||||
public getNotificationPolicy(): Promise<GetNotificationPolicyResponse.AsObject> {
|
||||
const req = new GetNotificationPolicyRequest();
|
||||
return this.grpcService.admin.getNotificationPolicy(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public updateNotificationPolicy(req: UpdateNotificationPolicyRequest): Promise<UpdateNotificationPolicyResponse.AsObject> {
|
||||
return this.grpcService.admin.updateNotificationPolicy(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public addNotificationPolicy(req: AddNotificationPolicyRequest): Promise<AddNotificationPolicyResponse.AsObject> {
|
||||
return this.grpcService.admin.addNotificationPolicy(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
/* security policy */
|
||||
|
||||
public getSecurityPolicy(): Promise<GetSecurityPolicyResponse.AsObject> {
|
||||
|
@@ -24,6 +24,8 @@ import {
|
||||
AddCustomLockoutPolicyResponse,
|
||||
AddCustomLoginPolicyRequest,
|
||||
AddCustomLoginPolicyResponse,
|
||||
AddCustomNotificationPolicyRequest,
|
||||
AddCustomNotificationPolicyResponse,
|
||||
AddCustomPasswordAgePolicyRequest,
|
||||
AddCustomPasswordAgePolicyResponse,
|
||||
AddCustomPasswordComplexityPolicyRequest,
|
||||
@@ -108,6 +110,8 @@ import {
|
||||
GetCustomInitMessageTextResponse,
|
||||
GetCustomLoginTextsRequest,
|
||||
GetCustomLoginTextsResponse,
|
||||
GetCustomPasswordChangeMessageTextRequest,
|
||||
GetCustomPasswordChangeMessageTextResponse,
|
||||
GetCustomPasswordlessRegistrationMessageTextRequest,
|
||||
GetCustomPasswordlessRegistrationMessageTextResponse,
|
||||
GetCustomPasswordResetMessageTextRequest,
|
||||
@@ -124,6 +128,8 @@ import {
|
||||
GetDefaultLabelPolicyResponse,
|
||||
GetDefaultLoginTextsRequest,
|
||||
GetDefaultLoginTextsResponse,
|
||||
GetDefaultPasswordChangeMessageTextRequest,
|
||||
GetDefaultPasswordChangeMessageTextResponse,
|
||||
GetDefaultPasswordComplexityPolicyRequest,
|
||||
GetDefaultPasswordComplexityPolicyResponse,
|
||||
GetDefaultPasswordlessRegistrationMessageTextRequest,
|
||||
@@ -156,6 +162,8 @@ import {
|
||||
GetLoginPolicyResponse,
|
||||
GetMyOrgRequest,
|
||||
GetMyOrgResponse,
|
||||
GetNotificationPolicyRequest,
|
||||
GetNotificationPolicyResponse,
|
||||
GetOIDCInformationRequest,
|
||||
GetOIDCInformationResponse,
|
||||
GetOrgByDomainGlobalRequest,
|
||||
@@ -343,6 +351,8 @@ import {
|
||||
ResetCustomInitMessageTextToDefaultResponse,
|
||||
ResetCustomLoginTextsToDefaultRequest,
|
||||
ResetCustomLoginTextsToDefaultResponse,
|
||||
ResetCustomPasswordChangeMessageTextToDefaultRequest,
|
||||
ResetCustomPasswordChangeMessageTextToDefaultResponse,
|
||||
ResetCustomPasswordlessRegistrationMessageTextToDefaultRequest,
|
||||
ResetCustomPasswordlessRegistrationMessageTextToDefaultResponse,
|
||||
ResetCustomPasswordResetMessageTextToDefaultRequest,
|
||||
@@ -357,6 +367,8 @@ import {
|
||||
ResetLockoutPolicyToDefaultResponse,
|
||||
ResetLoginPolicyToDefaultRequest,
|
||||
ResetLoginPolicyToDefaultResponse,
|
||||
ResetNotificationPolicyToDefaultRequest,
|
||||
ResetNotificationPolicyToDefaultResponse,
|
||||
ResetPasswordAgePolicyToDefaultRequest,
|
||||
ResetPasswordAgePolicyToDefaultResponse,
|
||||
ResetPasswordComplexityPolicyToDefaultRequest,
|
||||
@@ -403,6 +415,8 @@ import {
|
||||
UpdateCustomLockoutPolicyResponse,
|
||||
UpdateCustomLoginPolicyRequest,
|
||||
UpdateCustomLoginPolicyResponse,
|
||||
UpdateCustomNotificationPolicyRequest,
|
||||
UpdateCustomNotificationPolicyResponse,
|
||||
UpdateCustomPasswordAgePolicyRequest,
|
||||
UpdateCustomPasswordAgePolicyResponse,
|
||||
UpdateCustomPasswordComplexityPolicyRequest,
|
||||
@@ -631,6 +645,26 @@ export class ManagementService {
|
||||
return this.grpcService.mgmt.getDefaultPasswordlessRegistrationMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getDefaultPasswordChangeMessageText(
|
||||
req: GetDefaultPasswordChangeMessageTextRequest,
|
||||
): Promise<GetDefaultPasswordChangeMessageTextResponse.AsObject> {
|
||||
return this.grpcService.mgmt.getDefaultPasswordChangeMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getCustomPasswordChangeMessageText(
|
||||
req: GetCustomPasswordChangeMessageTextRequest,
|
||||
): Promise<GetCustomPasswordChangeMessageTextResponse.AsObject> {
|
||||
return this.grpcService.mgmt.getCustomPasswordChangeMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public resetCustomPasswordChangeMessageTextToDefault(
|
||||
lang: string,
|
||||
): Promise<ResetCustomPasswordChangeMessageTextToDefaultResponse.AsObject> {
|
||||
const req = new ResetCustomPasswordChangeMessageTextToDefaultRequest();
|
||||
req.setLanguage(lang);
|
||||
return this.grpcService.mgmt.resetCustomPasswordChangeMessageTextToDefault(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getCustomPasswordlessRegistrationMessageText(
|
||||
req: GetCustomPasswordlessRegistrationMessageTextRequest,
|
||||
): Promise<GetCustomPasswordlessRegistrationMessageTextResponse.AsObject> {
|
||||
@@ -1371,6 +1405,30 @@ export class ManagementService {
|
||||
}
|
||||
}
|
||||
|
||||
/* notification policy */
|
||||
|
||||
public getNotificationPolicy(): Promise<GetNotificationPolicyResponse.AsObject> {
|
||||
const req = new GetNotificationPolicyRequest();
|
||||
return this.grpcService.mgmt.getNotificationPolicy(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public resetNotificationPolicyToDefault(): Promise<ResetNotificationPolicyToDefaultResponse.AsObject> {
|
||||
const req = new ResetNotificationPolicyToDefaultRequest();
|
||||
return this.grpcService.mgmt.resetNotificationPolicyToDefault(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public addCustomNotificationPolicy(
|
||||
req: AddCustomNotificationPolicyRequest,
|
||||
): Promise<AddCustomNotificationPolicyResponse.AsObject> {
|
||||
return this.grpcService.mgmt.addCustomNotificationPolicy(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public updateCustomNotificationPolicy(
|
||||
req: UpdateCustomNotificationPolicyRequest,
|
||||
): Promise<UpdateCustomNotificationPolicyResponse.AsObject> {
|
||||
return this.grpcService.mgmt.updateCustomNotificationPolicy(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getUserByID(id: string): Promise<GetUserByIDResponse.AsObject> {
|
||||
const req = new GetUserByIDRequest();
|
||||
req.setId(id);
|
||||
|
@@ -883,7 +883,7 @@
|
||||
"LOGIN": "Loginverhalten und Sicherheit",
|
||||
"LOCKOUT": "Sperrmechanismen",
|
||||
"COMPLEXITY": "Passwordkomplexität",
|
||||
"NOTIFICATIONS": "Benachrichtigungen",
|
||||
"NOTIFICATIONS": "Benachrichtigungseinstellungen",
|
||||
"NOTIFICATIONS_DESC": "SMTP und SMS Einstellungen",
|
||||
"MESSAGETEXTS": "Benachrichtigungstexte",
|
||||
"IDP": "Identity Provider",
|
||||
@@ -1007,6 +1007,11 @@
|
||||
"NUMBERERROR": "Muss eine Ziffer beinhalten.",
|
||||
"PATTERNERROR": "Das Passwort erfüllt nicht die vorgeschriebene Richtlinie."
|
||||
},
|
||||
"NOTIFICATION": {
|
||||
"TITLE": "Notification",
|
||||
"DESCRIPTION": "Legt fest, bei welchen Änderungen Benachrichtigungen gesendet werden",
|
||||
"PASSWORDCHANGE": "Passwordänderung"
|
||||
},
|
||||
"PRIVATELABELING": {
|
||||
"TITLE": "Branding",
|
||||
"DESCRIPTION": "Verleihen Sie dem Login Ihren benutzerdefinierten Style und passen Sie das Verhalten an.",
|
||||
@@ -1146,7 +1151,8 @@
|
||||
"VP": "Telefonnummerverifikation",
|
||||
"PR": "Passwort Wiederherstellung",
|
||||
"DC": "Domainbeanspruchung",
|
||||
"PL": "Passwortlos"
|
||||
"PL": "Passwortlos",
|
||||
"PC": "Passwordwechsel"
|
||||
},
|
||||
"CHIPS": {
|
||||
"firstname": "Vorname",
|
||||
|
@@ -883,7 +883,7 @@
|
||||
"LOGIN": "Login Behavior and Security",
|
||||
"LOCKOUT": "Lockout",
|
||||
"COMPLEXITY": "Password complexity",
|
||||
"NOTIFICATIONS": "Notification providers and SMTP",
|
||||
"NOTIFICATIONS": "Notification settings",
|
||||
"NOTIFICATIONS_DESC": "SMTP and SMS Settings",
|
||||
"MESSAGETEXTS": "Message Texts",
|
||||
"IDP": "Identity Providers",
|
||||
@@ -1007,6 +1007,11 @@
|
||||
"NUMBERERROR": "Must include a digit.",
|
||||
"PATTERNERROR": "The password does not meet the required pattern."
|
||||
},
|
||||
"NOTIFICATION": {
|
||||
"TITLE": "Notification",
|
||||
"DESCRIPTION": "Determines on which changes, notifications will be sent.",
|
||||
"PASSWORDCHANGE": "Password change"
|
||||
},
|
||||
"PRIVATELABELING": {
|
||||
"TITLE": "Branding",
|
||||
"DESCRIPTION": "Give the login your personalized style and modify its behavior.",
|
||||
@@ -1146,7 +1151,8 @@
|
||||
"VP": "Verify Phone",
|
||||
"PR": "Password Reset",
|
||||
"DC": "Domain Claim",
|
||||
"PL": "Passwordless"
|
||||
"PL": "Passwordless",
|
||||
"PC": "Password Change"
|
||||
},
|
||||
"CHIPS": {
|
||||
"firstname": "Firstname",
|
||||
|
@@ -883,7 +883,7 @@
|
||||
"LOGIN": "Comportement de connexion et sécurité",
|
||||
"LOCKOUT": "Verrouillage",
|
||||
"COMPLEXITY": "Complexité du mot de passe",
|
||||
"NOTIFICATIONS": "Fournisseurs de notifications et SMTP",
|
||||
"NOTIFICATIONS": "Paramètres de notification",
|
||||
"NOTIFICATIONS_DESC": "Paramètres SMTP et SMS",
|
||||
"MESSAGETEXTS": "Textes des messages",
|
||||
"IDP": "Fournisseurs d'identité",
|
||||
@@ -1007,6 +1007,11 @@
|
||||
"NUMBERERROR": "Doit inclure un chiffre.",
|
||||
"PATTERNERROR": "Le mot de passe ne correspond pas au modèle requis."
|
||||
},
|
||||
"NOTIFICATION": {
|
||||
"TITLE": "Notifications",
|
||||
"DESCRIPTION": "Détermine sur quels changements, les notifications seront envoyées",
|
||||
"PASSWORDCHANGE": "Changement de mot de passe"
|
||||
},
|
||||
"PRIVATELABELING": {
|
||||
"TITLE": "Image de marque",
|
||||
"DESCRIPTION": "Donnez au login votre style personnalisé et modifiez son comportement.",
|
||||
@@ -1146,7 +1151,8 @@
|
||||
"VP": "Vérifier le téléphone",
|
||||
"PR": "Réinitialisation du mot de passe",
|
||||
"DC": "Réclamation de domaine",
|
||||
"PL": "Sans mot de passe"
|
||||
"PL": "Sans mot de passe",
|
||||
"PC": "Changement de mot de passe"
|
||||
},
|
||||
"CHIPS": {
|
||||
"firstname": "Prénom",
|
||||
|
@@ -884,7 +884,7 @@
|
||||
"LOGIN": "Comportamento login e sicurezza",
|
||||
"LOCKOUT": "Meccanismi di bloccaggio",
|
||||
"COMPLEXITY": "Complessità della password",
|
||||
"NOTIFICATIONS": "Notifiche",
|
||||
"NOTIFICATIONS": "Impostazioni di notifica",
|
||||
"NOTIFICATIONS_DESC": "Impostazioni SMTP e SMS",
|
||||
"MESSAGETEXTS": "Testi di notifica",
|
||||
"IDP": "Identity Providers",
|
||||
@@ -1008,6 +1008,11 @@
|
||||
"NUMBERERROR": "Deve includere una cifra.",
|
||||
"PATTERNERROR": "La password non corrisponde al modello richiesto."
|
||||
},
|
||||
"NOTIFICATION": {
|
||||
"TITLE": "Notifiche",
|
||||
"DESCRIPTION": "Determina su quali modifiche verranno inviate le notifiche",
|
||||
"PASSWORDCHANGE": "Cambiamento della password"
|
||||
},
|
||||
"PRIVATELABELING": {
|
||||
"TITLE": "Branding",
|
||||
"DESCRIPTION": "Dai al login il tuo stile personalizzato e modifica il suo comportamento.",
|
||||
@@ -1147,7 +1152,8 @@
|
||||
"VP": "Verificazione del telefono",
|
||||
"PR": "Ripristino della password",
|
||||
"DC": "Rivendicazione del dominio",
|
||||
"PL": "Autenticazione Passwordless"
|
||||
"PL": "Autenticazione Passwordless",
|
||||
"PC": "Cambiamento della password"
|
||||
},
|
||||
"CHIPS": {
|
||||
"firstname": "Nome",
|
||||
|
@@ -883,7 +883,7 @@
|
||||
"LOGIN": "登录行为和安全",
|
||||
"LOCKOUT": "安全锁策略",
|
||||
"COMPLEXITY": "密码复杂性",
|
||||
"NOTIFICATIONS": "通知服务商",
|
||||
"NOTIFICATIONS": "通知设置",
|
||||
"NOTIFICATIONS_DESC": "SMTP 和 SMS 设置",
|
||||
"MESSAGETEXTS": "消息文本",
|
||||
"IDP": "身份提供者",
|
||||
@@ -1007,6 +1007,11 @@
|
||||
"NUMBERERROR": "密码必须包含数字。",
|
||||
"PATTERNERROR": "密码不符合要求。"
|
||||
},
|
||||
"NOTIFICATION": {
|
||||
"TITLE": "通知",
|
||||
"DESCRIPTION": "确定将发送哪些更改、通知",
|
||||
"PASSWORDCHANGE": "更改密码"
|
||||
},
|
||||
"PRIVATELABELING": {
|
||||
"TITLE": "品牌标识",
|
||||
"DESCRIPTION": "为登录提供您的个性化风格并修改其行为。",
|
||||
@@ -1145,7 +1150,8 @@
|
||||
"VP": "验证手机号码",
|
||||
"PR": "重置密码",
|
||||
"DC": "域名声明",
|
||||
"PL": "无密码身份验证"
|
||||
"PL": "无密码身份验证",
|
||||
"PC": "修改密码"
|
||||
},
|
||||
"CHIPS": {
|
||||
"firstname": "名",
|
||||
|
Reference in New Issue
Block a user