feat(console): message and login texts, privacy policy (#2016)

* message texts wrapper components

* message-text sub, i18n, grid

* fix routing

* pack

* pack

* update material

* audit

* fix mgmt service for labelplcy

* map conv

* edit text from map

* request map

* fetch data, mgmt admin service

* warn box, i18n

* resetbtn

* login texts

* login text requests

* reset, default, i18n

* disabled, features, message text setter, service

* locale switcher

* policy grid

* password reset, domain claimed i18n

* lint files

* fix admin service, i18n, lang setter

* fix scss duplicate

* privacy policy, cleanup grid, fix message, login texts (#2031)

* policy grid everywhere 🦒

* cleanup home

* log login text request

* patch all data

* refresh toggle

* fix: add dialog for unsaved changes (#2057)

* logintexts dialog

* check for dialog on pairwise operation

* fix: patch value to local state after save

* fix: i18n and custom login texts (#2060)

* fix: i18n and custom login texts

* fix: tos and privacy texts

* fix frontend

* fix: tos and privacy texts and tests

* fix: i18n, tos and privacy texts and tests

* fix frontend maps

* i18n

* add ResetCustomLoginTextToDefault in admin api and fix template remove in handlers

* resetlogintexttodefault

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
Max Peintner
2021-07-26 12:44:45 +02:00
committed by GitHub
parent 26e4e607bc
commit 2e684684de
100 changed files with 4500 additions and 2259 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -11,16 +11,16 @@
"private": true,
"dependencies": {
"@angular/animations": "~12.0.3",
"@angular/cdk": "~12.0.0",
"@angular/cdk": "~12.1.1",
"@angular/common": "~12.0.5",
"@angular/compiler": "~12.0.0",
"@angular/core": "~12.0.0",
"@angular/forms": "~12.0.5",
"@angular/material": "^12.0.3",
"@angular/material-moment-adapter": "^12.0.3",
"@angular/material": "^12.1.1",
"@angular/material-moment-adapter": "^12.1.1",
"@angular/platform-browser": "~12.0.0",
"@angular/router": "~12.0.5",
"@angular/platform-browser-dynamic": "~12.0.5",
"@angular/router": "~12.0.5",
"@angular/service-worker": "~12.0.5",
"@grpc/grpc-js": "^1.3.2",
"@ngx-translate/core": "^13.0.0",
@@ -37,7 +37,6 @@
"grpc-web": "^1.2.1",
"libphonenumber-js": "^1.9.16",
"moment": "^2.29.1",
"ngx-image-cropper": "^3.3.5",
"ngx-color": "^7.2.0",
"ngx-quicklink": "^0.2.6",
"rxjs": "~6.6.7",
@@ -47,8 +46,8 @@
"zone.js": "~0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "~12.1.0",
"@angular/cli": "~12.1.0",
"@angular-devkit/build-angular": "~12.1.1",
"@angular/cli": "~12.1.1",
"@angular/compiler-cli": "~12.0.5",
"@angular/language-service": "~12.1.0",
"@types/jasmine": "~3.7.7",
@@ -71,4 +70,4 @@
"tslint": "~6.1.3",
"typescript": "^4.2.4"
}
}
}

View File

@@ -1,204 +1,205 @@
import {
animate,
animateChild,
AnimationTriggerMetadata,
group,
query,
stagger,
style,
transition,
trigger,
animate,
animateChild,
AnimationTriggerMetadata,
group,
query,
stagger,
style,
transition,
trigger,
} from '@angular/animations';
export const toolbarAnimation: AnimationTriggerMetadata =
trigger('toolbar', [
transition(':enter', [
style({
transform: 'translateY(-100%)',
opacity: 0,
}),
animate(
'.2s ease-out',
style({
transform: 'translateY(0%)',
opacity: 1,
}),
),
]),
]);
trigger('toolbar', [
transition(':enter', [
style({
transform: 'translateY(-100%)',
opacity: 0,
}),
animate(
'.2s ease-out',
style({
transform: 'translateY(0%)',
opacity: 1,
}),
),
]),
]);
export const adminLineAnimation: AnimationTriggerMetadata =
trigger('adminline', [
transition(':enter', [
style({
transform: 'translateY(100%)',
opacity: 0.5,
}),
animate(
'.2s ease-out',
style({
transform: 'translateY(0%)',
opacity: 1,
}),
),
]),
]);
trigger('adminline', [
transition(':enter', [
style({
transform: 'translateY(100%)',
opacity: 0.5,
}),
animate(
'.2s ease-out',
style({
transform: 'translateY(0%)',
opacity: 1,
}),
),
]),
]);
export const accountCard: AnimationTriggerMetadata = trigger('accounts', [
transition(':enter', [
style({
transform: 'scale(.9) translateY(-10%)',
height: '200px',
opacity: 0,
}),
animate(
'.1s ease-out',
style({
transform: 'scale(1) translateY(0%)',
height: '*',
opacity: 1,
}),
),
]),
transition(':enter', [
style({
transform: 'scale(.9) translateY(-10%)',
height: '200px',
opacity: 0,
}),
animate(
'.1s ease-out',
style({
transform: 'scale(1) translateY(0%)',
height: '*',
opacity: 1,
}),
),
]),
]);
export const navAnimations: Array<AnimationTriggerMetadata> = [
trigger('navAnimation', [
transition('* => *', [
query('@navitem', stagger('50ms', animateChild()), { optional: true }),
]),
trigger('navAnimation', [
transition('* => *', [
query('@navitem', stagger('50ms', animateChild()), { optional: true }),
]),
trigger('navitem', [
transition(':enter', [
style({
opacity: 0,
}),
animate(
'.0s',
style({
opacity: 1,
}),
),
]),
transition(':leave', [
style({
opacity: 1,
}),
animate(
'.0s',
style({
opacity: 0,
}),
),
]),
]),
trigger('navitem', [
transition(':enter', [
style({
opacity: 0,
}),
animate(
'.0s',
style({
opacity: 1,
}),
),
]),
transition(':leave', [
style({
opacity: 1,
}),
animate(
'.0s',
style({
opacity: 0,
}),
),
]),
]),
];
export const enterAnimations: Array<AnimationTriggerMetadata> = [
trigger('appearfade', [
transition(':enter', [
style({
transform: 'scale(.9) translateY(-10%)',
opacity: 0,
}),
animate(
'100ms ease-in-out',
style({
transform: 'scale(1) translateY(0%)',
opacity: 1,
}),
),
]),
transition(':leave', [
style({
transform: 'scale(1) translateY(0%)',
opacity: 1,
}),
animate(
'100ms ease-in-out',
style({
transform: 'scale(.9) translateY(-10%)',
opacity: 0,
}),
),
]),
trigger('appearfade', [
transition(':enter', [
style({
transform: 'scale(.9) translateY(-10%)',
opacity: 0,
}),
animate(
'100ms ease-in-out',
style({
transform: 'scale(1) translateY(0%)',
opacity: 1,
}),
),
]),
transition(':leave', [
style({
transform: 'scale(1) translateY(0%)',
opacity: 1,
}),
animate(
'100ms ease-in-out',
style({
transform: 'scale(.9) translateY(-10%)',
opacity: 0,
}),
),
]),
]),
];
export const routeAnimations: AnimationTriggerMetadata = trigger('routeAnimations', [
transition('HomePage => AddPage', [
style({ transform: 'translateX(100%)', opacity: 0.5 }),
animate('250ms ease-out', style({ transform: 'translateX(0%)', opacity: 1 })),
transition('HomePage => AddPage', [
style({ transform: 'translateX(100%)', opacity: 0.5 }),
animate('250ms ease-out', style({ transform: 'translateX(0%)', opacity: 1 })),
]),
transition('AddPage => HomePage',
[animate('250ms', style({ transform: 'translateX(100%)', opacity: 0.5 }))],
),
transition('HomePage => DetailPage', [
query(':enter, :leave', style({ position: 'absolute', left: 0, right: 0 }), {
optional: true,
}),
group([
query(
':enter',
[
style({
transform: 'translateX(20%)',
opacity: 0.5,
}),
animate(
'.35s ease-in',
style({
transform: 'translateX(0%)',
opacity: 1,
}),
),
],
{
optional: true,
},
),
query(
':leave',
[style({ opacity: 1, width: '100%' }), animate('.35s ease-out', style({ opacity: 0 }))],
{
optional: true,
},
),
]),
transition('AddPage => HomePage',
[animate('250ms', style({ transform: 'translateX(100%)', opacity: 0.5 }))],
),
transition('HomePage => DetailPage', [
query(':enter, :leave', style({ position: 'absolute', left: 0, right: 0 }), {
optional: true,
}),
group([
query(
':enter',
[
style({
transform: 'translateX(20%)',
opacity: 0.5,
}),
animate(
'.35s ease-in',
style({
transform: 'translateX(0%)',
opacity: 1,
}),
),
],
{
optional: true,
},
),
query(
':leave',
[style({ opacity: 1, width: '100%' }), animate('.35s ease-out', style({ opacity: 0 }))],
{
optional: true,
},
),
]),
]),
transition('DetailPage => HomePage', [
query(':enter, :leave', style({ position: 'absolute', left: 0, right: 0 }), {
optional: true,
}),
group([
query(
':enter',
[
style({
opacity: 0,
}),
animate(
'.35s ease-out',
style({
opacity: 1,
}),
),
],
{
optional: true,
},
),
query(
':leave',
[
style({ width: '100%', transform: 'translateX(0%)' }),
animate('.35s ease-in', style({ transform: 'translateX(30%)', opacity: 0 })),
],
{
optional: true,
},
),
]),
]),
transition('DetailPage => HomePage', [
query(':enter, :leave', style({ position: 'absolute', left: 0, right: 0 }), {
optional: true,
}),
group([
query(
':enter',
[
style({
opacity: 0,
}),
animate(
'.35s ease-out',
style({
opacity: 1,
}),
),
],
{
optional: true,
},
),
query(
':leave',
[
style({ width: '100%', transform: 'translateX(0%)' }),
animate('.35s ease-in', style({ transform: 'translateX(30%)', opacity: 0 })),
],
{
optional: true,
},
),
]),
]),
]);

View File

@@ -0,0 +1,31 @@
<div *ngIf="currentMap">
<form [formGroup]="form" >
<ng-container *ngFor="let key of (current$ | async) | keyvalue">
<div class="block">
<div class="flex" *ngIf="(default$ | async) as defaultmap">
<cnsl-form-field class="formfield" >
<cnsl-label>{{key.key}}</cnsl-label>
<textarea class="text" cnslInput [formControlName]="key.key" [placeholder]="defaultmap[key.key]" [name]="key.key" [ngClass]="{'defaulttext': form.get(key.key)?.value === ''}"></textarea>
<div class="chips" *ngIf="warnText[key.key] == undefined">
<ng-container *ngFor="let chip of chips" >
<div class="chip" appCopyToClipboard [valueToCopy]="chip.value" (copiedValue)="copied = $event" (click)="addChip(key.key, chip.value)">
<span class="key">{{chip.key | translate}}</span>
<span class="value">{{chip.value}}</span>
<i *ngIf="copied != chip.value" class="las la-clipboard"></i>
<i *ngIf="copied == chip.value" class="las la-clipboard-check"></i>
</div>
</ng-container>
</div>
</cnsl-form-field>
<div class="actions">
<button matTooltip="{{'ACTIONS.RESETDEFAULT'| translate }}" mat-icon-button [disabled]="form.get(key.key)?.value == defaultmap[key.key] || disabled" (click)="form.get(key.key)?.setValue(defaultmap[key.key])" (mouseenter) = "form.get(key.key)?.value != defaultmap[key.key] && setWarnText(key.key, defaultmap[key.key])" (mouseleave) ="setWarnText(key.key, undefined)"><i class="las la-history"></i></button>
<button matTooltip="{{'ACTIONS.RESETCURRENT'| translate }}" mat-icon-button [disabled]="form.get(key.key)?.value == currentMap[key.key] || disabled" (click)="form.get(key.key)?.setValue(currentMap[key.key])" (mouseenter) = "form.get(key.key)?.value != currentMap[key.key] && setWarnText(key.key, currentMap[key.key])" (mouseleave) ="setWarnText(key.key, undefined)"><i class="las la-undo"></i></button>
</div>
</div>
</div>
<cnsl-info-section *ngIf="warnText[key.key] !== undefined" class="info" type="WARN">{{'ACTIONS.RESETTO'| translate }} <cite>'{{warnText[key.key]}}'</cite></cnsl-info-section>
</ng-container>
</form>
</div>

View File

@@ -0,0 +1,96 @@
.block {
display: block;
.flex {
display: flex;
.formfield {
flex: 1;
.text {
min-height: 80px;
}
&.hovering {
background-color: red;
}
.chips {
display: flex;
flex-wrap: wrap;
opacity: 0;
margin: 0 -.25rem;
transition: all .2s ease;
.chip {
border-radius: 50vw;
padding: 4px .5rem;
font-size: 12px;
background: #5282c1;
color: white;
margin: .25rem;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
* {
transition: all .2s ease;
}
i {
opacity: .5;
font-size: 1.1rem;
margin-left: .5rem;
}
.key {
display: inline-block;
}
.value {
display: none;
}
&:hover {
i {
opacity: 1;
}
.key {
display: none;
}
.value {
display: inline-block;
}
}
}
}
&.cnsl-focused {
.chips {
opacity: 1;
cursor: copy;
}
}
.chips:hover {
visibility: visible;
}
}
.actions {
display: flex;
flex-direction: column;
align-self: flex-start;
margin-top: 30px;
}
}
}
.info {
display: block;
margin-right: 40px;
margin-bottom: 1.5rem;
}

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditTextComponent } from './edit-text.component';
describe('EditTextComponent', () => {
let component: EditTextComponent;
let fixture: ComponentFixture<EditTextComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [EditTextComponent],
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(EditTextComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,54 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { Observable, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'cnsl-edit-text',
templateUrl: './edit-text.component.html',
styleUrls: ['./edit-text.component.scss'],
})
export class EditTextComponent implements OnInit, OnDestroy {
@Input() label: string = '';
@Input() current$!: Observable<{ [key: string]: any | string; }>;
@Input() default$!: Observable<{ [key: string]: any | string; }>;
@Input() currentlyDragged: string = '';
@Output() changedValues: EventEmitter<{ [key: string]: string; }> = new EventEmitter();
public currentMap: { [key: string]: string; } = {};
private destroy$: Subject<void> = new Subject();
public form!: FormGroup;
public warnText: { [key: string]: string | undefined; } = {};
@Input() public chips: any[] = [];
@Input() public disabled: boolean = true;
public copied: string = '';
public ngOnInit(): void {
this.current$.pipe(takeUntil(this.destroy$)).subscribe(value => {
this.currentMap = value;
this.form = new FormGroup({});
Object.keys(value).map(key => {
const control = new FormControl({ value: value[key], disabled: this.disabled });
this.form.addControl(key, control);
});
this.form.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(values => this.changedValues.emit(values));
});
}
public ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
public setWarnText(key: string, text: string | undefined): void {
this.warnText[key] = text;
}
public addChip(key: string, value: string): void {
const c = this.form.get(key)?.value;
this.form.get(key)?.setValue(`${c} ${value}`);
}
}

View File

@@ -0,0 +1,39 @@
import { TextFieldModule } from '@angular/cdk/text-field';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { CopyToClipboardModule } from 'src/app/directives/copy-to-clipboard/copy-to-clipboard.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { FormFieldModule } from '../form-field/form-field.module';
import { InfoSectionModule } from '../info-section/info-section.module';
import { EditTextComponent } from './edit-text.component';
@NgModule({
declarations: [
EditTextComponent,
],
imports: [
CommonModule,
InfoSectionModule,
ReactiveFormsModule,
FormsModule,
InputModule,
FormFieldModule,
MatButtonModule,
MatIconModule,
MatTooltipModule,
TranslateModule,
MatTooltipModule,
TextFieldModule,
CopyToClipboardModule,
],
exports: [
EditTextComponent,
],
})
export class EditTextModule { }

View File

@@ -52,21 +52,26 @@
<p class="default" *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</p>
<div class="content" *ngIf="features">
<div class="row">
<span class="left-desc">{{'FEATURES.DATA.AUDITLOGRETENTION' | translate}}</span>
<span class="fill-space"></span>
<span>{{features.auditLogRetention | timestampToRetention }} {{'FEATURES.RETENTIONDAYS' |
translate}}</span>
<span class="left-desc">{{'FEATURES.DATA.AUDITLOGRETENTION' | translate}}</span>
<span class="fill-space"></span>
<span>{{features.auditLogRetention | timestampToRetention }} {{'FEATURES.RETENTIONDAYS' |
translate}}</span>
</div>
<br/>
<div class="row">
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYUSERNAMELOGIN' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
[(ngModel)]="features.loginPolicyUsernameLogin"
[disabled]="(['iam.features.write'] | hasRole | async) == false">
</mat-slide-toggle>
<i class="icon las la-sign-in-alt"></i>
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYUSERNAMELOGIN' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
[(ngModel)]="features.loginPolicyUsernameLogin"
[disabled]="(['iam.features.write'] | hasRole | async) == false">
</mat-slide-toggle>
</div>
<div class="row">
<i class="icon las la-sign-in-alt"></i>
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYPASSWORDRESET' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
@@ -76,66 +81,81 @@
</div>
<div class="row">
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYREGISTRATION' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
[(ngModel)]="features.loginPolicyRegistration"
[disabled]="(['iam.features.write'] | hasRole | async) == false">
</mat-slide-toggle>
<i class="icon las la-sign-in-alt"></i>
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYREGISTRATION' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
[(ngModel)]="features.loginPolicyRegistration"
[disabled]="(['iam.features.write'] | hasRole | async) == false">
</mat-slide-toggle>
</div>
<div class="row">
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYIDP' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="features.loginPolicyIdp"
[disabled]="(['iam.features.write'] | hasRole | async) == false">
</mat-slide-toggle>
<i class="icon las la-sign-in-alt"></i>
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYIDP' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="features.loginPolicyIdp"
[disabled]="(['iam.features.write'] | hasRole | async) == false">
</mat-slide-toggle>
</div>
<div class="row">
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYFACTORS' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
[(ngModel)]="features.loginPolicyFactors"
[disabled]="(['iam.features.write'] | hasRole | async) == false">
</mat-slide-toggle>
<i class="icon las la-sign-in-alt"></i>
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYFACTORS' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
[(ngModel)]="features.loginPolicyFactors"
[disabled]="(['iam.features.write'] | hasRole | async) == false">
</mat-slide-toggle>
</div>
<div class="row">
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYPASSWORDLESS' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
[(ngModel)]="features.loginPolicyPasswordless"
[disabled]="(['iam.features.write'] | hasRole | async) == false">
</mat-slide-toggle>
<i class="icon las la-sign-in-alt"></i>
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYPASSWORDLESS' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
[(ngModel)]="features.loginPolicyPasswordless"
[disabled]="(['iam.features.write'] | hasRole | async) == false">
</mat-slide-toggle>
</div>
<br/>
<div class="row">
<mat-icon class="icon" svgIcon="mdi_textbox_password"></mat-icon>
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYCOMPLEXITYPOLICY' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
[(ngModel)]="features.passwordComplexityPolicy"
[disabled]="(['iam.features.write'] | hasRole | async) == false">
</mat-slide-toggle>
</div>
<br/>
<div class="row">
<i class="icon las la-swatchbook"></i>
<span class="left-desc">{{'FEATURES.DATA.LABELPOLICYPRIVATELABEL' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="features.labelPolicyPrivateLabel"
[disabled]="(['iam.features.write'] | hasRole | async) == false">
</mat-slide-toggle>
</div>
<div class="row">
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYCOMPLEXITYPOLICY' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
[(ngModel)]="features.passwordComplexityPolicy"
[disabled]="(['iam.features.write'] | hasRole | async) == false">
</mat-slide-toggle>
<i class="icon las la-swatchbook"></i>
<span class="left-desc">{{'FEATURES.DATA.LABELPOLICYWATERMARK' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="features.labelPolicyWatermark"
[disabled]="(['iam.features.write'] | hasRole | async) == false">
</mat-slide-toggle>
</div>
<div class="row">
<span class="left-desc">{{'FEATURES.DATA.LABELPOLICYPRIVATELABEL' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="features.labelPolicyPrivateLabel"
[disabled]="(['iam.features.write'] | hasRole | async) == false">
</mat-slide-toggle>
</div>
<div class="row">
<span class="left-desc">{{'FEATURES.DATA.LABELPOLICYWATERMARK' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="features.labelPolicyWatermark"
[disabled]="(['iam.features.write'] | hasRole | async) == false">
</mat-slide-toggle>
</div>
<br/>
<div class="row">
<i class="icon las la-gem"></i>
<span class="left-desc">{{'FEATURES.DATA.CUSTOMDOMAIN' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="features.customDomain"
@@ -144,6 +164,16 @@
</div>
<div class="row">
<i class="icon las la-file-contract"></i>
<span class="left-desc">{{'FEATURES.DATA.PRIVACYPOLICY' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="features.privacyPolicy"
[disabled]="(['iam.features.write'] | hasRole | async) == false">
</mat-slide-toggle>
</div>
<div class="row">
<i class="icon las la-paragraph"></i>
<span class="left-desc">{{'FEATURES.DATA.CUSTOMTEXT' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="features.customText"

View File

@@ -81,6 +81,12 @@
align-items: center;
padding: .3rem 0;
i,
.icon {
margin-right: 1rem;
font-size: 1.5rem;
}
.left-desc {
font-size: .9rem;
}

View File

@@ -161,6 +161,7 @@ export class FeaturesComponent implements OnDestroy {
req.setLabelPolicyWatermark(this.features.labelPolicyWatermark);
req.setCustomDomain(this.features.customDomain);
req.setCustomText(this.features.customText);
req.setPrivacyPolicy(this.features.privacyPolicy);
this.adminService.setOrgFeatures(req).then(() => {
this.toast.showInfo('POLICY.TOAST.SET', true);

View File

@@ -219,6 +219,5 @@
</app-card>
</ng-template>
<cnsl-links *ngIf="nextLinks" [links]="nextLinks"></cnsl-links>
<app-policy-grid class="grid" [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="security"></app-policy-grid>
</app-detail-layout>

View File

@@ -137,3 +137,8 @@
background-color: rgba(var(--grey), .5);
margin: 1rem 0;
}
.grid {
display: block;
margin-top: 100px;
}

View File

@@ -19,15 +19,7 @@ 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 { CnslLinks } from '../../links/links.component';
import {
IAM_COMPLEXITY_LINK,
IAM_POLICY_LINK,
IAM_PRIVATELABEL_LINK,
ORG_COMPLEXITY_LINK,
ORG_IAM_POLICY_LINK,
ORG_PRIVATELABEL_LINK,
} from '../../policy-grid/policy-links';
import { GridPolicy, LOGIN_POLICY } from '../../policy-grid/policies';
import { PolicyComponentServiceType } from '../policy-component-types.enum';
import { AddIdpDialogComponent } from './add-idp-dialog/add-idp-dialog.component';
@@ -51,7 +43,7 @@ export class LoginPolicyComponent implements OnDestroy {
public disabled: boolean = true;
public IDPStylingType: any = IDPStylingType;
public nextLinks: CnslLinks[] = [];
public currentPolicy: GridPolicy = LOGIN_POLICY;
constructor(
private route: ActivatedRoute,
private toast: ToastService,
@@ -67,11 +59,6 @@ export class LoginPolicyComponent implements OnDestroy {
PasswordlessType.PASSWORDLESS_TYPE_ALLOWED,
PasswordlessType.PASSWORDLESS_TYPE_NOT_ALLOWED,
];
this.nextLinks = [
ORG_COMPLEXITY_LINK,
ORG_IAM_POLICY_LINK,
ORG_PRIVATELABEL_LINK,
];
break;
case PolicyComponentServiceType.ADMIN:
this.service = this.injector.get(AdminService as Type<AdminService>);
@@ -79,11 +66,6 @@ export class LoginPolicyComponent implements OnDestroy {
PasswordlessType.PASSWORDLESS_TYPE_ALLOWED,
PasswordlessType.PASSWORDLESS_TYPE_NOT_ALLOWED,
];
this.nextLinks = [
IAM_COMPLEXITY_LINK,
IAM_POLICY_LINK,
IAM_PRIVATELABEL_LINK,
];
break;
}

View File

@@ -19,36 +19,36 @@ import { HasFeaturePipeModule } from 'src/app/pipes/has-feature-pipe/has-feature
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { InfoSectionModule } from '../../info-section/info-section.module';
import { LinksModule } from '../../links/links.module';
import { PolicyGridModule } from '../../policy-grid/policy-grid.module';
import { AddIdpDialogModule } from './add-idp-dialog/add-idp-dialog.module';
import { LoginPolicyRoutingModule } from './login-policy-routing.module';
import { LoginPolicyComponent } from './login-policy.component';
@NgModule({
declarations: [LoginPolicyComponent],
imports: [
LoginPolicyRoutingModule,
CommonModule,
InfoSectionModule,
FormsModule,
CardModule,
InputModule,
MatButtonModule,
HasFeaturePipeModule,
MatSlideToggleModule,
MatIconModule,
HasRoleModule,
HasRolePipeModule,
MatTooltipModule,
TranslateModule,
DetailLayoutModule,
AddIdpDialogModule,
IdpTableModule,
MfaTableModule,
MatProgressSpinnerModule,
MatSelectModule,
MatRippleModule,
LinksModule,
],
declarations: [LoginPolicyComponent],
imports: [
LoginPolicyRoutingModule,
CommonModule,
InfoSectionModule,
FormsModule,
CardModule,
InputModule,
MatButtonModule,
HasFeaturePipeModule,
MatSlideToggleModule,
MatIconModule,
HasRoleModule,
HasRolePipeModule,
MatTooltipModule,
TranslateModule,
DetailLayoutModule,
AddIdpDialogModule,
IdpTableModule,
MfaTableModule,
MatProgressSpinnerModule,
MatSelectModule,
MatRippleModule,
PolicyGridModule,
],
})
export class LoginPolicyModule { }

View File

@@ -0,0 +1,317 @@
import { SetCustomLoginTextsRequest as AdminSetCustomLoginTextsRequest } from 'src/app/proto/generated/zitadel/admin_pb';
import { SetCustomLoginTextsRequest } from 'src/app/proto/generated/zitadel/management_pb';
import {
EmailVerificationDoneScreenText,
EmailVerificationScreenText,
ExternalUserNotFoundScreenText,
FooterText,
InitializeUserDoneScreenText,
InitializeUserScreenText,
InitMFADoneScreenText,
InitMFAOTPScreenText,
InitMFAPromptScreenText,
InitMFAU2FScreenText,
InitPasswordDoneScreenText,
InitPasswordScreenText,
LinkingUserDoneScreenText,
LoginScreenText,
LogoutDoneScreenText,
MFAProvidersText,
PasswordChangeDoneScreenText,
PasswordChangeScreenText,
PasswordlessScreenText,
PasswordResetDoneScreenText,
PasswordScreenText,
RegistrationOptionScreenText,
RegistrationOrgScreenText,
RegistrationUserScreenText,
SelectAccountScreenText,
SuccessLoginScreenText,
UsernameChangeDoneScreenText,
UsernameChangeScreenText,
VerifyMFAOTPScreenText,
VerifyMFAU2FScreenText,
} from 'src/app/proto/generated/zitadel/text_pb';
type Req = AdminSetCustomLoginTextsRequest | SetCustomLoginTextsRequest;
type Map = AdminSetCustomLoginTextsRequest.AsObject | SetCustomLoginTextsRequest.AsObject;
export function mapRequestValues(map: Partial<Map>, req: Req): Req {
const r0 = new EmailVerificationDoneScreenText();
r0.setCancelButtonText(map.emailVerificationDoneText?.cancelButtonText ?? '');
r0.setDescription(map.emailVerificationDoneText?.description ?? '');
r0.setLoginButtonText(map.emailVerificationDoneText?.loginButtonText ?? '');
r0.setNextButtonText(map.emailVerificationDoneText?.nextButtonText ?? '');
r0.setTitle(map.emailVerificationDoneText?.title ?? '');
req.setEmailVerificationDoneText(r0);
const r1 = new EmailVerificationScreenText();
r1.setCodeLabel(map.emailVerificationText?.codeLabel ?? '');
r1.setDescription(map.emailVerificationText?.description ?? '');
r1.setNextButtonText(map.emailVerificationText?.nextButtonText ?? '');
r1.setResendButtonText(map.emailVerificationText?.resendButtonText ?? '');
r1.setTitle(map.emailVerificationText?.title ?? '');
req.setEmailVerificationText(r1);
const r2 = new ExternalUserNotFoundScreenText();
r2.setAutoRegisterButtonText(map.externalUserNotFoundText?.autoRegisterButtonText ?? '');
r2.setDescription(map.externalUserNotFoundText?.description ?? '');
r2.setLinkButtonText(map.externalUserNotFoundText?.linkButtonText ?? '');
r2.setTitle(map.externalUserNotFoundText?.title ?? '');
req.setExternalUserNotFoundText(r2);
const r3 = new FooterText();
r3.setHelp(map.footerText?.help ?? '');
r3.setHelpLink(map.footerText?.helpLink ?? '');
r3.setPrivacyPolicy(map.footerText?.privacyPolicy ?? '');
r3.setTos(map.footerText?.tos ?? '');
req.setFooterText(r3);
const r4 = new InitMFADoneScreenText();
r4.setCancelButtonText(map.initMfaDoneText?.cancelButtonText ?? '');
r4.setDescription(map.initMfaDoneText?.description ?? '');
r4.setNextButtonText(map.initMfaDoneText?.nextButtonText ?? '');
r4.setTitle(map.initMfaDoneText?.title ?? '');
req.setInitMfaDoneText(r4);
const r5 = new InitMFAOTPScreenText();
r5.setCancelButtonText(map.initMfaOtpText?.cancelButtonText ?? '');
r5.setCodeLabel(map.initMfaOtpText?.codeLabel ?? '');
r5.setDescription(map.initMfaOtpText?.description ?? '');
r5.setDescriptionOtp(map.initMfaOtpText?.descriptionOtp ?? '');
r5.setNextButtonText(map.initMfaOtpText?.nextButtonText ?? '');
r5.setSecretLabel(map.initMfaOtpText?.secretLabel ?? '');
r5.setTitle(map.initMfaOtpText?.title ?? '');
req.setInitMfaOtpText(r5);
const r6 = new InitMFAPromptScreenText();
r6.setDescription(map.initMfaPromptText?.description ?? '');
r6.setNextButtonText(map.initMfaPromptText?.nextButtonText ?? '');
r6.setOtpOption(map.initMfaPromptText?.otpOption ?? '');
r6.setSkipButtonText(map.initMfaPromptText?.skipButtonText ?? '');
r6.setTitle(map.initMfaPromptText?.title ?? '');
r6.setU2fOption(map.initMfaPromptText?.otpOption ?? '');
req.setInitMfaPromptText(r6);
const r7 = new InitMFAU2FScreenText();
r7.setDescription(map.initMfaU2fText?.description ?? '');
r7.setErrorRetry(map.initMfaU2fText?.errorRetry ?? '');
r7.setNotSupported(map.initMfaU2fText?.notSupported ?? '');
r7.setRegisterTokenButtonText(map.initMfaU2fText?.registerTokenButtonText ?? '');
r7.setTitle(map.initMfaU2fText?.title ?? '');
r7.setTokenNameLabel(map.initMfaU2fText?.tokenNameLabel ?? '');
req.setInitMfaU2fText(r7);
const r8 = new InitPasswordDoneScreenText();
r8.setCancelButtonText(map.initPasswordDoneText?.cancelButtonText ?? '');
r8.setDescription(map.initPasswordDoneText?.description ?? '');
r8.setNextButtonText(map.initPasswordDoneText?.nextButtonText ?? '');
r8.setTitle(map.initPasswordDoneText?.title ?? '');
req.setInitPasswordDoneText(r8);
const r9 = new InitPasswordScreenText();
r9.setCodeLabel(map.initPasswordText?.description ?? '');
r9.setDescription(map.initPasswordText?.description ?? '');
r9.setNewPasswordConfirmLabel(map.initPasswordText?.newPasswordConfirmLabel ?? '');
r9.setNewPasswordLabel(map.initPasswordText?.newPasswordLabel ?? '');
r9.setNextButtonText(map.initPasswordText?.nextButtonText ?? '');
r9.setResendButtonText(map.initPasswordText?.resendButtonText ?? '');
r9.setTitle(map.initPasswordText?.title ?? '');
req.setInitPasswordText(r9);
const r10 = new InitializeUserDoneScreenText();
r10.setCancelButtonText(map.initializeDoneText?.cancelButtonText ?? '');
r10.setDescription(map.initializeDoneText?.description ?? '');
r10.setNextButtonText(map.initializeDoneText?.nextButtonText ?? '');
r10.setTitle(map.initializeDoneText?.title ?? '');
req.setInitializeDoneText(r10);
const r11 = new InitializeUserScreenText();
r11.setCodeLabel(map.initializeUserText?.codeLabel ?? '');
r11.setDescription(map.initializeUserText?.description ?? '');
r11.setNewPasswordConfirmLabel(map.initializeUserText?.newPasswordConfirmLabel ?? '');
r11.setNewPasswordLabel(map.initializeUserText?.newPasswordLabel ?? '');
r11.setNextButtonText(map.initializeUserText?.nextButtonText ?? '');
r11.setResendButtonText(map.initializeUserText?.resendButtonText ?? '');
r11.setTitle(map.initializeUserText?.title ?? '');
req.setInitializeUserText(r11);
const r12 = new LinkingUserDoneScreenText();
r12.setCancelButtonText(map.linkingUserDoneText?.cancelButtonText ?? '');
r12.setDescription(map.linkingUserDoneText?.description ?? '');
r12.setNextButtonText(map.linkingUserDoneText?.nextButtonText ?? '');
r12.setTitle(map.linkingUserDoneText?.title ?? '');
req.setLinkingUserDoneText(r12);
const r13 = new LoginScreenText();
r13.setDescription(map.loginText?.description ?? '');
r13.setDescriptionLinkingProcess(map.loginText?.descriptionLinkingProcess ?? '');
r13.setExternalUserDescription(map.loginText?.externalUserDescription ?? '');
r13.setLoginNameLabel(map.loginText?.loginNameLabel ?? '');
r13.setLoginNamePlaceholder(map.loginText?.loginNamePlaceholder ?? '');
r13.setNextButtonText(map.loginText?.nextButtonText ?? '');
r13.setRegisterButtonText(map.loginText?.registerButtonText ?? '');
r13.setTitle(map.loginText?.title ?? '');
r13.setTitleLinkingProcess(map.loginText?.titleLinkingProcess ?? '');
r13.setUserMustBeMemberOfOrg(map.loginText?.userMustBeMemberOfOrg ?? '');
r13.setUserNamePlaceholder(map.loginText?.userNamePlaceholder ?? '');
req.setLoginText(r13);
const r14 = new LogoutDoneScreenText();
r14.setDescription(map.logoutText?.description ?? '');
r14.setLoginButtonText(map.logoutText?.loginButtonText ?? '');
r14.setTitle(map.logoutText?.title ?? '');
req.setLogoutText(r14);
const r15 = new MFAProvidersText();
r15.setChooseOther(map.mfaProvidersText?.chooseOther ?? '');
r15.setOtp(map.mfaProvidersText?.otp ?? '');
r15.setU2f(map.mfaProvidersText?.u2f ?? '');
req.setMfaProvidersText(r15);
const r16 = new PasswordChangeDoneScreenText();
r16.setDescription(map.passwordChangeDoneText?.description ?? '');
r16.setNextButtonText(map.passwordChangeDoneText?.nextButtonText ?? '');
r16.setTitle(map.passwordChangeDoneText?.title ?? '');
req.setPasswordChangeDoneText(r16);
const r17 = new PasswordChangeScreenText();
r17.setDescription(map.passwordChangeText?.description ?? '');
r17.setNextButtonText(map.passwordChangeText?.nextButtonText ?? '');
r17.setTitle(map.passwordChangeText?.title ?? '');
r17.setNewPasswordLabel(map.passwordChangeText?.newPasswordLabel ?? '');
r17.setNewPasswordConfirmLabel(map.passwordChangeText?.newPasswordConfirmLabel ?? '');
r17.setCancelButtonText(map.passwordChangeText?.cancelButtonText ?? '');
r17.setOldPasswordLabel(map.passwordChangeText?.oldPasswordLabel ?? '');
req.setPasswordChangeText(r17);
const r18 = new PasswordResetDoneScreenText();
r18.setDescription(map.passwordResetDoneText?.description ?? '');
r18.setNextButtonText(map.passwordResetDoneText?.nextButtonText ?? '');
r18.setTitle(map.passwordResetDoneText?.title ?? '');
req.setPasswordResetDoneText(r18);
const r19 = new PasswordScreenText();
r19.setBackButtonText(map.passwordText?.backButtonText ?? '');
r19.setConfirmation(map.passwordText?.confirmation ?? '');
r19.setDescription(map.passwordText?.description ?? '');
r19.setHasLowercase(map.passwordText?.hasLowercase ?? '');
r19.setHasNumber(map.passwordText?.hasNumber ?? '');
r19.setHasSymbol(map.passwordText?.hasSymbol ?? '');
r19.setHasUppercase(map.passwordText?.hasUppercase ?? '');
r19.setMinLength(map.passwordText?.minLength ?? '');
r19.setNextButtonText(map.passwordText?.nextButtonText ?? '');
r19.setPasswordLabel(map.passwordText?.passwordLabel ?? '');
r19.setResetLinkText(map.passwordText?.resetLinkText ?? '');
r19.setTitle(map.passwordText?.title ?? '');
req.setPasswordText(r19);
const r20 = new PasswordlessScreenText();
r20.setDescription(map.passwordlessText?.description ?? '');
r20.setErrorRetry(map.passwordlessText?.errorRetry ?? '');
r20.setLoginWithPwButtonText(map.passwordlessText?.loginWithPwButtonText ?? '');
r20.setNotSupported(map.passwordlessText?.notSupported ?? '');
r20.setTitle(map.passwordlessText?.title ?? '');
r20.setValidateTokenButtonText(map.passwordlessText?.validateTokenButtonText ?? '');
req.setPasswordlessText(r20);
const r21 = new RegistrationOptionScreenText();
r21.setDescription(map.registrationOptionText?.description ?? '');
r21.setExternalLoginDescription(map.registrationOptionText?.externalLoginDescription ?? '');
r21.setTitle(map.registrationOptionText?.title ?? '');
r21.setUserNameButtonText(map.registrationOptionText?.userNameButtonText ?? '');
req.setRegistrationOptionText(r21);
const r22 = new RegistrationOrgScreenText();
r22.setDescription(map.registrationOrgText?.description ?? '');
r22.setEmailLabel(map.registrationOrgText?.emailLabel ?? '');
r22.setFirstnameLabel(map.registrationOrgText?.firstnameLabel ?? '');
r22.setLastnameLabel(map.registrationOrgText?.lastnameLabel ?? '');
r22.setOrgnameLabel(map.registrationOrgText?.orgnameLabel ?? '');
r22.setPasswordConfirmLabel(map.registrationOrgText?.passwordConfirmLabel ?? '');
r22.setPasswordLabel(map.registrationOrgText?.passwordLabel ?? '');
r22.setTosConfirmAnd(map.registrationOrgText?.tosConfirm ?? '');
r22.setPrivacyLinkText(map.registrationOrgText?.privacyLinkText ?? '');
r22.setSaveButtonText(map.registrationOrgText?.saveButtonText ?? '');
r22.setTitle(map.registrationOrgText?.title ?? '');
r22.setTosAndPrivacyLabel(map.registrationOrgText?.tosAndPrivacyLabel ?? '');
r22.setTosConfirm(map.registrationOrgText?.tosConfirm ?? '');
r22.setTosConfirmAnd(map.registrationOrgText?.tosConfirmAnd ?? '');
r22.setTosLinkText(map.registrationOrgText?.tosLinkText ?? '');
r22.setUsernameLabel(map.registrationOrgText?.usernameLabel ?? '');
req.setRegistrationOrgText(r22);
const r23 = new RegistrationUserScreenText();
r23.setBackButtonText(map.registrationUserText?.backButtonText ?? '');
r23.setDescription(map.registrationUserText?.description ?? '');
r23.setDescriptionOrgRegister(map.registrationUserText?.descriptionOrgRegister ?? '');
r23.setEmailLabel(map.registrationUserText?.emailLabel ?? '');
r23.setFirstnameLabel(map.registrationUserText?.firstnameLabel ?? '');
r23.setGenderLabel(map.registrationUserText?.genderLabel ?? '');
r23.setLanguageLabel(map.registrationUserText?.languageLabel ?? '');
r23.setLastnameLabel(map.registrationUserText?.lastnameLabel ?? '');
r23.setNextButtonText(map.registrationUserText?.nextButtonText ?? '');
r23.setPasswordConfirmLabel(map.registrationUserText?.passwordConfirmLabel ?? '');
r23.setPasswordLabel(map.registrationUserText?.passwordLabel ?? '');
r23.setTosConfirm(map.registrationUserText?.tosConfirm ?? '');
r23.setTosConfirmAnd(map.registrationUserText?.tosConfirmAnd ?? '');
r23.setTosLinkText(map.registrationUserText?.tosLinkText ?? '');
r23.setPrivacyLinkText(map.registrationUserText?.privacyLinkText ?? '');
r23.setTitle(map.registrationUserText?.title ?? '');
r23.setTosAndPrivacyLabel(map.registrationUserText?.tosAndPrivacyLabel ?? '');
r23.setTosConfirm(map.registrationUserText?.tosConfirm ?? '');
r23.setUsernameLabel(map.registrationUserText?.usernameLabel ?? '');
req.setRegistrationUserText(r23);
const r24 = new SelectAccountScreenText();
r24.setDescription(map.selectAccountText?.description ?? '');
r24.setDescriptionLinkingProcess(map.selectAccountText?.descriptionLinkingProcess ?? '');
r24.setOtherUser(map.selectAccountText?.otherUser ?? '');
r24.setSessionStateActive(map.selectAccountText?.sessionStateActive ?? '');
r24.setSessionStateInactive(map.selectAccountText?.sessionStateInactive ?? '');
r24.setTitle(map.selectAccountText?.title ?? '');
r24.setTitleLinkingProcess(map.selectAccountText?.titleLinkingProcess ?? '');
r24.setUserMustBeMemberOfOrg(map.selectAccountText?.userMustBeMemberOfOrg ?? '');
req.setSelectAccountText(r24);
const r25 = new SuccessLoginScreenText();
r25.setAutoRedirectDescription(map.successLoginText?.autoRedirectDescription ?? '');
r25.setNextButtonText(map.successLoginText?.nextButtonText ?? '');
r25.setRedirectedDescription(map.successLoginText?.redirectedDescription ?? '');
r25.setTitle(map.successLoginText?.title ?? '');
req.setSuccessLoginText(r25);
const r26 = new UsernameChangeDoneScreenText();
r26.setDescription(map.usernameChangeDoneText?.description ?? '');
r26.setNextButtonText(map.usernameChangeDoneText?.nextButtonText ?? '');
r26.setTitle(map.usernameChangeDoneText?.title ?? '');
req.setUsernameChangeDoneText(r26);
const r27 = new UsernameChangeScreenText();
r27.setCancelButtonText(map.usernameChangeText?.cancelButtonText ?? '');
r27.setDescription(map.usernameChangeText?.description ?? '');
r27.setNextButtonText(map.usernameChangeText?.nextButtonText ?? '');
r27.setTitle(map.usernameChangeText?.title ?? '');
r27.setUsernameLabel(map.usernameChangeText?.usernameLabel ?? '');
req.setUsernameChangeText(r27);
const r28 = new VerifyMFAOTPScreenText();
r28.setCodeLabel(map.verifyMfaOtpText?.codeLabel ?? '');
r28.setDescription(map.verifyMfaOtpText?.description ?? '');
r28.setNextButtonText(map.verifyMfaOtpText?.nextButtonText ?? '');
r28.setTitle(map.verifyMfaOtpText?.title ?? '');
req.setVerifyMfaOtpText(r28);
const r29 = new VerifyMFAU2FScreenText();
r29.setDescription(map.verifyMfaU2fText?.description ?? '');
r29.setErrorRetry(map.verifyMfaU2fText?.errorRetry ?? '');
r29.setNotSupported(map.verifyMfaU2fText?.notSupported ?? '');
r29.setTitle(map.verifyMfaU2fText?.title ?? '');
r29.setValidateTokenText(map.verifyMfaU2fText?.validateTokenText ?? '');
req.setVerifyMfaU2fText(r29);
return req;
}

View File

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

View File

@@ -0,0 +1,60 @@
<app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
[title]="'POLICY.LOGIN_TEXTS.TITLE' | translate"
[description]="'POLICY.LOGIN_TEXTS.DESCRIPTION' | translate">
<div class="date">
<div>
<p class="newer-title" *ngIf="newerVersionExists">{{'POLICY.LOGIN_TEXTS.NEWERVERSIONEXISTS' | translate}}</p>
<p *ngIf="newerPolicyChangeDate && newerVersionExists">{{'POLICY.LOGIN_TEXTS.CHANGEDATE' | translate}}: {{newerPolicyChangeDate | timestampToDate | localizedDate: 'dd. MMMM YYYY, HH:mm:ss'}}</p>
<p class="gray" *ngIf="currentPolicyChangeDate">{{'POLICY.LOGIN_TEXTS.CURRENTDATE' | translate}}: {{currentPolicyChangeDate | timestampToDate | localizedDate: 'dd. MMMM YYYY, HH:mm:ss'}}</p>
</div>
<button [disabled]="!newerVersionExists" color="primary" mat-raised-button (click)="loadData()">
<i class="las la-sync-alt"></i>
{{'ACTIONS.REFRESH' | translate}}</button>
</div>
<form *ngIf="form" class="top-actions" [formGroup]="form">
<cnsl-form-field class="keys" appearance="outline" >
<cnsl-label>{{ 'POLICY.LOGIN_TEXTS.KEYNAME' | translate }}</cnsl-label>
<mat-select formControlName="currentSubMap" name="currentSubMap">
<mat-option *ngFor="let key of KeyNamesArray" [value]="key">
{{'POLICY.LOGIN_TEXTS.KEYS.'+key | translate }}
</mat-option>
</mat-select>
</cnsl-form-field>
<cnsl-form-field class="language">
<cnsl-label>{{ 'POLICY.LOGIN_TEXTS.LOCALE' | translate }}</cnsl-label>
<mat-select formControlName="locale" name="locale">
<mat-option *ngFor="let loc of LOCALES" [value]="loc">
<div class="centerline">
<span>{{loc}} <span class="lighter">|&nbsp;{{'POLICY.LOGIN_TEXTS.LOCALES.'+loc | translate }}</span></span>
</div>
</mat-option>
</mat-select>
</cnsl-form-field>
</form>
<cnsl-info-section class="warn"
*ngIf="serviceType == PolicyComponentServiceType.MGMT && (['custom_text'] | hasFeature | async) == false"
type="WARN">
{{'FEATURES.NOTAVAILABLE' | translate: ({value:
'custom_text'})}}
</cnsl-info-section>
<div class="divider"></div>
<div class="content" >
<cnsl-edit-text label="one" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['custom_text'] | hasFeature | async) == false" [default$]="getDefaultInitMessageTextMap$" [current$]="getCustomInitMessageTextMap$" (changedValues)="updateCurrentValues(
$event)"></cnsl-edit-text>
</div>
<div class="actions">
<button class="reset-button" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['custom_text'] | hasFeature | async) == false" (click)="resetDefault()" color="warn" type="submit"
mat-stroked-button><i class="las la-history"></i> {{ 'ACTIONS.RESETDEFAULT' | translate }}</button>
<button class="save-button" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['custom_text'] | hasFeature | async) == false" (click)="saveCurrentMessage()" color="primary" type="submit"
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
</div>
<app-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="text"></app-policy-grid>
</app-detail-layout>

View File

@@ -0,0 +1,108 @@
.spinner-wr {
margin: .5rem 0;
}
.date {
display: flex;
justify-content: space-between;
p {
margin: .4rem 0;
font-size: 14px;
&.gray {
color: var(--grey);
}
}
.newer-title {
font-size: 1rem;
margin-bottom: 1rem;
}
button {
align-self: center;
i {
margin-bottom: 3px;
margin-right: .5rem;
}
}
}
.top-actions {
display: flex;
margin: 0 -.5rem;
flex-wrap: wrap;
.keys {
flex: 1;
margin: 0 .5rem;
min-width: 150px;
}
.language {
margin: 0 .5rem;
min-width: 150px;
.lighter {
font-size: 12px;
color: var(--grey);
padding: 0 .5rem;
}
}
}
.centerline {
display: flex;
align-items: center;
}
.content {
padding-top: 1rem;
}
.chips {
display: flex;
margin: 0 -.25rem;
.chip {
border-radius: 50vw;
padding: 2px .5rem;
font-size: 12px;
background: #cbf4c9;
color: #0e6245;
margin: .25rem;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
}
}
.divider {
width: 100%;
height: 1px;
background-color: rgba(#81868a, .5);
margin: 1.5rem 0 0 0;
}
.actions {
display: flex;
justify-content: flex-end;
.save-button,
.reset-button {
display: block;
margin: 0 .25rem 3rem .25rem;
}
.reset-button {
display: flex;
align-items: center;
i {
margin-bottom: 3px;
}
}
}

View File

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

View File

@@ -0,0 +1,346 @@
import { Component, Injector, OnDestroy, Type } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute } from '@angular/router';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { BehaviorSubject, from, interval, Observable, of, Subject, Subscription } from 'rxjs';
import { map, pairwise, startWith, switchMap, takeUntil } from 'rxjs/operators';
import {
GetCustomLoginTextsRequest as AdminGetCustomLoginTextsRequest,
GetDefaultLoginTextsRequest as AdminGetDefaultLoginTextsRequest,
SetCustomLoginTextsRequest as AdminSetCustomLoginTextsRequest,
} from 'src/app/proto/generated/zitadel/admin_pb';
import {
GetCustomLoginTextsRequest,
GetDefaultLoginTextsRequest,
SetCustomLoginTextsRequest,
} from 'src/app/proto/generated/zitadel/management_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 { GridPolicy, LOGIN_TEXTS_POLICY } from '../../policy-grid/policies';
import { WarnDialogComponent } from '../../warn-dialog/warn-dialog.component';
import { PolicyComponentServiceType } from '../policy-component-types.enum';
import { mapRequestValues } from './helper';
// tslint:disable
const KeyNamesArray = [
'emailVerificationDoneText',
'emailVerificationText',
'externalUserNotFoundText',
'footerText',
'initMfaDoneText',
'initMfaOtpText',
'initMfaPromptText',
'initMfaU2fText',
'initPasswordDoneText',
'initPasswordText',
'initializeDoneText',
'initializeUserText',
'linkingUserDoneText',
'loginText',
'logoutText',
'mfaProvidersText',
'passwordChangeDoneText',
'passwordChangeText',
'passwordResetDoneText',
'passwordText',
'passwordlessText',
'registrationOptionText',
'registrationOrgText',
'registrationUserText',
'selectAccountText',
'successLoginText',
'usernameChangeDoneText',
'usernameChangeText',
'verifyMfaOtpText',
'verifyMfaU2fText',
];
// tslint:enable
const REQUESTMAP = {
[PolicyComponentServiceType.MGMT]: {
get: new GetCustomLoginTextsRequest(),
set: new SetCustomLoginTextsRequest(),
getDefault: new GetDefaultLoginTextsRequest(),
setFcn: (mgmtmap: Partial<SetCustomLoginTextsRequest.AsObject>): SetCustomLoginTextsRequest => {
let req = new SetCustomLoginTextsRequest();
req.setLanguage(mgmtmap.language ?? '');
req = mapRequestValues(mgmtmap, req);
return req;
},
},
[PolicyComponentServiceType.ADMIN]: {
get: new AdminGetCustomLoginTextsRequest(),
set: new AdminSetCustomLoginTextsRequest(),
getDefault: new AdminGetDefaultLoginTextsRequest(),
setFcn: (adminmap: Partial<AdminSetCustomLoginTextsRequest.AsObject>): AdminSetCustomLoginTextsRequest => {
let req = new AdminSetCustomLoginTextsRequest();
req.setLanguage(adminmap.language ?? '');
req = mapRequestValues(adminmap, req);
return req;
},
},
};
@Component({
selector: 'app-login-texts',
templateUrl: './login-texts.component.html',
styleUrls: ['./login-texts.component.scss'],
})
export class LoginTextsComponent implements OnDestroy {
public currentPolicyChangeDate!: Timestamp.AsObject | undefined;
public newerPolicyChangeDate!: Timestamp.AsObject | undefined;
public totalCustomPolicy: { [key: string]: { [key: string]: string; }; } = {};
public getDefaultInitMessageTextMap$: Observable<{ [key: string]: string; }> = of({});
public getCustomInitMessageTextMap$: BehaviorSubject<{ [key: string]: string; }> = new BehaviorSubject({});
public service!: ManagementService | AdminService;
public PolicyComponentServiceType: any = PolicyComponentServiceType;
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
public KeyNamesArray: string[] = KeyNamesArray;
public LOCALES: string[] = ['en'];
private sub: Subscription = new Subscription();
public updateRequest!: SetCustomLoginTextsRequest;
public currentPolicy: GridPolicy = LOGIN_TEXTS_POLICY;
public destroy$: Subject<void> = new Subject();
public form: FormGroup = new FormGroup({
currentSubMap: new FormControl('emailVerificationDoneText'),
locale: new FormControl('en'),
});
constructor(
private route: ActivatedRoute,
private injector: Injector,
private dialog: MatDialog,
private toast: ToastService,
) {
this.sub = this.route.data.pipe(switchMap(data => {
this.serviceType = data.serviceType;
switch (this.serviceType) {
case PolicyComponentServiceType.MGMT:
this.service = this.injector.get(ManagementService as Type<ManagementService>);
this.service.getSupportedLanguages().then(lang => {
this.LOCALES = lang.languagesList;
});
this.loadData();
break;
case PolicyComponentServiceType.ADMIN:
this.service = this.injector.get(AdminService as Type<AdminService>);
this.service.getSupportedLanguages().then(lang => {
this.LOCALES = lang.languagesList;
});
this.loadData();
break;
}
return this.route.params;
})).subscribe(() => {
interval(10000).pipe(
// debounceTime(5000),
takeUntil(this.destroy$),
).subscribe(x => {
this.checkForChanges();
});
});
this.form.valueChanges.pipe(
startWith({ currentSubMap: 'emailVerificationDoneText', locale: 'en' }),
pairwise(),
takeUntil(this.destroy$),
).subscribe(pair => {
this.checkForUnsaved(pair[0].currentSubMap).then((wantsToSave) => {
if (wantsToSave) {
this.saveCurrentMessage().then(() => {
this.loadData();
}).catch(() => {
// load even if save failed
this.loadData();
});
} else {
this.loadData();
}
});
});
}
public getDefaultValues(req: any): Promise<any> {
return this.service.getDefaultLoginTexts(req).then((res) => {
if (res.customText) {
// delete res.customText.details;
return Object.assign({}, res.customText);
} else {
return {};
}
});
}
public getCurrentValues(req: any): Promise<any> {
return (this.service as ManagementService).getCustomLoginTexts(req).then((res) => {
if (res.customText) {
this.currentPolicyChangeDate = res.customText.details?.changeDate;
// delete res.customText.details;
return Object.assign({}, res.customText);
} else {
return {};
}
});
}
public async loadData(): Promise<any> {
const reqDefaultInit = REQUESTMAP[this.serviceType].getDefault;
reqDefaultInit.setLanguage(this.locale);
this.getDefaultInitMessageTextMap$ = from(
this.getDefaultValues(reqDefaultInit),
).pipe(map(m => m[this.currentSubMap]));
const reqCustomInit = REQUESTMAP[this.serviceType].get.setLanguage(this.locale);
this.totalCustomPolicy = (await this.getCurrentValues(reqCustomInit));
this.getCustomInitMessageTextMap$.next(
this.totalCustomPolicy[this.currentSubMap],
);
}
private async patchSingleCurrentMap(): Promise<any> {
const reqCustomInit = REQUESTMAP[this.serviceType].get.setLanguage(this.locale);
const pol = (await this.getCurrentValues(reqCustomInit));
this.getCustomInitMessageTextMap$.next(
pol[this.currentSubMap],
);
}
public checkForChanges(): void {
const reqCustomInit = REQUESTMAP[this.serviceType].get.setLanguage(this.locale);
(this.service as ManagementService).getCustomLoginTexts(reqCustomInit).then(policy => {
this.newerPolicyChangeDate = policy.customText?.details?.changeDate;
});
}
/**
*
* @param oldkey which was potentially unsaved
* @returns a boolean if saving is desired
*/
public checkForUnsaved(oldkey: string): Promise<boolean> {
const old = this.getCustomInitMessageTextMap$.getValue();
const unsaved = this.totalCustomPolicy[oldkey];
if (old && unsaved && JSON.stringify(old) !== JSON.stringify(unsaved)) {
const dialogRef = this.dialog.open(WarnDialogComponent, {
data: {
confirmKey: 'ACTIONS.SAVE',
cancelKey: 'ACTIONS.CONTINUEWITHOUTSAVE',
titleKey: 'POLICY.LOGIN_TEXTS.UNSAVED_TITLE',
descriptionKey: 'POLICY.LOGIN_TEXTS.UNSAVED_DESCRIPTION',
},
width: '400px',
});
return dialogRef.afterClosed().toPromise();
} else {
return Promise.resolve(false);
}
}
public updateCurrentValues(values: { [key: string]: string; }): void {
const setFcn = REQUESTMAP[this.serviceType].setFcn;
this.totalCustomPolicy[this.currentSubMap] = values;
this.updateRequest = setFcn(this.totalCustomPolicy);
this.updateRequest.setLanguage(this.locale);
}
public saveCurrentMessage(): Promise<any> {
const entirePayload = this.updateRequest.toObject();
this.getCustomInitMessageTextMap$.next(
(entirePayload as any)[this.currentSubMap],
);
switch (this.serviceType) {
case PolicyComponentServiceType.MGMT:
return (this.service as ManagementService).setCustomLoginText(this.updateRequest).then(() => {
this.toast.showInfo('POLICY.MESSAGE_TEXTS.TOAST.UPDATED', true);
setTimeout(() => {
this.patchSingleCurrentMap();
}, 1000);
}).catch(error => this.toast.showError(error));
case PolicyComponentServiceType.ADMIN:
return (this.service as AdminService).setCustomLoginText(this.updateRequest).then(() => {
this.toast.showInfo('POLICY.MESSAGE_TEXTS.TOAST.UPDATED', true);
}).catch(error => this.toast.showError(error));
}
}
public resetDefault(): void {
const dialogRef = this.dialog.open(WarnDialogComponent, {
data: {
icon: 'las la-history',
confirmKey: 'ACTIONS.RESTORE',
cancelKey: 'ACTIONS.CANCEL',
titleKey: 'POLICY.LOGIN_TEXTS.RESET_TITLE',
descriptionKey: 'POLICY.LOGIN_TEXTS.RESET_DESCRIPTION',
},
width: '400px',
});
dialogRef.afterClosed().subscribe(resp => {
if (resp) {
if (this.serviceType === PolicyComponentServiceType.MGMT) {
(this.service as ManagementService).resetCustomLoginTextToDefault(this.locale).then(() => {
setTimeout(() => {
this.loadData();
}, 1000);
}).catch(error => {
this.toast.showError(error);
});
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
(this.service as AdminService).resetCustomLoginTextToDefault(this.locale).then(() => {
setTimeout(() => {
this.loadData();
}, 1000);
}).catch(error => {
this.toast.showError(error);
});
}
}
});
}
public ngOnDestroy(): void {
this.sub.unsubscribe();
this.destroy$.next();
this.destroy$.complete();
}
public get newerVersionExists(): boolean {
const toDate = (ts: Timestamp.AsObject) => {
return new Date(ts.seconds * 1000 + ts.nanos / 1000 / 1000);
};
if (this.newerPolicyChangeDate && this.currentPolicyChangeDate) {
const ms = toDate(this.newerPolicyChangeDate).getTime() - toDate(this.currentPolicyChangeDate).getTime();
// show button if changes are newer than 10s
return ms / 1000 > 10;
} else {
return false;
}
}
public get locale(): string {
return this.form.get('locale')?.value;
}
public get currentSubMap(): string {
return this.form.get('currentSubMap')?.value;
}
}

View File

@@ -0,0 +1,58 @@
import { TextFieldModule } from '@angular/cdk/text-field';
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 { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module';
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module';
import { HasRoleModule } from '../../../directives/has-role/has-role.module';
import { DetailLayoutModule } from '../../../modules/detail-layout/detail-layout.module';
import { InputModule } from '../../../modules/input/input.module';
import { HasFeaturePipeModule } from '../../../pipes/has-feature-pipe/has-feature-pipe.module';
import { HasRolePipeModule } from '../../../pipes/has-role-pipe/has-role-pipe.module';
import { EditTextModule } from '../../edit-text/edit-text.module';
import { FormFieldModule } from '../../form-field/form-field.module';
import { InfoSectionModule } from '../../info-section/info-section.module';
import { PolicyGridModule } from '../../policy-grid/policy-grid.module';
import { WarnDialogModule } from '../../warn-dialog/warn-dialog.module';
import { LoginTextsRoutingModule } from './login-texts-routing.module';
import { LoginTextsComponent } from './login-texts.component';
@NgModule({
declarations: [LoginTextsComponent],
imports: [
LoginTextsRoutingModule,
MatSelectModule,
CommonModule,
InfoSectionModule,
ReactiveFormsModule,
FormsModule,
InputModule,
FormFieldModule,
EditTextModule,
MatButtonModule,
HasFeaturePipeModule,
MatIconModule,
HasRoleModule,
HasRolePipeModule,
MatTooltipModule,
TranslateModule,
MatTooltipModule,
DetailLayoutModule,
MatProgressSpinnerModule,
TextFieldModule,
MatDialogModule,
WarnDialogModule,
PolicyGridModule,
TimestampToDatePipeModule,
LocalizedDatePipeModule,
],
})
export class LoginTextsPolicyModule { }

View File

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

View File

@@ -0,0 +1,51 @@
<app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
[title]="'POLICY.MESSAGE_TEXTS.TITLE' | translate"
[description]="'POLICY.MESSAGE_TEXTS.DESCRIPTION' | translate">
<div class="top-actions">
<div class="message-type">
<button (click)="setCurrentType(type.value)" [ngClass]="{'active': currentType == type.value}" mat-button *ngFor="let type of MESSAGETYPES | keyvalue">{{'POLICY.MESSAGE_TEXTS.TYPES.'+type.value | translate}}</button>
</div>
<cnsl-form-field class="language">
<cnsl-label>{{ 'POLICY.LOGIN_TEXTS.LOCALE' | translate }}</cnsl-label>
<mat-select [(ngModel)]="locale" name="locale" (selectionChange)="changeLocale($event)">
<mat-option *ngFor="let loc of LOCALES" [value]="loc">
<div class="centerline">
<span>{{loc}} <span class="lighter">|&nbsp;{{'POLICY.LOGIN_TEXTS.LOCALES.'+loc | translate }}</span></span>
</div>
</mat-option>
</mat-select>
</cnsl-form-field>
</div>
<cnsl-info-section class="warn"
*ngIf="serviceType == PolicyComponentServiceType.MGMT && (['custom_text'] | hasFeature | async) == false"
type="WARN">
{{'FEATURES.NOTAVAILABLE' | translate: ({value:
'custom_text'})}}
</cnsl-info-section>
<div class="content" >
<cnsl-edit-text [chips]="chips" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['custom_text'] | hasFeature | async) == false" label="one" [default$]="getDefaultInitMessageTextMap$" [current$]="getCustomInitMessageTextMap$" (changedValues)="updateCurrentValues(
$event)"></cnsl-edit-text>
</div>
<div class="actions">
<button class="reset-button" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['custom_text'] | hasFeature | async) == false" (click)="resetDefault()" color="warn" type="submit"
mat-stroked-button><i class="las la-history"></i> {{ 'ACTIONS.RESETDEFAULT' | translate }}</button>
<button class="save-button" [disabled]="!updateRequest || serviceType == PolicyComponentServiceType.MGMT && (['custom_text'] | hasFeature | async) == false" (click)="saveCurrentMessage()" color="primary" type="submit"
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
</div>
<div class="divider"></div>
<ng-container
*ngIf="serviceType == PolicyComponentServiceType.MGMT && (['login_policy.idp'] | hasFeature | async) == false">
<cnsl-info-section type="WARN">{{'FEATURES.NOTAVAILABLE' | translate: ({value:
'login_policy.idp'})}}
</cnsl-info-section>
</ng-container>
<app-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="text"></app-policy-grid>
</app-detail-layout>

View File

@@ -0,0 +1,69 @@
.spinner-wr {
margin: .5rem 0;
}
.top-actions {
display: flex;
margin: 0 -.5rem;
justify-content: space-between;
align-items: flex-end;
.message-type {
display: flex;
flex-wrap: wrap;
margin-bottom: 11px;
.active {
background: #cbf4c9;
color: #0e6245;
}
}
.language {
display: block;
margin: 0 .5rem;
min-width: 150px;
.lighter {
font-size: 12px;
color: var(--grey);
padding: 0 .5rem;
}
}
}
.warn {
margin-top: 1rem;
display: block;
}
.content {
padding-top: 1rem;
}
.divider {
width: 100%;
height: 1px;
background-color: rgba(var(--grey), .5);
margin: 1rem 0;
}
.actions {
display: flex;
justify-content: flex-end;
.save-button,
.reset-button {
display: block;
margin: 0 .25rem 3rem .25rem;
}
.reset-button {
display: flex;
align-items: center;
i {
margin-bottom: 3px;
}
}
}

View File

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

View File

@@ -0,0 +1,461 @@
import { Component, Injector, OnDestroy, Type } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatSelectChange } from '@angular/material/select';
import { ActivatedRoute } from '@angular/router';
import { BehaviorSubject, from, Observable, of, Subscription } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import {
GetCustomPasswordResetMessageTextRequest as AdminGetCustomPasswordResetMessageTextRequest,
GetDefaultInitMessageTextRequest as AdminGetDefaultInitMessageTextRequest,
GetDefaultVerifyEmailMessageTextRequest as AdminGetDefaultVerifyEmailMessageTextRequest,
GetDefaultVerifyPhoneMessageTextRequest as AdminGetDefaultVerifyPhoneMessageTextRequest,
SetDefaultDomainClaimedMessageTextRequest,
SetDefaultInitMessageTextRequest,
SetDefaultPasswordResetMessageTextRequest,
SetDefaultVerifyEmailMessageTextRequest,
SetDefaultVerifyPhoneMessageTextRequest,
} from 'src/app/proto/generated/zitadel/admin_pb';
import {
GetCustomDomainClaimedMessageTextRequest,
GetCustomPasswordResetMessageTextRequest,
GetCustomVerifyEmailMessageTextRequest,
GetCustomVerifyPhoneMessageTextRequest,
GetDefaultDomainClaimedMessageTextRequest,
GetDefaultInitMessageTextRequest,
GetDefaultPasswordResetMessageTextRequest,
GetDefaultVerifyEmailMessageTextRequest,
GetDefaultVerifyPhoneMessageTextRequest,
SetCustomDomainClaimedMessageTextRequest,
SetCustomInitMessageTextRequest,
SetCustomPasswordResetMessageTextRequest,
SetCustomVerifyEmailMessageTextRequest,
SetCustomVerifyPhoneMessageTextRequest,
} from 'src/app/proto/generated/zitadel/management_pb';
import { MessageCustomText } from 'src/app/proto/generated/zitadel/text_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 { GridPolicy, MESSAGE_TEXTS_POLICY } from '../../policy-grid/policies';
import { WarnDialogComponent } from '../../warn-dialog/warn-dialog.component';
import { PolicyComponentServiceType } from '../policy-component-types.enum';
enum MESSAGETYPES {
INIT = 'INIT',
VERIFYPHONE = 'VP',
VERIFYEMAIL = 'VE',
PASSWORDRESET = 'PR',
DOMAINCLAIMED = 'DC',
}
const REQUESTMAP = {
[PolicyComponentServiceType.MGMT]: {
[MESSAGETYPES.INIT]: {
get: new GetDefaultInitMessageTextRequest(),
set: new SetCustomInitMessageTextRequest(),
getDefault: new GetDefaultInitMessageTextRequest(),
setFcn: (map: Partial<MessageCustomText.AsObject>): SetCustomInitMessageTextRequest => {
const req = new SetCustomInitMessageTextRequest();
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.VERIFYEMAIL]: {
get: new GetCustomVerifyEmailMessageTextRequest(),
set: new SetCustomVerifyEmailMessageTextRequest(),
getDefault: new GetDefaultVerifyEmailMessageTextRequest(),
setFcn: (map: Partial<MessageCustomText.AsObject>): SetCustomVerifyEmailMessageTextRequest => {
const req = new SetCustomVerifyEmailMessageTextRequest();
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.VERIFYPHONE]: {
get: new GetCustomVerifyPhoneMessageTextRequest(),
set: new SetCustomVerifyPhoneMessageTextRequest(),
getDefault: new GetDefaultVerifyPhoneMessageTextRequest(),
setFcn: (map: Partial<MessageCustomText.AsObject>): SetCustomVerifyPhoneMessageTextRequest => {
const req = new SetCustomVerifyPhoneMessageTextRequest();
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.PASSWORDRESET]: {
get: new GetCustomPasswordResetMessageTextRequest(),
set: new SetCustomPasswordResetMessageTextRequest(),
getDefault: new GetDefaultPasswordResetMessageTextRequest(),
setFcn: (map: Partial<SetCustomPasswordResetMessageTextRequest.AsObject>):
SetCustomPasswordResetMessageTextRequest => {
const req = new SetCustomPasswordResetMessageTextRequest();
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.DOMAINCLAIMED]: {
get: new GetCustomDomainClaimedMessageTextRequest(),
set: new SetCustomDomainClaimedMessageTextRequest(),
getDefault: new GetDefaultDomainClaimedMessageTextRequest(),
setFcn: (map: Partial<SetCustomDomainClaimedMessageTextRequest.AsObject>):
SetCustomDomainClaimedMessageTextRequest => {
const req = new SetCustomDomainClaimedMessageTextRequest();
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;
},
},
},
[PolicyComponentServiceType.ADMIN]: {
[MESSAGETYPES.INIT]: {
get: new AdminGetDefaultInitMessageTextRequest(),
set: new SetDefaultInitMessageTextRequest(),
setFcn: (map: Partial<MessageCustomText.AsObject>):
SetDefaultInitMessageTextRequest => {
const req = new SetDefaultInitMessageTextRequest();
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.VERIFYEMAIL]: {
get: new AdminGetDefaultVerifyEmailMessageTextRequest(),
set: new SetDefaultVerifyEmailMessageTextRequest(),
setFcn: (map: Partial<MessageCustomText.AsObject>):
SetDefaultVerifyEmailMessageTextRequest => {
const req = new SetDefaultVerifyEmailMessageTextRequest();
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.VERIFYPHONE]: {
get: new AdminGetDefaultVerifyPhoneMessageTextRequest(),
set: new SetDefaultVerifyPhoneMessageTextRequest(),
setFcn: (map: Partial<MessageCustomText.AsObject>):
SetDefaultVerifyPhoneMessageTextRequest => {
const req = new SetDefaultVerifyPhoneMessageTextRequest();
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.PASSWORDRESET]: {
get: new AdminGetCustomPasswordResetMessageTextRequest(),
set: new SetDefaultPasswordResetMessageTextRequest(),
setFcn: (map: Partial<SetDefaultPasswordResetMessageTextRequest.AsObject>):
SetDefaultPasswordResetMessageTextRequest => {
const req = new SetDefaultPasswordResetMessageTextRequest();
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.DOMAINCLAIMED]: {
get: new GetDefaultDomainClaimedMessageTextRequest(),
set: new SetDefaultDomainClaimedMessageTextRequest(),
setFcn: (map: Partial<SetDefaultDomainClaimedMessageTextRequest.AsObject>):
SetDefaultDomainClaimedMessageTextRequest => {
const req = new SetDefaultDomainClaimedMessageTextRequest();
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;
},
},
},
};
@Component({
selector: 'app-message-texts',
templateUrl: './message-texts.component.html',
styleUrls: ['./message-texts.component.scss'],
})
export class MessageTextsComponent implements OnDestroy {
public getDefaultInitMessageTextMap$: Observable<{ [key: string]: string; }> = of({});
public getCustomInitMessageTextMap$: BehaviorSubject<{ [key: string]: string; }> = new BehaviorSubject({});
public currentType: MESSAGETYPES = MESSAGETYPES.INIT;
public service!: ManagementService | AdminService;
public PolicyComponentServiceType: any = PolicyComponentServiceType;
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
public MESSAGETYPES: any = MESSAGETYPES;
public updateRequest!: SetCustomInitMessageTextRequest | SetDefaultInitMessageTextRequest;
public chips: any[] = [
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.firstname', value: '{{.FirstName}}' },
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.lastname', value: '{{.Lastname}}' },
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.code', value: '{{.Code}}' },
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.preferredLoginName', value: '{{.PreferredLoginName}}' },
];
public locale: string = 'en';
public LOCALES: string[] = ['en'];
private sub: Subscription = new Subscription();
public currentPolicy: GridPolicy = MESSAGE_TEXTS_POLICY;
constructor(
private route: ActivatedRoute,
private toast: ToastService,
private injector: Injector,
private dialog: MatDialog,
) {
this.sub = this.route.data.pipe(switchMap(data => {
this.serviceType = data.serviceType;
switch (this.serviceType) {
case PolicyComponentServiceType.MGMT:
this.service = this.injector.get(ManagementService as Type<ManagementService>);
this.service.getSupportedLanguages().then(lang => {
this.LOCALES = lang.languagesList;
});
this.loadData(this.currentType);
break;
case PolicyComponentServiceType.ADMIN:
this.service = this.injector.get(AdminService as Type<AdminService>);
this.service.getSupportedLanguages().then(lang => {
this.LOCALES = lang.languagesList;
});
this.loadData(this.currentType);
break;
}
return this.route.params;
})).subscribe(() => {
});
}
public getDefaultValues(type: MESSAGETYPES, req: any): Promise<any> {
switch (type) {
case MESSAGETYPES.INIT:
return this.stripDetails((this.service).getDefaultInitMessageText(req));
case MESSAGETYPES.VERIFYPHONE:
return this.stripDetails((this.service).getDefaultVerifyPhoneMessageText(req));
case MESSAGETYPES.VERIFYEMAIL:
return this.stripDetails((this.service).getDefaultVerifyEmailMessageText(req));
case MESSAGETYPES.PASSWORDRESET:
return this.stripDetails((this.service).getDefaultPasswordResetMessageText(req));
case MESSAGETYPES.DOMAINCLAIMED:
return this.stripDetails((this.service).getDefaultDomainClaimedMessageText(req));
}
}
public getCurrentValues(type: MESSAGETYPES, req: any): Promise<any> | undefined {
if (this.serviceType === PolicyComponentServiceType.MGMT) {
switch (type) {
case MESSAGETYPES.INIT:
return this.stripDetails((this.service as ManagementService).getCustomInitMessageText(req));
case MESSAGETYPES.VERIFYPHONE:
return this.stripDetails((this.service as ManagementService).getCustomVerifyPhoneMessageText(req));
case MESSAGETYPES.VERIFYEMAIL:
return this.stripDetails((this.service as ManagementService).getCustomVerifyEmailMessageText(req));
case MESSAGETYPES.PASSWORDRESET:
return this.stripDetails((this.service as ManagementService).getCustomPasswordResetMessageText(req));
case MESSAGETYPES.DOMAINCLAIMED:
return this.stripDetails((this.service as ManagementService).getCustomDomainClaimedMessageText(req));
}
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
switch (type) {
case MESSAGETYPES.INIT:
return this.stripDetails((this.service as AdminService).getCustomInitMessageText(req));
case MESSAGETYPES.VERIFYPHONE:
return this.stripDetails((this.service as AdminService).getCustomVerifyPhoneMessageText(req));
case MESSAGETYPES.VERIFYEMAIL:
return this.stripDetails((this.service as AdminService).getCustomVerifyEmailMessageText(req));
case MESSAGETYPES.PASSWORDRESET:
return this.stripDetails((this.service as AdminService).getCustomPasswordResetMessageText(req));
case MESSAGETYPES.DOMAINCLAIMED:
return this.stripDetails((this.service as AdminService).getCustomDomainClaimedMessageText(req));
}
}
}
public changeLocale(selection: MatSelectChange): void {
this.locale = selection.value;
this.loadData(this.currentType);
}
public async loadData(type: MESSAGETYPES): Promise<any> {
if (this.serviceType === PolicyComponentServiceType.MGMT) {
const reqDefaultInit = REQUESTMAP[this.serviceType][type].getDefault;
reqDefaultInit.setLanguage(this.locale);
console.log(this.locale);
this.getDefaultInitMessageTextMap$ = from(
this.getDefaultValues(type, reqDefaultInit),
);
}
const reqCustomInit = REQUESTMAP[this.serviceType][type].get.setLanguage(this.locale);
this.getCustomInitMessageTextMap$.next(
await this.getCurrentValues(type, reqCustomInit),
);
}
public updateCurrentValues(values: { [key: string]: string; }): void {
const req = REQUESTMAP[this.serviceType][this.currentType].setFcn;
const mappedValues = req(values);
this.updateRequest = mappedValues;
this.updateRequest.setLanguage(this.locale);
}
public saveCurrentMessage(): any {
const handler = (prom: Promise<any>): Promise<any> => {
return prom.then(() => {
this.toast.showInfo('POLICY.MESSAGE_TEXTS.TOAST.UPDATED', true);
}).catch(error => this.toast.showError(error));
};
if (this.serviceType === PolicyComponentServiceType.MGMT) {
switch (this.currentType) {
case MESSAGETYPES.INIT:
return handler((this.service as ManagementService).setCustomInitMessageText(this.updateRequest));
case MESSAGETYPES.VERIFYPHONE:
return handler((this.service as ManagementService).setCustomVerifyPhoneMessageText(this.updateRequest));
case MESSAGETYPES.VERIFYEMAIL:
return handler((this.service as ManagementService).setCustomVerifyEmailMessageText(this.updateRequest));
case MESSAGETYPES.PASSWORDRESET:
return handler((this.service as ManagementService).setCustomPasswordResetMessageText(this.updateRequest));
case MESSAGETYPES.DOMAINCLAIMED:
return handler((this.service as ManagementService).setCustomDomainClaimedMessageCustomText(this.updateRequest));
}
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
switch (this.currentType) {
case MESSAGETYPES.INIT:
return handler((this.service as AdminService).setDefaultInitMessageText(this.updateRequest));
case MESSAGETYPES.VERIFYPHONE:
return handler((this.service as AdminService).setDefaultVerifyPhoneMessageText(this.updateRequest));
case MESSAGETYPES.VERIFYEMAIL:
return handler((this.service as AdminService).setDefaultVerifyEmailMessageText(this.updateRequest));
case MESSAGETYPES.PASSWORDRESET:
return handler((this.service as AdminService).setDefaultPasswordResetMessageText(this.updateRequest));
case MESSAGETYPES.DOMAINCLAIMED:
return handler((this.service as AdminService).setDefaultDomainClaimedMessageText(this.updateRequest));
}
}
}
public resetDefault(): void {
const dialogRef = this.dialog.open(WarnDialogComponent, {
data: {
icon: 'las la-history',
confirmKey: 'ACTIONS.RESTORE',
cancelKey: 'ACTIONS.CANCEL',
titleKey: 'POLICY.LOGIN_TEXTS.RESET_TITLE',
descriptionKey: 'POLICY.LOGIN_TEXTS.RESET_DESCRIPTION',
},
width: '400px',
});
dialogRef.afterClosed().subscribe(resp => {
if (resp) {
if (this.serviceType === PolicyComponentServiceType.MGMT) {
const handler = (prom: Promise<any>): Promise<any> => {
return prom.then(() => {
setTimeout(() => {
this.loadData(this.currentType);
}, 1000);
}).catch(error => {
this.toast.showError(error);
});
};
switch (this.currentType) {
case MESSAGETYPES.INIT:
return handler((this.service as ManagementService).resetCustomInitMessageTextToDefault(this.locale));
case MESSAGETYPES.VERIFYPHONE:
return handler((this.service as ManagementService).resetCustomVerifyPhoneMessageTextToDefault(this.locale));
case MESSAGETYPES.VERIFYEMAIL:
return handler((this.service as ManagementService).resetCustomVerifyEmailMessageTextToDefault(this.locale));
case MESSAGETYPES.PASSWORDRESET:
return handler((this.service as ManagementService).resetCustomPasswordResetMessageTextToDefault(this.locale));
case MESSAGETYPES.DOMAINCLAIMED:
return handler((this.service as ManagementService).resetCustomDomainClaimedMessageTextToDefault(this.locale));
}
}
}
});
}
private stripDetails(prom: Promise<any>): Promise<any> {
return prom.then(res => {
if (res.customText) {
delete res.customText.details;
return Object.assign({}, res.customText as unknown as { [key: string]: string; });
} else {
return {};
}
});
}
public ngOnDestroy(): void {
this.sub.unsubscribe();
}
public setCurrentType(key: MESSAGETYPES): void {
this.currentType = key;
this.loadData(this.currentType);
}
}

View File

@@ -0,0 +1,50 @@
import { TextFieldModule } from '@angular/cdk/text-field';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select';
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 { HasFeaturePipeModule } from 'src/app/pipes/has-feature-pipe/has-feature-pipe.module';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { EditTextModule } from '../../edit-text/edit-text.module';
import { FormFieldModule } from '../../form-field/form-field.module';
import { InfoSectionModule } from '../../info-section/info-section.module';
import { PolicyGridModule } from '../../policy-grid/policy-grid.module';
import { MessageTextsRoutingModule } from './message-texts-routing.module';
import { MessageTextsComponent } from './message-texts.component';
@NgModule({
declarations: [MessageTextsComponent],
imports: [
MessageTextsRoutingModule,
CommonModule,
InfoSectionModule,
ReactiveFormsModule,
FormsModule,
InputModule,
FormFieldModule,
EditTextModule,
MatButtonModule,
HasFeaturePipeModule,
MatIconModule,
HasRoleModule,
HasRolePipeModule,
MatTooltipModule,
TranslateModule,
MatTooltipModule,
MatSelectModule,
DetailLayoutModule,
MatProgressSpinnerModule,
PolicyGridModule,
TextFieldModule,
],
})
export class MessageTextsPolicyModule { }

View File

@@ -24,5 +24,5 @@
}}</button>
</div>
<cnsl-links *ngIf="nextLinks" [links]="nextLinks"></cnsl-links>
<app-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="login"></app-policy-grid>
</app-detail-layout>

View File

@@ -33,6 +33,7 @@
display: flex;
justify-content: flex-end;
width: 100%;
margin-bottom: 50px;
button {
margin-top: 3rem;

View File

@@ -11,15 +11,7 @@ import { ManagementService } from 'src/app/services/mgmt.service';
import { StorageService } from 'src/app/services/storage.service';
import { ToastService } from 'src/app/services/toast.service';
import { CnslLinks } from '../../links/links.component';
import {
IAM_COMPLEXITY_LINK,
IAM_LOGIN_POLICY_LINK,
IAM_PRIVATELABEL_LINK,
ORG_COMPLEXITY_LINK,
ORG_LOGIN_POLICY_LINK,
ORG_PRIVATELABEL_LINK,
} from '../../policy-grid/policy-links';
import { GridPolicy, IAM_POLICY } from '../../policy-grid/policies';
import { PolicyComponentServiceType } from '../policy-component-types.enum';
@Component({
@@ -37,7 +29,8 @@ export class OrgIamPolicyComponent implements OnDestroy {
private org!: Org.AsObject;
public PolicyComponentServiceType: any = PolicyComponentServiceType;
public nextLinks: Array<CnslLinks> = [];
public currentPolicy: GridPolicy = IAM_POLICY;
constructor(
private route: ActivatedRoute,
private toast: ToastService,
@@ -53,17 +46,6 @@ export class OrgIamPolicyComponent implements OnDestroy {
this.serviceType = data.serviceType;
if (this.serviceType === PolicyComponentServiceType.MGMT) {
this.managementService = this.injector.get(ManagementService as Type<ManagementService>);
this.nextLinks = [
ORG_COMPLEXITY_LINK,
ORG_LOGIN_POLICY_LINK,
ORG_PRIVATELABEL_LINK,
];
} else {
this.nextLinks = [
IAM_COMPLEXITY_LINK,
IAM_LOGIN_POLICY_LINK,
IAM_PRIVATELABEL_LINK,
];
}
return this.route.params;
})).subscribe(_ => {

View File

@@ -9,28 +9,28 @@ 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 { InfoSectionModule } from '../../info-section/info-section.module';
import { LinksModule } from '../../links/links.module';
import { InfoSectionModule } from '../../info-section/info-section.module';
import { PolicyGridModule } from '../../policy-grid/policy-grid.module';
import { OrgIamPolicyRoutingModule } from './org-iam-policy-routing.module';
import { OrgIamPolicyComponent } from './org-iam-policy.component';
@NgModule({
declarations: [OrgIamPolicyComponent],
imports: [
OrgIamPolicyRoutingModule,
CommonModule,
FormsModule,
InputModule,
MatButtonModule,
MatSlideToggleModule,
MatIconModule,
HasRoleModule,
MatTooltipModule,
InfoSectionModule,
TranslateModule,
DetailLayoutModule,
LinksModule,
],
declarations: [OrgIamPolicyComponent],
imports: [
OrgIamPolicyRoutingModule,
CommonModule,
FormsModule,
InputModule,
MatButtonModule,
MatSlideToggleModule,
MatIconModule,
HasRoleModule,
MatTooltipModule,
InfoSectionModule,
TranslateModule,
DetailLayoutModule,
PolicyGridModule,
],
})
export class OrgIamPolicyModule { }

View File

@@ -66,5 +66,5 @@
}}</button>
</div>
<cnsl-links *ngIf="nextLinks" [links]="nextLinks"></cnsl-links>
<app-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="security"></app-policy-grid>
</app-detail-layout>

View File

@@ -42,6 +42,7 @@
display: flex;
justify-content: flex-end;
width: 100%;
margin-bottom: 50px;
button {
margin-top: 3rem;

View File

@@ -13,15 +13,7 @@ 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 { CnslLinks } from '../../links/links.component';
import {
IAM_LOGIN_POLICY_LINK,
IAM_POLICY_LINK,
IAM_PRIVATELABEL_LINK,
ORG_IAM_POLICY_LINK,
ORG_LOGIN_POLICY_LINK,
ORG_PRIVATELABEL_LINK,
} from '../../policy-grid/policy-links';
import { COMPLEXITY_POLICY, GridPolicy } from '../../policy-grid/policies';
import { PolicyComponentServiceType } from '../policy-component-types.enum';
@Component({
@@ -39,7 +31,8 @@ export class PasswordComplexityPolicyComponent implements OnDestroy {
public PolicyComponentServiceType: any = PolicyComponentServiceType;
public loading: boolean = false;
public nextLinks: CnslLinks[] = [];
public currentPolicy: GridPolicy = COMPLEXITY_POLICY;
constructor(
private route: ActivatedRoute,
private toast: ToastService,
@@ -51,19 +44,9 @@ export class PasswordComplexityPolicyComponent implements OnDestroy {
switch (this.serviceType) {
case PolicyComponentServiceType.MGMT:
this.service = this.injector.get(ManagementService as Type<ManagementService>);
this.nextLinks = [
ORG_IAM_POLICY_LINK,
ORG_LOGIN_POLICY_LINK,
ORG_PRIVATELABEL_LINK,
];
break;
case PolicyComponentServiceType.ADMIN:
this.service = this.injector.get(AdminService as Type<AdminService>);
this.nextLinks = [
IAM_POLICY_LINK,
IAM_LOGIN_POLICY_LINK,
IAM_PRIVATELABEL_LINK,
];
break;
}

View File

@@ -10,27 +10,27 @@ 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 { LinksModule } from '../../links/links.module';
import { PolicyGridModule } from '../../policy-grid/policy-grid.module';
import { PasswordComplexityPolicyRoutingModule } from './password-complexity-policy-routing.module';
import { PasswordComplexityPolicyComponent } from './password-complexity-policy.component';
@NgModule({
declarations: [PasswordComplexityPolicyComponent],
imports: [
PasswordComplexityPolicyRoutingModule,
CommonModule,
FormsModule,
InputModule,
MatButtonModule,
MatSlideToggleModule,
MatIconModule,
HasRoleModule,
MatTooltipModule,
TranslateModule,
DetailLayoutModule,
MatProgressSpinnerModule,
LinksModule,
],
declarations: [PasswordComplexityPolicyComponent],
imports: [
PasswordComplexityPolicyRoutingModule,
CommonModule,
FormsModule,
InputModule,
MatButtonModule,
MatSlideToggleModule,
MatIconModule,
HasRoleModule,
MatTooltipModule,
TranslateModule,
DetailLayoutModule,
MatProgressSpinnerModule,
PolicyGridModule,
],
})
export class PasswordComplexityPolicyModule { }

View File

@@ -5,7 +5,11 @@ export enum PolicyComponentType {
IAM = 'iam',
LOGIN = 'login',
PRIVATELABEL = 'privatelabel',
MESSAGETEXTS = 'messagetexts',
LOGINTEXTS = 'logintexts',
PRIVACYPOLICY = 'privacypolicy',
}
export enum PolicyComponentServiceType {
MGMT = 'mgmt',
ADMIN = 'admin',

View File

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

View File

@@ -0,0 +1,37 @@
<app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
[title]="'POLICY.PRIVACY_POLICY.TITLE' | translate"
[description]="'POLICY.PRIVACY_POLICY.DESCRIPTION' | translate">
<cnsl-info-section class="warn"
*ngIf="serviceType == PolicyComponentServiceType.MGMT && (['privacy_policy'] | hasFeature | async) == false"
type="WARN">
{{'FEATURES.NOTAVAILABLE' | translate: ({value:
'privacy_policy'})}}
</cnsl-info-section>
<div class="divider"></div>
<div class="content" >
<form *ngIf="form" [formGroup]="form">
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'POLICY.PRIVACY_POLICY.TOSLINK' | translate }}</cnsl-label>
<input cnslInput name="tosLink" formControlName="tosLink" />
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'POLICY.PRIVACY_POLICY.POLICYLINK' | translate }}</cnsl-label>
<input cnslInput name="privacyLink" formControlName="privacyLink" />
</cnsl-form-field>
</form>
</div>
<div class="actions">
<button *ngIf="!privacyPolicy?.isDefault" class="reset-button" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['privacy_policy'] | hasFeature | async) == false" (click)="resetDefault()" color="warn" type="submit"
mat-stroked-button><i class="las la-history"></i> {{ 'ACTIONS.RESETDEFAULT' | translate }}</button>
<button class="save-button" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['privacy_policy'] | hasFeature | async) == false" (click)="saveCurrentMessage()" color="primary" type="submit"
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
</div>
<app-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="text"></app-policy-grid>
</app-detail-layout>

View File

@@ -0,0 +1,81 @@
.spinner-wr {
margin: .5rem 0;
}
.top-actions {
display: flex;
margin: 0 -.5rem;
flex-wrap: wrap;
.keys {
flex: 1;
margin: 0 .5rem;
min-width: 150px;
}
.language {
margin: 0 .5rem;
min-width: 150px;
.lighter {
font-size: 12px;
color: var(--grey);
padding: 0 .5rem;
}
}
}
.centerline {
display: flex;
align-items: center;
}
.content {
padding-top: 1rem;
}
.chips {
display: flex;
margin: 0 -.25rem;
.chip {
border-radius: 50vw;
padding: 2px .5rem;
font-size: 12px;
background: #cbf4c9;
color: #0e6245;
margin: .25rem;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
}
}
.divider {
width: 100%;
height: 1px;
background-color: rgba(#81868a, .5);
margin: 1.5rem 0 0 0;
}
.actions {
display: flex;
justify-content: flex-end;
margin: 0 -.25rem;
.save-button,
.reset-button {
display: block;
margin: 0 .25rem 3rem .25rem;
}
.reset-button {
display: flex;
align-items: center;
i {
margin-bottom: 3px;
}
}
}

View File

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

View File

@@ -0,0 +1,147 @@
import { Component, Injector, OnDestroy, Type } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import {
GetPrivacyPolicyResponse as AdminGetPrivacyPolicyResponse,
UpdatePrivacyPolicyRequest,
} from 'src/app/proto/generated/zitadel/admin_pb';
import {
AddCustomPrivacyPolicyRequest,
GetPrivacyPolicyResponse,
UpdateCustomPrivacyPolicyRequest,
} from 'src/app/proto/generated/zitadel/management_pb';
import { PrivacyPolicy } 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 { CnslLinks } from '../../links/links.component';
import { GridPolicy, PRIVACY_POLICY } from '../../policy-grid/policies';
import { WarnDialogComponent } from '../../warn-dialog/warn-dialog.component';
import { PolicyComponentServiceType } from '../policy-component-types.enum';
@Component({
selector: 'app-privacy-policy',
templateUrl: './privacy-policy.component.html',
styleUrls: ['./privacy-policy.component.scss'],
})
export class PrivacyPolicyComponent implements OnDestroy {
public service!: ManagementService | AdminService;
public PolicyComponentServiceType: any = PolicyComponentServiceType;
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
public nextLinks: CnslLinks[] = [];
private sub: Subscription = new Subscription();
public privacyPolicy!: PrivacyPolicy.AsObject;
public form!: FormGroup;
public currentPolicy: GridPolicy = PRIVACY_POLICY;
constructor(
private route: ActivatedRoute,
private injector: Injector,
private dialog: MatDialog,
private toast: ToastService,
private fb: FormBuilder,
) {
this.form = this.fb.group({
tosLink: ['', []],
privacyLink: ['', []],
});
this.route.data.pipe(switchMap(data => {
this.serviceType = data.serviceType;
switch (this.serviceType) {
case PolicyComponentServiceType.MGMT:
this.service = this.injector.get(ManagementService as Type<ManagementService>);
this.loadData();
break;
case PolicyComponentServiceType.ADMIN:
this.service = this.injector.get(AdminService as Type<AdminService>);
this.loadData();
break;
}
return this.route.params;
})).subscribe();
}
public async loadData(): Promise<any> {
const getData = ():
Promise<AdminGetPrivacyPolicyResponse.AsObject | GetPrivacyPolicyResponse.AsObject> => {
return (this.service as AdminService).getPrivacyPolicy();
};
getData().then(resp => {
if (resp.policy) {
this.privacyPolicy = resp.policy;
this.form.patchValue(this.privacyPolicy);
}
});
}
public saveCurrentMessage(): void {
console.log(this.form.get('privacyLink')?.value, this.form.get('tosLink')?.value);
if (this.serviceType === PolicyComponentServiceType.MGMT) {
if ((this.privacyPolicy as PrivacyPolicy.AsObject).isDefault) {
const req = new AddCustomPrivacyPolicyRequest();
req.setPrivacyLink(this.form.get('privacyLink')?.value);
req.setTosLink(this.form.get('tosLink')?.value);
(this.service as ManagementService).addCustomPrivacyPolicy(req).then(() => {
this.toast.showInfo('POLICY.PRIVACY_POLICY.SAVED', true);
}).catch(error => this.toast.showError(error));
} else {
const req = new UpdateCustomPrivacyPolicyRequest();
req.setPrivacyLink(this.form.get('privacyLink')?.value);
req.setTosLink(this.form.get('tosLink')?.value);
(this.service as ManagementService).updateCustomPrivacyPolicy(req).then(() => {
this.toast.showInfo('POLICY.PRIVACY_POLICY.SAVED', true);
}).catch(error => this.toast.showError(error));
}
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
const req = new UpdatePrivacyPolicyRequest();
req.setPrivacyLink(this.form.get('privacyLink')?.value);
req.setTosLink(this.form.get('tosLink')?.value);
(this.service as AdminService).updatePrivacyPolicy(req).then(() => {
this.toast.showInfo('POLICY.PRIVACY_POLICY.SAVED', true);
}).catch(error => this.toast.showError(error));
}
}
public resetDefault(): void {
const dialogRef = this.dialog.open(WarnDialogComponent, {
data: {
icon: 'las la-history',
confirmKey: 'ACTIONS.RESTORE',
cancelKey: 'ACTIONS.CANCEL',
titleKey: 'POLICY.PRIVACY_POLICY.RESET_TITLE',
descriptionKey: 'POLICY.PRIVACY_POLICY.RESET_DESCRIPTION',
},
width: '400px',
});
dialogRef.afterClosed().subscribe(resp => {
if (resp) {
if (this.serviceType === PolicyComponentServiceType.MGMT) {
(this.service as ManagementService).resetPrivacyPolicyToDefault().then(() => {
setTimeout(() => {
this.loadData();
}, 1000);
}).catch(error => {
this.toast.showError(error);
});
}
}
});
}
public ngOnDestroy(): void {
this.sub.unsubscribe();
}
}

View File

@@ -0,0 +1,52 @@
import { TextFieldModule } from '@angular/cdk/text-field';
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 { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from '../../../directives/has-role/has-role.module';
import { DetailLayoutModule } from '../../../modules/detail-layout/detail-layout.module';
import { InputModule } from '../../../modules/input/input.module';
import { HasFeaturePipeModule } from '../../../pipes/has-feature-pipe/has-feature-pipe.module';
import { HasRolePipeModule } from '../../../pipes/has-role-pipe/has-role-pipe.module';
import { FormFieldModule } from '../../form-field/form-field.module';
import { InfoSectionModule } from '../../info-section/info-section.module';
import { PolicyGridModule } from '../../policy-grid/policy-grid.module';
import { WarnDialogModule } from '../../warn-dialog/warn-dialog.module';
import { PrivacyPolicyRoutingModule } from './privacy-policy-routing.module';
import { PrivacyPolicyComponent } from './privacy-policy.component';
@NgModule({
declarations: [PrivacyPolicyComponent],
imports: [
PrivacyPolicyRoutingModule,
MatSelectModule,
CommonModule,
InfoSectionModule,
ReactiveFormsModule,
FormsModule,
InputModule,
FormFieldModule,
MatButtonModule,
HasFeaturePipeModule,
MatIconModule,
HasRoleModule,
HasRolePipeModule,
MatTooltipModule,
TranslateModule,
MatTooltipModule,
DetailLayoutModule,
MatProgressSpinnerModule,
TextFieldModule,
MatDialogModule,
WarnDialogModule,
PolicyGridModule,
],
})
export class PrivacyPolicyModule { }

View File

@@ -279,5 +279,5 @@
</div>
</div>
<cnsl-links *ngIf="nextLinks" [links]="nextLinks"></cnsl-links>
<app-policy-grid class="grid" [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="text"></app-policy-grid>
</div>

View File

@@ -453,3 +453,8 @@
}
}
}
.grid {
display: block;
margin: 50px 0;
}

View File

@@ -24,15 +24,7 @@ import { ManagementService } from 'src/app/services/mgmt.service';
import { StorageService } from 'src/app/services/storage.service';
import { ToastService } from 'src/app/services/toast.service';
import { CnslLinks } from '../../links/links.component';
import {
IAM_COMPLEXITY_LINK,
IAM_LOGIN_POLICY_LINK,
IAM_POLICY_LINK,
ORG_COMPLEXITY_LINK,
ORG_IAM_POLICY_LINK,
ORG_LOGIN_POLICY_LINK,
} from '../../policy-grid/policy-links';
import { GridPolicy, PRIVATELABEL_POLICY } from '../../policy-grid/policies';
import { PolicyComponentServiceType } from '../policy-component-types.enum';
export enum Theme {
@@ -85,11 +77,6 @@ export class PrivateLabelingPolicyComponent implements OnDestroy {
public PolicyComponentServiceType: any = PolicyComponentServiceType;
public loading: boolean = false;
public nextLinks: CnslLinks[] = [
IAM_COMPLEXITY_LINK,
IAM_POLICY_LINK,
IAM_LOGIN_POLICY_LINK,
];
public Theme: any = Theme;
public Preview: any = Preview;
@@ -99,7 +86,7 @@ export class PrivateLabelingPolicyComponent implements OnDestroy {
public refreshPreview: EventEmitter<void> = new EventEmitter();
public loadingImages: boolean = false;
private org!: Org.AsObject;
public currentPolicy: GridPolicy = PRIVATELABEL_POLICY;
constructor(
private authService: GrpcAuthService,
private route: ActivatedRoute,
@@ -121,19 +108,9 @@ export class PrivateLabelingPolicyComponent implements OnDestroy {
switch (this.serviceType) {
case PolicyComponentServiceType.MGMT:
this.service = this.injector.get(ManagementService as Type<ManagementService>);
this.nextLinks = [
ORG_IAM_POLICY_LINK,
ORG_LOGIN_POLICY_LINK,
ORG_COMPLEXITY_LINK,
];
break;
case PolicyComponentServiceType.ADMIN:
this.service = this.injector.get(AdminService as Type<AdminService>);
this.nextLinks = [
IAM_POLICY_LINK,
IAM_LOGIN_POLICY_LINK,
IAM_COMPLEXITY_LINK,
];
break;
}

View File

@@ -17,7 +17,7 @@ import { DropzoneModule } from '../../../directives/dropzone/dropzone.module';
import { DetailLayoutModule } from '../../detail-layout/detail-layout.module';
import { InfoSectionModule } from '../../info-section/info-section.module';
import { InputModule } from '../../input/input.module';
import { LinksModule } from '../../links/links.module';
import { PolicyGridModule } from '../../policy-grid/policy-grid.module';
import { ColorComponent } from './color/color.component';
import { PreviewComponent } from './preview/preview.component';
import { PrivateLabelingPolicyRoutingModule } from './private-labeling-policy-routing.module';
@@ -45,7 +45,7 @@ import { PrivateLabelingPolicyComponent } from './private-labeling-policy.compon
DetailLayoutModule,
DropzoneModule,
MatProgressSpinnerModule,
LinksModule,
PolicyGridModule,
MatExpansionModule,
InfoSectionModule,
HasFeaturePipeModule,

View File

@@ -0,0 +1,108 @@
import { PolicyComponentType } from '../policies/policy-component-types.enum';
export interface GridPolicy {
i18nTitle: string;
i18nDesc: string;
iamRouterLink: any;
orgRouterLink: any;
iamWithRole: string[];
orgWithRole: string[];
tags: string[];
icon?: string;
svgIcon?: string;
color: string;
}
export const COMPLEXITY_POLICY: GridPolicy = {
i18nTitle: 'POLICY.PWD_COMPLEXITY.TITLE',
i18nDesc: 'POLICY.PWD_COMPLEXITY.DESCRIPTION',
iamRouterLink: ['/iam', 'policy', PolicyComponentType.COMPLEXITY],
orgRouterLink: ['/org', 'policy', PolicyComponentType.COMPLEXITY],
iamWithRole: ['iam.policy.read'],
orgWithRole: ['policy.read'],
tags: ['login', 'security'],
svgIcon: 'mdi_textbox_password',
color: 'yellow',
};
export const IAM_POLICY = {
i18nTitle: 'POLICY.IAM_POLICY.TITLE',
i18nDesc: 'POLICY.IAM_POLICY.DESCRIPTION',
iamRouterLink: ['/iam', 'policy', PolicyComponentType.IAM],
orgRouterLink: ['/org', 'policy', PolicyComponentType.IAM],
iamWithRole: ['iam.policy.read'],
orgWithRole: ['iam.policy.read'],
tags: ['login'],
icon: 'las la-gem',
color: 'purple',
};
export const LOGIN_POLICY = {
i18nTitle: 'POLICY.LOGIN_POLICY.TITLE',
i18nDesc: 'POLICY.LOGIN_POLICY.DESCRIPTION',
iamRouterLink: ['/iam', 'policy', PolicyComponentType.LOGIN],
orgRouterLink: ['/org', 'policy', PolicyComponentType.LOGIN],
iamWithRole: ['iam.policy.read'],
orgWithRole: ['policy.read'],
tags: ['login', 'security'],
icon: 'las la-sign-in-alt',
color: 'green',
};
export const PRIVATELABEL_POLICY = {
i18nTitle: 'POLICY.PRIVATELABELING.TITLE',
i18nDesc: 'POLICY.PRIVATELABELING.DESCRIPTION',
iamRouterLink: ['/iam', 'policy', PolicyComponentType.PRIVATELABEL],
orgRouterLink: ['/org', 'policy', PolicyComponentType.PRIVATELABEL],
iamWithRole: ['iam.policy.read'],
orgWithRole: ['policy.read'],
tags: ['login', 'appearance'],
icon: 'las la-sign-in-alt',
color: 'blue',
};
export const PRIVACY_POLICY = {
i18nTitle: 'POLICY.PRIVACY_POLICY.TITLE',
i18nDesc: 'POLICY.PRIVACY_POLICY.DESCRIPTION',
iamRouterLink: ['/iam', 'policy', PolicyComponentType.PRIVACYPOLICY],
orgRouterLink: ['/org', 'policy', PolicyComponentType.PRIVACYPOLICY],
iamWithRole: ['iam.policy.read'],
orgWithRole: ['policy.read'],
tags: ['documents', 'text'],
icon: 'las la-file-contract',
color: 'black',
};
export const MESSAGE_TEXTS_POLICY = {
i18nTitle: 'POLICY.MESSAGE_TEXTS.TITLE',
i18nDesc: 'POLICY.MESSAGE_TEXTS.DESCRIPTION',
iamRouterLink: ['/iam', 'policy', PolicyComponentType.MESSAGETEXTS],
orgRouterLink: ['/org', 'policy', PolicyComponentType.MESSAGETEXTS],
iamWithRole: ['iam.policy.read'],
orgWithRole: ['policy.read'],
tags: ['appearance', 'text'],
icon: 'las la-paragraph',
color: 'red',
};
export const LOGIN_TEXTS_POLICY = {
i18nTitle: 'POLICY.LOGIN_TEXTS.TITLE',
i18nDesc: 'POLICY.LOGIN_TEXTS.DESCRIPTION_SHORT',
iamRouterLink: ['/iam', 'policy', PolicyComponentType.LOGINTEXTS],
orgRouterLink: ['/org', 'policy', PolicyComponentType.LOGINTEXTS],
iamWithRole: ['iam.policy.read'],
orgWithRole: ['policy.read'],
tags: ['appearance', 'text'],
icon: 'las la-paragraph',
color: 'red',
};
export const POLICIES: GridPolicy[] = [
COMPLEXITY_POLICY,
IAM_POLICY,
LOGIN_POLICY,
PRIVATELABEL_POLICY,
PRIVACY_POLICY,
MESSAGE_TEXTS_POLICY,
LOGIN_TEXTS_POLICY,
];

View File

@@ -2,128 +2,36 @@
<p class="top-desc">{{'POLICY.DESCRIPTION' | translate}}</p>
<div class="tags" *ngIf="tags">
<span class="tag" [ngClass]="{'active': tag === tagForFilter}" (click)="tagForFilter !== tag ? tagForFilter = tag : tagForFilter = ''" *ngFor="let tag of tags"><i class="las la-hashtag"></i>{{tag}}</span>
</div>
<div class="row-lyt">
<ng-container *ngFor="let policy of filteredPolicies">
<ng-template appHasRole
[appHasRole]="type == PolicyGridType.IAM ? ['iam.policy.read'] : type == PolicyGridType.ORG ? ['policy.read'] : []">
[appHasRole]="type == PolicyComponentServiceType.ADMIN ? policy.iamWithRole : type == PolicyComponentServiceType.MGMT ? policy.orgWithRole : []">
<div class="p-item card">
<div class="avatar">
<mat-icon class="icon" svgIcon="mdi_textbox_password"></mat-icon>
<div class="avatar {{policy.color}}">
<mat-icon *ngIf="policy.svgIcon" class="icon" [svgIcon]="policy.svgIcon"></mat-icon>
<i *ngIf="policy.icon" class="icon {{policy.icon}}"></i>
</div>
<div class="title">
<span>{{'POLICY.PWD_COMPLEXITY.TITLE' | translate}}</span>
<button mat-icon-button disabled>
<i class="icon las la-check-circle"></i>
</button>
<span>{{policy.i18nTitle | translate}}</span>
</div>
<p class="desc">
{{'POLICY.PWD_COMPLEXITY.DESCRIPTION' | translate}}</p>
<div class="icons">
<mat-icon matTooltip="{{'POLICY.DATA.HASUPPERCASE' | translate}}" *ngIf="complexityPolicy?.hasUppercase"
class="icon" svgIcon="mdi_format-letter-case-upper">
</mat-icon>
<mat-icon matTooltip="{{'POLICY.DATA.HASLOWERCASE' | translate}}" *ngIf="complexityPolicy?.hasLowercase"
class="icon" svgIcon="mdi_format-letter-case-lower">
</mat-icon>
<mat-icon matTooltip="{{'POLICY.DATA.HASNUMBER' | translate}}" *ngIf="complexityPolicy?.hasNumber"
class="icon" svgIcon="mdi_numeric"></mat-icon>
<mat-icon matTooltip="{{'POLICY.DATA.HASSYMBOL' | translate}}" *ngIf="complexityPolicy?.hasSymbol"
class="icon" svgIcon="mdi_symbol"></mat-icon>
<!-- <mat-icon *ngIf="complexityPolicy?." class="icon" svgIcon="mdi_counter"></mat-icon> -->
</div>
{{policy.i18nDesc | translate}}</p>
<span class="fill-space"></span>
<div class="tags" *ngIf="policy.tags">
<span class="tag" *ngFor="let tag of policy.tags" (click)="tagForFilter !== tag ? tagForFilter = tag : tagForFilter = ''"><i class="las la-hashtag"></i>{{tag}}</span>
</div>
<div class="btn-wrapper">
<button
[routerLink]="[type == PolicyGridType.IAM ? '/iam' : type == PolicyGridType.ORG ? '/org' : '', 'policy', PolicyComponentType.COMPLEXITY ]"
[routerLink]="type == PolicyComponentServiceType.ADMIN ? policy.iamRouterLink : type == PolicyComponentServiceType.MGMT ? policy.orgRouterLink : null"
mat-stroked-button>{{'POLICY.BTN_EDIT' | translate}}</button>
</div>
</div>
</ng-template>
<ng-template appHasRole [appHasRole]="['iam.policy.read']">
<div class="p-item card">
<div class="avatar">
<i class="icon las la-gem"></i>
</div>
<div class="title">
<span>{{'POLICY.IAM_POLICY.TITLE' | translate}}</span>
<button mat-icon-button disabled>
<i class="icon las la-check-circle"></i>
</button>
</div>
<p class="desc">
{{'POLICY.IAM_POLICY.DESCRIPTION' | translate}}</p>
<span class="fill-space"></span>
<div class="btn-wrapper">
<ng-template appHasRole [appHasRole]="['iam.policy.write']">
<button
[routerLink]="[type == PolicyGridType.IAM ? '/iam' : type == PolicyGridType.ORG ? '/org' : '','policy', PolicyComponentType.IAM ]"
mat-stroked-button>{{'POLICY.BTN_EDIT' | translate}}</button>
</ng-template>
</div>
</div>
</ng-template>
<ng-template appHasRole [appHasRole]="type == PolicyGridType.IAM ? ['iam.policy.read'] : type == PolicyGridType.ORG ? ['policy.read'] : []">
<div class="p-item card">
<div class="avatar">
<i class="icon las la-gem"></i>
</div>
<div class="title">
<span>{{'POLICY.PRIVATELABELING_POLICY.TITLE' | translate}}</span>
<button mat-icon-button disabled>
<i class="icon las la-check-circle"></i>
</button>
</div>
<p class="desc">
{{'POLICY.PRIVATELABELING_POLICY.DESCRIPTION' | translate}}</p>
<!-- <cnsl-info-section class="warn"
*ngIf="type == PolicyGridType.ORG && (['password_complexity_policy'] | hasFeature | async) == false"
type="WARN">
{{'FEATURES.NOTAVAILABLE' | translate: ({value:
'password_complexity_policy'})}}
</cnsl-info-section> -->
<span class="fill-space"></span>
<div class="btn-wrapper">
<ng-template appHasRole [appHasRole]="type == PolicyGridType.IAM ? ['iam.policy.write'] : type == PolicyGridType.ORG ? ['policy.write'] : []">
<button
[routerLink]="[type == PolicyGridType.IAM ? '/iam' : type == PolicyGridType.ORG ? '/org' : '','policy', PolicyComponentType.PRIVATELABEL ]"
mat-stroked-button>{{'POLICY.BTN_EDIT' | translate}}</button>
</ng-template>
</div>
</div>
</ng-template>
<ng-template appHasRole
[appHasRole]="type == PolicyGridType.IAM ? ['iam.policy.read'] : type == PolicyGridType.ORG ? ['policy.read'] : []">
<div class="p-item card">
<div class="avatar">
<i class="icon las la-sign-in-alt"></i>
</div>
<div class="title">
<span>{{'POLICY.LOGIN_POLICY.TITLE' | translate}}</span>
<button mat-icon-button disabled>
<i class="icon las la-check-circle"></i>
</button>
</div>
<p class="desc">
{{'POLICY.LOGIN_POLICY.DESCRIPTION' | translate}}</p>
<span class="fill-space"></span>
<div class="btn-wrapper">
<ng-template appHasRole [appHasRole]="['policy.write']">
<button
[routerLink]="[type == PolicyGridType.IAM ? '/iam' : type == PolicyGridType.ORG ? '/org' : '','policy', PolicyComponentType.LOGIN ]"
mat-stroked-button>{{'POLICY.BTN_EDIT' | translate}}</button>
</ng-template>
</div>
</div>
</ng-template>
</ng-container>
</div>

View File

@@ -10,14 +10,41 @@ h2 {
font-size: 14px;
}
.row-lyt {
.tags {
display: flex;
flex-wrap: wrap;
margin: 0 -.5rem;
align-items: center;
margin: 0 -.5rem 1rem -.5rem;
.tag {
display: flex;
align-items: center;
margin: 0 .5rem;
cursor: pointer;
&:hover,
&.active {
color: var(--color-main);
}
}
}
.row-lyt {
margin: 0;
display: grid;
row-gap: 1rem;
column-gap: 1rem;
grid-template-columns: 1fr 1fr 1fr;
@media only screen and (max-width: 1300px) {
grid-template-columns: 1fr 1fr;
}
@media only screen and (max-width: 450px) {
grid-template-columns: 1fr;
}
.p-item {
flex-basis: 290px;
margin: .5rem;
display: flex;
flex-direction: column;
min-height: 250px;
@@ -33,11 +60,35 @@ h2 {
height: 60px;
width: 60px;
border-radius: 50%;
background: linear-gradient(40deg, rgb(129, 85, 185) 30%, #7b8ada);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: .5rem;
background: linear-gradient(40deg, rgb(129, 85, 185) 30%, #7b8ada);
&.purple {
background: linear-gradient(40deg, #7c3aed 30%, #6d28d9);
}
&.red {
background: linear-gradient(40deg, #dc2626 30%, #db2777);
}
&.green {
background: linear-gradient(40deg, #059669 30%, #047857);
}
&.blue {
background: linear-gradient(40deg, #3b82f6 30%, #4f46e5);
}
&.yellow {
background: linear-gradient(40deg, #f59e0b 30%, #b45309);
}
&.black {
background: linear-gradient(40deg, #1f2937, #111827);
}
.icon,
i {
@@ -83,6 +134,17 @@ h2 {
flex: 1;
}
.tags {
.tag {
color: var(--grey);
font-size: 12px;
i {
font-size: 18px;
}
}
}
.btn-wrapper {
display: flex;
}

View File

@@ -1,41 +1,29 @@
import { Component, Input, OnInit } from '@angular/core';
import { PolicyComponentType } from 'src/app/modules/policies/policy-component-types.enum';
import { PasswordComplexityPolicy } 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 { Component, Input } from '@angular/core';
import { PolicyComponentServiceType, PolicyComponentType } from 'src/app/modules/policies/policy-component-types.enum';
export enum PolicyGridType {
ORG,
IAM,
}
import { GridPolicy, POLICIES } from './policies';
@Component({
selector: 'app-policy-grid',
templateUrl: './policy-grid.component.html',
styleUrls: ['./policy-grid.component.scss'],
selector: 'app-policy-grid',
templateUrl: './policy-grid.component.html',
styleUrls: ['./policy-grid.component.scss'],
})
export class PolicyGridComponent implements OnInit {
@Input() public type!: PolicyGridType;
public PolicyComponentType: any = PolicyComponentType;
public PolicyGridType: any = PolicyGridType;
export class PolicyGridComponent {
@Input() public type!: PolicyComponentServiceType;
@Input() public tag: string = '';
public PolicyComponentType: any = PolicyComponentType;
public PolicyComponentServiceType: any = PolicyComponentServiceType;
public POLICIES: GridPolicy[] = POLICIES;
public tags: Set<string> = new Set(POLICIES.map(p => p.tags).flat());
public complexityPolicy!: PasswordComplexityPolicy.AsObject;
@Input() public tagForFilter: string = '';
@Input() public currentPolicy!: GridPolicy;
constructor(private mgmtService: ManagementService, private adminService: AdminService) { }
public ngOnInit(): void {
if (this.type === PolicyGridType.ORG) {
this.mgmtService.getPasswordComplexityPolicy().then((resp) => {
if (resp.policy) {
this.complexityPolicy = resp.policy;
}
});
} else if (this.type === PolicyGridType.IAM) {
this.adminService.getPasswordComplexityPolicy().then((resp) => {
if (resp.policy) {
this.complexityPolicy = resp.policy;
}
});
}
public get filteredPolicies(): GridPolicy[] {
if (this.tagForFilter) {
return POLICIES.filter(p => p !== this.currentPolicy && p.tags.includes(this.tagForFilter));
} else {
return POLICIES.filter(p => p !== this.currentPolicy);
}
}
}

View File

@@ -1,58 +0,0 @@
import { PolicyComponentType } from '../policies/policy-component-types.enum';
export const IAM_COMPLEXITY_LINK = {
i18nTitle: 'POLICY.PWD_COMPLEXITY.TITLE',
i18nDesc: 'POLICY.PWD_COMPLEXITY.DESCRIPTION',
routerLink: ['/iam', 'policy', PolicyComponentType.COMPLEXITY],
withRole: ['iam.policy.read'],
};
export const IAM_POLICY_LINK = {
i18nTitle: 'POLICY.IAM_POLICY.TITLE',
i18nDesc: 'POLICY.IAM_POLICY.DESCRIPTION',
routerLink: ['/iam', 'policy', PolicyComponentType.IAM],
withRole: ['iam.policy.read'],
};
export const IAM_LOGIN_POLICY_LINK = {
i18nTitle: 'POLICY.LOGIN_POLICY.TITLE',
i18nDesc: 'POLICY.LOGIN_POLICY.DESCRIPTION',
routerLink: ['/iam', 'policy', PolicyComponentType.LOGIN],
withRole: ['iam.policy.read'],
};
export const IAM_PRIVATELABEL_LINK = {
i18nTitle: 'POLICY.PRIVATELABELING.TITLE',
i18nDesc: 'POLICY.PRIVATELABELING.DESCRIPTION',
routerLink: ['/iam', 'policy', PolicyComponentType.PRIVATELABEL],
withRole: ['iam.policy.read'],
};
export const ORG_COMPLEXITY_LINK = {
i18nTitle: 'POLICY.PWD_COMPLEXITY.TITLE',
i18nDesc: 'POLICY.PWD_COMPLEXITY.DESCRIPTION',
routerLink: ['/org', 'policy', PolicyComponentType.COMPLEXITY],
withRole: ['policy.read'],
};
export const ORG_IAM_POLICY_LINK = {
i18nTitle: 'POLICY.IAM_POLICY.TITLE',
i18nDesc: 'POLICY.IAM_POLICY.DESCRIPTION',
routerLink: ['/org', 'policy', PolicyComponentType.IAM],
withRole: ['iam.policy.read'],
};
export const ORG_LOGIN_POLICY_LINK = {
i18nTitle: 'POLICY.LOGIN_POLICY.TITLE',
i18nDesc: 'POLICY.LOGIN_POLICY.DESCRIPTION',
routerLink: ['/org', 'policy', PolicyComponentType.LOGIN],
withRole: ['policy.read'],
};
export const ORG_PRIVATELABEL_LINK = {
i18nTitle: 'POLICY.PRIVATELABELING.TITLE',
i18nDesc: 'POLICY.PRIVATELABELING.DESCRIPTION',
routerLink: ['/org', 'policy', PolicyComponentType.PRIVATELABEL],
withRole: ['policy.read'],
};

View File

@@ -1,13 +1,16 @@
<span class="title" mat-dialog-title>{{data.titleKey | translate: data.titleParam}}</span>
<div mat-dialog-content>
<p class="desc"> {{data.descriptionKey | translate: data.descriptionParam}}</p>
<div class="icon-wrapper" *ngIf="data.icon">
<i class="icon {{data.icon}}"></i>
</div>
<p class="desc"> {{data.descriptionKey | translate: data.descriptionParam}}</p>
</div>
<div mat-dialog-actions class="action">
<button *ngIf="data.cancelKey" mat-button (click)="closeDialog()">
{{data.cancelKey | translate}}
</button>
<span class="fill-space"></span>
<button color="warn" mat-raised-button class="ok-button" (click)="closeDialogWithSuccess()">
{{data.confirmKey | translate}}
</button>
<button *ngIf="data.cancelKey" mat-button (click)="closeDialog()">
{{data.cancelKey | translate}}
</button>
<span class="fill-space"></span>
<button color="warn" mat-raised-button class="ok-button" (click)="closeDialogWithSuccess()">
{{data.confirmKey | translate}}
</button>
</div>

View File

@@ -3,6 +3,18 @@
margin-top: 0;
}
.icon-wrapper {
display: flex;
justify-content: center;
padding: 1rem;
margin: 1rem;
align-items: center;
.icon {
font-size: 3rem;
}
}
.desc {
color: var(--grey);
font-size: .9rem;

View File

@@ -1,24 +1,7 @@
<div class="wrapper max-width-container">
<div class="header" *ngIf="(authService.user | async) || {} as user">
<app-avatar [routerLink]="['/users/me']"
*ngIf="user && (user.human?.profile?.displayName || (user.human?.profile?.firstName && user.human?.profile?.lastName))"
class="avatar"
[avatarUrl]="user.human?.profile?.avatarUrl || ''"
[forColor]="user?.preferredLoginName"
[name]="user.human?.profile?.displayName ? user.human?.profile?.displayName : (user.human?.profile?.firstName + ' '+ user.human?.profile?.lastName)"
[size]="100">
</app-avatar>
<h3
*ngIf="user && (user.human?.profile?.displayName || user.human?.profile?.firstName || user.human?.profile?.lastName); else loader">
{{'HOME.WELCOME' | translate}},
{{user?.human?.profile?.displayName ? user.human?.profile?.displayName : (user.human?.profile?.firstName + '
'+ user.human?.profile?.lastName)}}</h3>
<p>{{user?.human?.profile?.userName}}</p>
<div class="header">
<h1 class="title">{{'HOME.WELCOME' | translate}}</h1>
<p class="wlc_stnce">{{'HOME.WELCOMESENTENCE' | translate}}</p>
<ng-template #loader>
<mat-spinner diameter="50"></mat-spinner>
</ng-template>
</div>
<div class="container">

View File

@@ -1,4 +1,3 @@
.wrapper {
padding-bottom: 100px;
position: relative;
@@ -6,24 +5,17 @@
.header {
display: flex;
flex-direction: column;
margin-bottom: 4rem;
align-items: center;
margin-bottom: 2rem;
h3 {
.title {
font-size: 24px;
margin-bottom: 1rem;
text-align: center;
}
.wlc_stnce {
color: var(--grey);
font-size: 16px;
}
.avatar {
outline: none;
cursor: pointer;
}
}
.container {
@@ -74,7 +66,7 @@
}
.onboard {
background: linear-gradient(40deg, rgb(80, 66, 121), rgb(177, 59, 122), rgb(225, 53, 81), rgb(230, 107, 86));
background: linear-gradient(40deg, rgb(177, 59, 122), rgb(225, 53, 81), rgb(230, 107, 86));
p {
color: #fad6e3;
@@ -82,7 +74,7 @@
}
.quickstart {
background: linear-gradient(30deg, #2283a6, #6c8f59);
background: linear-gradient(40deg, #047857, #059669);
p {
color: #d6f3fa;

View File

@@ -116,6 +116,30 @@ const routes: Routes = [
loadChildren: () => import('src/app/modules/policies/login-policy/login-policy.module')
.then(m => m.LoginPolicyModule),
},
{
path: PolicyComponentType.MESSAGETEXTS,
data: {
serviceType: PolicyComponentServiceType.ADMIN,
},
loadChildren: () => import('src/app/modules/policies/message-texts/message-texts.module')
.then(m => m.MessageTextsPolicyModule),
},
{
path: PolicyComponentType.LOGINTEXTS,
data: {
serviceType: PolicyComponentServiceType.ADMIN,
},
loadChildren: () => import('src/app/modules/policies/login-texts/login-texts.module')
.then(m => m.LoginTextsPolicyModule),
},
{
path: PolicyComponentType.PRIVACYPOLICY,
data: {
serviceType: PolicyComponentServiceType.ADMIN,
},
loadChildren: () => import('src/app/modules/policies/privacy-policy/privacy-policy.module')
.then(m => m.PrivacyPolicyModule),
},
],
},
];

View File

@@ -7,7 +7,7 @@
<p class="top-desc">{{'FEATURES.DESCRIPTION' | translate}}</p>
<cnsl-zitadel-tier [features]="features" [iam]="true"></cnsl-zitadel-tier>
<app-policy-grid [type]="PolicyGridType.IAM"></app-policy-grid>
<app-policy-grid [type]="PolicyComponentServiceType.ADMIN"></app-policy-grid>
</div>
<div metainfo>

View File

@@ -5,7 +5,6 @@ import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators';
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum';
import { PolicyGridType } from 'src/app/modules/policy-grid/policy-grid.component';
import { Features } from 'src/app/proto/generated/zitadel/features_pb';
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
import { User } from 'src/app/proto/generated/zitadel/user_pb';
@@ -25,7 +24,6 @@ export class IamComponent {
public membersSubject: BehaviorSubject<Member.AsObject[]>
= new BehaviorSubject<Member.AsObject[]>([]);
public PolicyGridType: any = PolicyGridType;
public features!: Features.AsObject;
constructor(public adminService: AdminService, private dialog: MatDialog, private toast: ToastService,

View File

@@ -48,7 +48,7 @@
</ng-container>
<ng-template appHasRole [appHasRole]="['policy.read']">
<app-policy-grid [type]="PolicyGridType.ORG"></app-policy-grid>
<app-policy-grid [type]="PolicyComponentServiceType.MGMT"></app-policy-grid>
</ng-template>
</div>

View File

@@ -8,7 +8,6 @@ import { catchError, finalize, map } from 'rxjs/operators';
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
import { ChangeType } from 'src/app/modules/changes/changes.component';
import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum';
import { PolicyGridType } from 'src/app/modules/policy-grid/policy-grid.component';
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
import { Features } from 'src/app/proto/generated/zitadel/features_pb';
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
@@ -42,8 +41,6 @@ export class OrgDetailComponent implements OnInit {
public totalMemberResult: number = 0;
public membersSubject: BehaviorSubject<Member.AsObject[]>
= new BehaviorSubject<Member.AsObject[]>([]);
public PolicyGridType: any = PolicyGridType;
public features!: Features.AsObject;
constructor(

View File

@@ -100,6 +100,30 @@ const routes: Routes = [
loadChildren: () => import('src/app/modules/policies/login-policy/login-policy.module')
.then(m => m.LoginPolicyModule),
},
{
path: PolicyComponentType.MESSAGETEXTS,
data: {
serviceType: PolicyComponentServiceType.MGMT,
},
loadChildren: () => import('src/app/modules/policies/message-texts/message-texts.module')
.then(m => m.MessageTextsPolicyModule),
},
{
path: PolicyComponentType.LOGINTEXTS,
data: {
serviceType: PolicyComponentServiceType.MGMT,
},
loadChildren: () => import('src/app/modules/policies/login-texts/login-texts.module')
.then(m => m.LoginTextsPolicyModule),
},
{
path: PolicyComponentType.PRIVACYPOLICY,
data: {
serviceType: PolicyComponentServiceType.MGMT,
},
loadChildren: () => import('src/app/modules/policies/privacy-policy/privacy-policy.module')
.then(m => m.PrivacyPolicyModule),
},
],
},
{

View File

@@ -6,7 +6,6 @@ import { MatIconModule } from '@angular/material/icon';
import { MatSelectModule } from '@angular/material/select';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { ImageCropperModule } from 'ngx-image-cropper';
import { DropzoneModule } from 'src/app/directives/dropzone/dropzone.module';
import { AvatarModule } from 'src/app/modules/avatar/avatar.module';
import { InputModule } from 'src/app/modules/input/input.module';
@@ -25,7 +24,6 @@ import { ProfilePictureComponent } from './profile-picture/profile-picture.compo
CommonModule,
FormsModule,
ReactiveFormsModule,
ImageCropperModule,
TranslateModule,
MatSelectModule,
MatButtonModule,

View File

@@ -19,10 +19,34 @@ import {
ClearViewResponse,
DeactivateIDPRequest,
DeactivateIDPResponse,
GetCustomDomainClaimedMessageTextRequest,
GetCustomDomainClaimedMessageTextResponse,
GetCustomInitMessageTextRequest,
GetCustomInitMessageTextResponse,
GetCustomLoginTextsRequest,
GetCustomLoginTextsResponse,
GetCustomOrgIAMPolicyRequest,
GetCustomOrgIAMPolicyResponse,
GetCustomPasswordResetMessageTextRequest,
GetCustomPasswordResetMessageTextResponse,
GetCustomVerifyEmailMessageTextRequest,
GetCustomVerifyEmailMessageTextResponse,
GetCustomVerifyPhoneMessageTextRequest,
GetCustomVerifyPhoneMessageTextResponse,
GetDefaultDomainClaimedMessageTextRequest,
GetDefaultDomainClaimedMessageTextResponse,
GetDefaultFeaturesRequest,
GetDefaultFeaturesResponse,
GetDefaultInitMessageTextRequest,
GetDefaultInitMessageTextResponse,
GetDefaultLoginTextsRequest,
GetDefaultLoginTextsResponse,
GetDefaultPasswordResetMessageTextRequest,
GetDefaultPasswordResetMessageTextResponse,
GetDefaultVerifyEmailMessageTextRequest,
GetDefaultVerifyEmailMessageTextResponse,
GetDefaultVerifyPhoneMessageTextRequest,
GetDefaultVerifyPhoneMessageTextResponse,
GetIDPByIDRequest,
GetIDPByIDResponse,
GetLabelPolicyRequest,
@@ -41,6 +65,10 @@ import {
GetPasswordLockoutPolicyResponse,
GetPreviewLabelPolicyRequest,
GetPreviewLabelPolicyResponse,
GetPrivacyPolicyRequest,
GetPrivacyPolicyResponse,
GetSupportedLanguagesRequest,
GetSupportedLanguagesResponse,
IDPQuery,
ListFailedEventsRequest,
ListFailedEventsResponse,
@@ -82,12 +110,26 @@ import {
RemoveMultiFactorFromLoginPolicyResponse,
RemoveSecondFactorFromLoginPolicyRequest,
RemoveSecondFactorFromLoginPolicyResponse,
ResetCustomLoginTextsToDefaultRequest,
ResetCustomLoginTextsToDefaultResponse,
ResetCustomOrgIAMPolicyToDefaultRequest,
ResetCustomOrgIAMPolicyToDefaultResponse,
ResetOrgFeaturesRequest,
ResetOrgFeaturesResponse,
SetCustomLoginTextsRequest,
SetCustomLoginTextsResponse,
SetDefaultDomainClaimedMessageTextRequest,
SetDefaultDomainClaimedMessageTextResponse,
SetDefaultFeaturesRequest,
SetDefaultFeaturesResponse,
SetDefaultInitMessageTextRequest,
SetDefaultInitMessageTextResponse,
SetDefaultPasswordResetMessageTextRequest,
SetDefaultPasswordResetMessageTextResponse,
SetDefaultVerifyEmailMessageTextRequest,
SetDefaultVerifyEmailMessageTextResponse,
SetDefaultVerifyPhoneMessageTextRequest,
SetDefaultVerifyPhoneMessageTextResponse,
SetOrgFeaturesRequest,
SetOrgFeaturesResponse,
SetUpOrgRequest,
@@ -112,6 +154,8 @@ import {
UpdatePasswordComplexityPolicyResponse,
UpdatePasswordLockoutPolicyRequest,
UpdatePasswordLockoutPolicyResponse,
UpdatePrivacyPolicyRequest,
UpdatePrivacyPolicyResponse,
} from '../proto/generated/zitadel/admin_pb';
import { SearchQuery } from '../proto/generated/zitadel/member_pb';
import { ListQuery } from '../proto/generated/zitadel/object_pb';
@@ -123,6 +167,114 @@ import { GrpcService } from './grpc.service';
export class AdminService {
constructor(private readonly grpcService: GrpcService) { }
public getSupportedLanguages(): Promise<GetSupportedLanguagesResponse.AsObject> {
const req = new GetSupportedLanguagesRequest();
return this.grpcService.admin.getSupportedLanguages(req, null).then(resp => resp.toObject());
}
public getDefaultLoginTexts(req: GetDefaultLoginTextsRequest):
Promise<GetDefaultLoginTextsResponse.AsObject> {
return this.grpcService.admin.getDefaultLoginTexts(req, null).then(resp => resp.toObject());
}
public getCustomLoginTexts(req: GetCustomLoginTextsRequest):
Promise<GetCustomLoginTextsResponse.AsObject> {
return this.grpcService.admin.getCustomLoginTexts(req, null).then(resp => resp.toObject());
}
public setCustomLoginText(req: SetCustomLoginTextsRequest):
Promise<SetCustomLoginTextsResponse.AsObject> {
return this.grpcService.admin.setCustomLoginText(req, null).then(resp => resp.toObject());
}
public resetCustomLoginTextToDefault(lang: string): Promise<ResetCustomLoginTextsToDefaultResponse.AsObject> {
const req = new ResetCustomLoginTextsToDefaultRequest();
req.setLanguage(lang);
return this.grpcService.admin.resetCustomLoginTextToDefault(req, null).then(resp => resp.toObject());
}
// message texts
public getDefaultInitMessageText(req: GetDefaultInitMessageTextRequest):
Promise<GetDefaultInitMessageTextResponse.AsObject> {
return this.grpcService.admin.getDefaultInitMessageText(req, null).then(resp => resp.toObject());
}
public getCustomInitMessageText(req: GetCustomInitMessageTextRequest):
Promise<GetCustomInitMessageTextResponse.AsObject> {
return this.grpcService.admin.getCustomInitMessageText(req, null).then(resp => resp.toObject());
}
public setDefaultInitMessageText(req: SetDefaultInitMessageTextRequest):
Promise<SetDefaultInitMessageTextResponse.AsObject> {
return this.grpcService.admin.setDefaultInitMessageText(req, null).then(resp => resp.toObject());
}
public getDefaultVerifyEmailMessageText(req: GetDefaultVerifyEmailMessageTextRequest):
Promise<GetDefaultVerifyEmailMessageTextResponse.AsObject> {
return this.grpcService.admin.getDefaultVerifyEmailMessageText(req, null).then(resp => resp.toObject());
}
public getCustomVerifyEmailMessageText(req: GetCustomVerifyEmailMessageTextRequest):
Promise<GetCustomVerifyEmailMessageTextResponse.AsObject> {
return this.grpcService.admin.getCustomVerifyEmailMessageText(req, null).then(resp => resp.toObject());
}
public setDefaultVerifyEmailMessageText(req: SetDefaultVerifyEmailMessageTextRequest):
Promise<SetDefaultVerifyEmailMessageTextResponse.AsObject> {
return this.grpcService.admin.setDefaultVerifyEmailMessageText(req, null).then(resp => resp.toObject());
}
public getDefaultVerifyPhoneMessageText(req: GetDefaultVerifyPhoneMessageTextRequest):
Promise<GetDefaultVerifyPhoneMessageTextResponse.AsObject> {
return this.grpcService.admin.getDefaultVerifyPhoneMessageText(req, null).then(resp => resp.toObject());
}
public getCustomVerifyPhoneMessageText(req: GetCustomVerifyPhoneMessageTextRequest):
Promise<GetCustomVerifyPhoneMessageTextResponse.AsObject> {
return this.grpcService.admin.getCustomVerifyPhoneMessageText(req, null).then(resp => resp.toObject());
}
public setDefaultVerifyPhoneMessageText(req: SetDefaultVerifyPhoneMessageTextRequest):
Promise<SetDefaultVerifyPhoneMessageTextResponse.AsObject> {
return this.grpcService.admin.setDefaultVerifyPhoneMessageText(req, null).then(resp => resp.toObject());
}
public getDefaultPasswordResetMessageText(req: GetDefaultPasswordResetMessageTextRequest):
Promise<GetDefaultPasswordResetMessageTextResponse.AsObject> {
return this.grpcService.admin.getDefaultPasswordResetMessageText(req, null).then(resp => resp.toObject());
}
public getCustomPasswordResetMessageText(req: GetCustomPasswordResetMessageTextRequest):
Promise<GetCustomPasswordResetMessageTextResponse.AsObject> {
return this.grpcService.admin.getCustomPasswordResetMessageText(req, null).then(resp => resp.toObject());
}
public setDefaultPasswordResetMessageText(req: SetDefaultPasswordResetMessageTextRequest):
Promise<SetDefaultPasswordResetMessageTextResponse.AsObject> {
return this.grpcService.admin.setDefaultPasswordResetMessageText(req, null).then(resp => resp.toObject());
}
public getDefaultDomainClaimedMessageText(req: GetDefaultDomainClaimedMessageTextRequest):
Promise<GetDefaultDomainClaimedMessageTextResponse.AsObject> {
return this.grpcService.admin.getDefaultDomainClaimedMessageText(req, null).then(resp => resp.toObject());
}
public getCustomDomainClaimedMessageText(req: GetCustomDomainClaimedMessageTextRequest):
Promise<GetCustomDomainClaimedMessageTextResponse.AsObject> {
return this.grpcService.admin.getCustomDomainClaimedMessageText(req, null).then(resp => resp.toObject());
}
public setDefaultDomainClaimedMessageText(req: SetDefaultDomainClaimedMessageTextRequest):
Promise<SetDefaultDomainClaimedMessageTextResponse.AsObject> {
return this.grpcService.admin.setDefaultDomainClaimedMessageText(req, null).then(resp => resp.toObject());
}
public SetUpOrg(
org: SetUpOrgRequest.Org,
human: SetUpOrgRequest.Human,
@@ -195,6 +347,17 @@ export class AdminService {
return this.grpcService.admin.removeFailedEvent(req, null).then(resp => resp.toObject());
}
public getPrivacyPolicy():
Promise<GetPrivacyPolicyResponse.AsObject> {
const req = new GetPrivacyPolicyRequest();
return this.grpcService.admin.getPrivacyPolicy(req, null).then(resp => resp.toObject());
}
public updatePrivacyPolicy(req: UpdatePrivacyPolicyRequest):
Promise<UpdatePrivacyPolicyResponse.AsObject> {
return this.grpcService.admin.updatePrivacyPolicy(req, null).then(resp => resp.toObject());
}
// Features
public getOrgFeatures(orgId: string): Promise<GetOrgFeaturesResponse.AsObject> {

View File

@@ -3,18 +3,7 @@ import { Empty } from 'google-protobuf/google/protobuf/empty_pb';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { BehaviorSubject } from 'rxjs';
import {
RemoveLabelPolicyFontRequest,
RemoveLabelPolicyFontResponse,
RemoveLabelPolicyIconDarkRequest,
RemoveLabelPolicyIconDarkResponse,
RemoveLabelPolicyIconRequest,
RemoveLabelPolicyIconResponse,
RemoveLabelPolicyLogoDarkRequest,
RemoveLabelPolicyLogoDarkResponse,
RemoveLabelPolicyLogoRequest,
RemoveLabelPolicyLogoResponse,
} from '../proto/generated/zitadel/admin_pb';
import { GetDefaultDomainClaimedMessageTextRequest } from '../proto/generated/zitadel/admin_pb';
import { AppQuery } from '../proto/generated/zitadel/app_pb';
import { KeyType } from '../proto/generated/zitadel/auth_n_key_pb';
import { ChangeQuery } from '../proto/generated/zitadel/change_pb';
@@ -36,6 +25,8 @@ import {
AddCustomPasswordComplexityPolicyResponse,
AddCustomPasswordLockoutPolicyRequest,
AddCustomPasswordLockoutPolicyResponse,
AddCustomPrivacyPolicyRequest,
AddCustomPrivacyPolicyResponse,
AddHumanUserRequest,
AddHumanUserResponse,
AddIDPToLoginPolicyRequest,
@@ -90,10 +81,33 @@ import {
GenerateOrgDomainValidationResponse,
GetAppByIDRequest,
GetAppByIDResponse,
GetCustomDomainClaimedMessageTextRequest,
GetCustomDomainClaimedMessageTextResponse,
GetCustomInitMessageTextRequest,
GetCustomInitMessageTextResponse,
GetCustomLoginTextsRequest,
GetCustomLoginTextsResponse,
GetCustomPasswordResetMessageTextRequest,
GetCustomPasswordResetMessageTextResponse,
GetCustomVerifyEmailMessageTextRequest,
GetCustomVerifyEmailMessageTextResponse,
GetCustomVerifyPhoneMessageTextRequest,
GetCustomVerifyPhoneMessageTextResponse,
GetDefaultDomainClaimedMessageTextResponse,
GetDefaultInitMessageTextRequest,
GetDefaultInitMessageTextResponse,
GetDefaultLabelPolicyRequest,
GetDefaultLabelPolicyResponse,
GetDefaultLoginTextsRequest,
GetDefaultLoginTextsResponse,
GetDefaultPasswordComplexityPolicyRequest,
GetDefaultPasswordComplexityPolicyResponse,
GetDefaultPasswordResetMessageTextRequest,
GetDefaultPasswordResetMessageTextResponse,
GetDefaultVerifyEmailMessageTextRequest,
GetDefaultVerifyEmailMessageTextResponse,
GetDefaultVerifyPhoneMessageTextRequest,
GetDefaultVerifyPhoneMessageTextResponse,
GetFeaturesRequest,
GetFeaturesResponse,
GetGrantedProjectByIDRequest,
@@ -128,10 +142,14 @@ import {
GetPasswordLockoutPolicyResponse,
GetPreviewLabelPolicyRequest,
GetPreviewLabelPolicyResponse,
GetPrivacyPolicyRequest,
GetPrivacyPolicyResponse,
GetProjectByIDRequest,
GetProjectByIDResponse,
GetProjectGrantByIDRequest,
GetProjectGrantByIDResponse,
GetSupportedLanguagesRequest,
GetSupportedLanguagesResponse,
GetUserByIDRequest,
GetUserByIDResponse,
GetUserByLoginNameGlobalRequest,
@@ -214,6 +232,16 @@ import {
RemoveAppKeyResponse,
RemoveAppRequest,
RemoveAppResponse,
RemoveCustomLabelPolicyFontRequest,
RemoveCustomLabelPolicyFontResponse,
RemoveCustomLabelPolicyIconDarkRequest,
RemoveCustomLabelPolicyIconDarkResponse,
RemoveCustomLabelPolicyIconRequest,
RemoveCustomLabelPolicyIconResponse,
RemoveCustomLabelPolicyLogoDarkRequest,
RemoveCustomLabelPolicyLogoDarkResponse,
RemoveCustomLabelPolicyLogoRequest,
RemoveCustomLabelPolicyLogoResponse,
RemoveHumanAuthFactorOTPRequest,
RemoveHumanAuthFactorOTPResponse,
RemoveHumanAuthFactorU2FRequest,
@@ -256,6 +284,18 @@ import {
ResendHumanInitializationRequest,
ResendHumanInitializationResponse,
ResendHumanPhoneVerificationRequest,
ResetCustomDomainClaimedMessageTextToDefaultRequest,
ResetCustomDomainClaimedMessageTextToDefaultResponse,
ResetCustomInitMessageTextToDefaultRequest,
ResetCustomInitMessageTextToDefaultResponse,
ResetCustomLoginTextsToDefaultRequest,
ResetCustomLoginTextsToDefaultResponse,
ResetCustomPasswordResetMessageTextToDefaultRequest,
ResetCustomPasswordResetMessageTextToDefaultResponse,
ResetCustomVerifyEmailMessageTextToDefaultRequest,
ResetCustomVerifyEmailMessageTextToDefaultResponse,
ResetCustomVerifyPhoneMessageTextToDefaultRequest,
ResetCustomVerifyPhoneMessageTextToDefaultResponse,
ResetLabelPolicyToDefaultRequest,
ResetLabelPolicyToDefaultResponse,
ResetLoginPolicyToDefaultRequest,
@@ -266,7 +306,21 @@ import {
ResetPasswordComplexityPolicyToDefaultResponse,
ResetPasswordLockoutPolicyToDefaultRequest,
ResetPasswordLockoutPolicyToDefaultResponse,
ResetPrivacyPolicyToDefaultRequest,
ResetPrivacyPolicyToDefaultResponse,
SendHumanResetPasswordNotificationRequest,
SetCustomDomainClaimedMessageTextRequest,
SetCustomDomainClaimedMessageTextResponse,
SetCustomInitMessageTextRequest,
SetCustomInitMessageTextResponse,
SetCustomLoginTextsRequest,
SetCustomLoginTextsResponse,
SetCustomPasswordResetMessageTextRequest,
SetCustomPasswordResetMessageTextResponse,
SetCustomVerifyEmailMessageTextRequest,
SetCustomVerifyEmailMessageTextResponse,
SetCustomVerifyPhoneMessageTextRequest,
SetCustomVerifyPhoneMessageTextResponse,
SetHumanInitialPasswordRequest,
SetPrimaryOrgDomainRequest,
SetPrimaryOrgDomainResponse,
@@ -284,6 +338,8 @@ import {
UpdateCustomPasswordComplexityPolicyResponse,
UpdateCustomPasswordLockoutPolicyRequest,
UpdateCustomPasswordLockoutPolicyResponse,
UpdateCustomPrivacyPolicyRequest,
UpdateCustomPrivacyPolicyResponse,
UpdateHumanEmailRequest,
UpdateHumanEmailResponse,
UpdateHumanPhoneRequest,
@@ -340,6 +396,146 @@ export class ManagementService {
constructor(private readonly grpcService: GrpcService) { }
public getSupportedLanguages(): Promise<GetSupportedLanguagesResponse.AsObject> {
const req = new GetSupportedLanguagesRequest();
return this.grpcService.mgmt.getSupportedLanguages(req, null).then(resp => resp.toObject());
}
public getDefaultLoginTexts(req: GetDefaultLoginTextsRequest): Promise<GetDefaultLoginTextsResponse.AsObject> {
return this.grpcService.mgmt.getDefaultLoginTexts(req, null).then(resp => resp.toObject());
}
public getCustomLoginTexts(req: GetCustomLoginTextsRequest): Promise<GetCustomLoginTextsResponse.AsObject> {
return this.grpcService.mgmt.getCustomLoginTexts(req, null).then(resp => resp.toObject());
}
public setCustomLoginText(req: SetCustomLoginTextsRequest): Promise<SetCustomLoginTextsResponse.AsObject> {
return this.grpcService.mgmt.setCustomLoginText(req, null).then(resp => resp.toObject());
}
public resetCustomLoginTextToDefault(lang: string): Promise<ResetCustomLoginTextsToDefaultResponse.AsObject> {
const req = new ResetCustomLoginTextsToDefaultRequest();
req.setLanguage(lang);
return this.grpcService.mgmt.resetCustomLoginTextToDefault(req, null).then(resp => resp.toObject());
}
// message texts
public getDefaultInitMessageText(req: GetDefaultInitMessageTextRequest):
Promise<GetDefaultInitMessageTextResponse.AsObject> {
return this.grpcService.mgmt.getDefaultInitMessageText(req, null).then(resp => resp.toObject());
}
public getCustomInitMessageText(req: GetCustomInitMessageTextRequest):
Promise<GetCustomInitMessageTextResponse.AsObject> {
return this.grpcService.mgmt.getCustomInitMessageText(req, null).then(resp => resp.toObject());
}
public setCustomInitMessageText(req: SetCustomInitMessageTextRequest):
Promise<SetCustomInitMessageTextResponse.AsObject> {
return this.grpcService.mgmt.setCustomInitMessageText(req, null).then(resp => resp.toObject());
}
public resetCustomInitMessageTextToDefault(lang: string):
Promise<ResetCustomInitMessageTextToDefaultResponse.AsObject> {
const req = new ResetCustomInitMessageTextToDefaultRequest();
req.setLanguage(lang);
return this.grpcService.mgmt.resetCustomInitMessageTextToDefault(req, null).then(resp => resp.toObject());
}
public getDefaultVerifyEmailMessageText(req: GetDefaultVerifyEmailMessageTextRequest):
Promise<GetDefaultVerifyEmailMessageTextResponse.AsObject> {
return this.grpcService.mgmt.getDefaultVerifyEmailMessageText(req, null).then(resp => resp.toObject());
}
public getCustomVerifyEmailMessageText(req: GetCustomVerifyEmailMessageTextRequest):
Promise<GetCustomVerifyEmailMessageTextResponse.AsObject> {
return this.grpcService.mgmt.getCustomVerifyEmailMessageText(req, null).then(resp => resp.toObject());
}
public setCustomVerifyEmailMessageText(req: SetCustomVerifyEmailMessageTextRequest):
Promise<SetCustomVerifyEmailMessageTextResponse.AsObject> {
return this.grpcService.mgmt.setCustomVerifyEmailMessageText(req, null).then(resp => resp.toObject());
}
public resetCustomVerifyEmailMessageTextToDefault(lang: string):
Promise<ResetCustomVerifyEmailMessageTextToDefaultResponse.AsObject> {
const req = new ResetCustomVerifyEmailMessageTextToDefaultRequest();
req.setLanguage(lang);
return this.grpcService.mgmt.resetCustomVerifyEmailMessageTextToDefault(req, null).then(resp => resp.toObject());
}
public getDefaultVerifyPhoneMessageText(req: GetDefaultVerifyPhoneMessageTextRequest):
Promise<GetDefaultVerifyPhoneMessageTextResponse.AsObject> {
return this.grpcService.mgmt.getDefaultVerifyPhoneMessageText(req, null).then(resp => resp.toObject());
}
public getCustomVerifyPhoneMessageText(req: GetCustomVerifyPhoneMessageTextRequest):
Promise<GetCustomVerifyPhoneMessageTextResponse.AsObject> {
return this.grpcService.mgmt.getCustomVerifyPhoneMessageText(req, null).then(resp => resp.toObject());
}
public setCustomVerifyPhoneMessageText(req: SetCustomVerifyPhoneMessageTextRequest):
Promise<SetCustomVerifyPhoneMessageTextResponse.AsObject> {
return this.grpcService.mgmt.setCustomVerifyPhoneMessageText(req, null).then(resp => resp.toObject());
}
public resetCustomVerifyPhoneMessageTextToDefault(lang: string):
Promise<ResetCustomVerifyPhoneMessageTextToDefaultResponse.AsObject> {
const req = new ResetCustomVerifyPhoneMessageTextToDefaultRequest();
req.setLanguage(lang);
return this.grpcService.mgmt.resetCustomVerifyPhoneMessageTextToDefault(req, null).then(resp => resp.toObject());
}
public getDefaultPasswordResetMessageText(req: GetDefaultPasswordResetMessageTextRequest):
Promise<GetDefaultPasswordResetMessageTextResponse.AsObject> {
return this.grpcService.mgmt.getDefaultPasswordResetMessageText(req, null).then(resp => resp.toObject());
}
public getCustomPasswordResetMessageText(req: GetCustomPasswordResetMessageTextRequest):
Promise<GetCustomPasswordResetMessageTextResponse.AsObject> {
return this.grpcService.mgmt.getCustomPasswordResetMessageText(req, null).then(resp => resp.toObject());
}
public setCustomPasswordResetMessageText(req: SetCustomPasswordResetMessageTextRequest):
Promise<SetCustomPasswordResetMessageTextResponse.AsObject> {
return this.grpcService.mgmt.setCustomPasswordResetMessageText(req, null).then(resp => resp.toObject());
}
public resetCustomPasswordResetMessageTextToDefault(lang: string):
Promise<ResetCustomPasswordResetMessageTextToDefaultResponse.AsObject> {
const req = new ResetCustomPasswordResetMessageTextToDefaultRequest();
req.setLanguage(lang);
return this.grpcService.mgmt.resetCustomPasswordResetMessageTextToDefault(req, null).then(resp => resp.toObject());
}
public getDefaultDomainClaimedMessageText(req: GetDefaultDomainClaimedMessageTextRequest):
Promise<GetDefaultDomainClaimedMessageTextResponse.AsObject> {
return this.grpcService.mgmt.getDefaultDomainClaimedMessageText(req, null).then(resp => resp.toObject());
}
public getCustomDomainClaimedMessageText(req: GetCustomDomainClaimedMessageTextRequest):
Promise<GetCustomDomainClaimedMessageTextResponse.AsObject> {
return this.grpcService.mgmt.getCustomDomainClaimedMessageText(req, null).then(resp => resp.toObject());
}
public setCustomDomainClaimedMessageCustomText(req: SetCustomDomainClaimedMessageTextRequest):
Promise<SetCustomDomainClaimedMessageTextResponse.AsObject> {
return this.grpcService.mgmt.setCustomDomainClaimedMessageCustomText(req, null).then(resp => resp.toObject());
}
public resetCustomDomainClaimedMessageTextToDefault(lang: string):
Promise<ResetCustomDomainClaimedMessageTextToDefaultResponse.AsObject> {
const req = new ResetCustomDomainClaimedMessageTextToDefaultRequest();
req.setLanguage(lang);
return this.grpcService.mgmt.resetCustomDomainClaimedMessageTextToDefault(req, null).then(resp => resp.toObject());
}
public listOrgIDPs(
limit?: number,
offset?: number,
@@ -360,6 +556,28 @@ export class ManagementService {
return this.grpcService.mgmt.listOrgIDPs(req, null).then(resp => resp.toObject());
}
public getPrivacyPolicy():
Promise<GetPrivacyPolicyResponse.AsObject> {
const req = new GetPrivacyPolicyRequest();
return this.grpcService.mgmt.getPrivacyPolicy(req, null).then(resp => resp.toObject());
}
public addCustomPrivacyPolicy(req: AddCustomPrivacyPolicyRequest):
Promise<AddCustomPrivacyPolicyResponse.AsObject> {
return this.grpcService.mgmt.addCustomPrivacyPolicy(req, null).then(resp => resp.toObject());
}
public updateCustomPrivacyPolicy(req: UpdateCustomPrivacyPolicyRequest):
Promise<UpdateCustomPrivacyPolicyResponse.AsObject> {
return this.grpcService.mgmt.updateCustomPrivacyPolicy(req, null).then(resp => resp.toObject());
}
public resetPrivacyPolicyToDefault():
Promise<ResetPrivacyPolicyToDefaultResponse.AsObject> {
const req = new ResetPrivacyPolicyToDefaultRequest();
return this.grpcService.mgmt.resetPrivacyPolicyToDefault(req, null).then(resp => resp.toObject());
}
public listHumanPasswordless(userId: string): Promise<ListHumanPasswordlessResponse.AsObject> {
const req = new ListHumanPasswordlessRequest();
req.setUserId(userId);
@@ -781,35 +999,34 @@ export class ManagementService {
}
public removeLabelPolicyFont():
Promise<RemoveLabelPolicyFontResponse.AsObject> {
const req = new RemoveLabelPolicyFontRequest();
Promise<RemoveCustomLabelPolicyFontResponse.AsObject> {
const req = new RemoveCustomLabelPolicyFontRequest();
return this.grpcService.mgmt.removeCustomLabelPolicyFont(req, null).then(resp => resp.toObject());
}
public removeLabelPolicyIcon():
Promise<RemoveLabelPolicyIconResponse.AsObject> {
const req = new RemoveLabelPolicyIconRequest();
Promise<RemoveCustomLabelPolicyIconResponse.AsObject> {
const req = new RemoveCustomLabelPolicyIconRequest();
return this.grpcService.mgmt.removeCustomLabelPolicyIcon(req, null).then(resp => resp.toObject());
}
public removeLabelPolicyIconDark():
Promise<RemoveLabelPolicyIconDarkResponse.AsObject> {
const req = new RemoveLabelPolicyIconDarkRequest();
Promise<RemoveCustomLabelPolicyIconDarkResponse.AsObject> {
const req = new RemoveCustomLabelPolicyIconDarkRequest();
return this.grpcService.mgmt.removeCustomLabelPolicyIconDark(req, null).then(resp => resp.toObject());
}
public removeLabelPolicyLogo():
Promise<RemoveLabelPolicyLogoResponse.AsObject> {
const req = new RemoveLabelPolicyLogoRequest();
Promise<RemoveCustomLabelPolicyLogoResponse.AsObject> {
const req = new RemoveCustomLabelPolicyLogoRequest();
return this.grpcService.mgmt.removeCustomLabelPolicyLogo(req, null).then(resp => resp.toObject());
}
public removeLabelPolicyLogoDark():
Promise<RemoveLabelPolicyLogoDarkResponse.AsObject> {
const req = new RemoveLabelPolicyLogoDarkRequest();
Promise<RemoveCustomLabelPolicyLogoDarkResponse.AsObject> {
const req = new RemoveCustomLabelPolicyLogoDarkRequest();
return this.grpcService.mgmt.removeCustomLabelPolicyLogoDark(req, null).then(resp => resp.toObject());
}
public getOrgIAMPolicy(): Promise<GetOrgIAMPolicyResponse.AsObject> {
const req = new GetOrgIAMPolicyRequest();
return this.grpcService.mgmt.getOrgIAMPolicy(req, null).then(resp => resp.toObject());

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 B

View File

@@ -60,7 +60,7 @@
"IAM": "Identity- und Access-Management",
"IAM_DESC": "Verwalte Deine Organisationen und Administratoren.",
"IAM_BUTTON": "ZITADEL verwalten",
"WELCOME": "Willkommen",
"WELCOME": "Loslegen mit ZITADEL",
"WELCOMESENTENCE": "Hier findest Du die empfohlenen Aktionen basierend auf Deinen zuletzt erworbenen Berechtigungen. Beachte bitte, dass Du möglicherweise Deine Organisation in der Kopfzeile wechseln musst.",
"DISCLAIMER": "Du kannst nur die Einstellungen Deiner aktuellen Organisation, die in der Kopfzeile angegeben ist, sehen. ZITADEL behandelt Deine Daten vertraulich und sicher.",
"DISCLAIMERLINK": "Mehr Informationen zur Sicherheit",
@@ -107,6 +107,9 @@
},
"ACTIONS": {
"SET":"Übernehmen",
"RESETDEFAULT":"Auf Standard zurücksetzen",
"RESETTO":"Zurücksetzen auf: ",
"RESETCURRENT":"Auf aktuellen Wert zurücksetzen",
"SHOW":"Aufklappen",
"HIDE":"Zuklappen",
"SAVE": "Speichern",
@@ -136,7 +139,9 @@
"PIN": "Anpinnen",
"CONFIGURE": "Konfigurieren",
"SEND": "Senden",
"NEWVALUE": "Neuer Wert"
"NEWVALUE": "Neuer Wert",
"RESTORE":"Wiederherstellen",
"CONTINUEWITHOUTSAVE":"Ohne speichern fortfahren"
},
"RESOURCEID": "Ressourcen-ID",
"TABLE": {
@@ -628,7 +633,8 @@
"LABELPOLICYPRIVATELABEL": "Label Richtlinie - benutzerdefiniert",
"LABELPOLICYWATERMARK": "Label Richtlinie - Wasserzeichen",
"CUSTOMDOMAIN": "Domänen Verifikation - verfügbar",
"CUSTOMTEXT": "Benutzerdefinierte Texte"
"CUSTOMTEXT": "Benutzerdefinierte Texte",
"PRIVACYPOLICY":"Datenschutzrichtlinie und AGB - benutzerdefiniert"
},
"TIERSTATES": {
"0": "Aktiv",
@@ -699,6 +705,87 @@
"DESCRIPTIONCREATEMGMT": "Nutzer können sich mit den verfügbaren Idps authentifizieren. Achtung: Es kann zwischen System- und organisationsspezifischen Providern gewählt werden.",
"SAVED": "Erfolgreich gespeichert."
},
"PRIVACY_POLICY": {
"TITLE": "Datenschutzbestimmungen und AGB",
"DESCRIPTION": "Legen Sie Ihre Datenschutzrichtlinien und Nutzungsbedingungen fest",
"TOSLINK":"Link zu den Allgemeinen Geschäftsbedingungen",
"POLICYLINK":"Link zur den Datenschutzrichtlinien",
"SAVED": "Saved successfully!",
"RESET_TITLE":"Standardwerte wiederherstellen",
"RESET_DESCRIPTION":"Sie sind im Begriff die Standardlinks für die AGBs und Datenschutzrichtlinie wiederherzustellen. Wollen Sie fortfahren?"
},
"LOGIN_TEXTS": {
"TITLE":"Login Interface Texte",
"DESCRIPTION": "Definiere die Texte für das Logininterface. Wenn Texte leer sind, wird der als Platzhalter angezeigte Standardwert verwendet.",
"DESCRIPTION_SHORT":"Definiere die Texte für das Logininterface.",
"NEWERVERSIONEXISTS":"Neuere Version verfügbar",
"CURRENTDATE":"Momentaner Stand",
"CHANGEDATE":"Neuer Stand vom",
"KEYNAME":"Login Screen / Interface",
"RESET_TITLE":"Standardwerte wiederherstellen",
"RESET_DESCRIPTION":"Sie sind im Begriff alle Standardwerte wiederherzustellen. Alle von Ihnen gesetzen Änderungen werden unwiederruflich gelöscht. Wollen Sie fortfahren?",
"UNSAVED_TITLE":"Ohne speichern fortfahren?",
"UNSAVED_DESCRIPTION":"Sie haben Änderungen vorgenommen ohne zu speichern. Möchten Sie jetzt speichern?",
"LOCALE":"Sprachcode",
"LOCALES": {
"de":"German",
"en":"English",
"it":"Italian",
"fr":"French"
},
"KEYS": {
"emailVerificationDoneText":"Email Verification erfolgreich",
"emailVerificationText":"Email Verification",
"externalUserNotFoundText":"Externer Benutzer nicht gefunden",
"footerText":"Fusszeile",
"initMfaDoneText":"MFA Initialisierung erfolgreich",
"initMfaOtpText":"MFA Initialisierung",
"initMfaPromptText":"MFA Einrichtungsaufforderung",
"initMfaU2fText":"Universeller Zweitfaktor Initialisierung",
"initPasswordDoneText":"Passwort Initialisierung erfolgreich",
"initPasswordText":"Password Initialisierung",
"initializeDoneText":"Benutzereinrichtung erfolgreich",
"initializeUserText":"Benutzereinrichtung",
"linkingUserDoneText":"Benutzerverlinkung erfolgreich",
"loginText":"Anmelden",
"logoutText":"Abmelden",
"mfaProvidersText":"MFA Provider",
"passwordChangeDoneText":"Passwortwechsel erfolgreich",
"passwordChangeText":"Passwortwechsel",
"passwordResetDoneText":"Passwort zurücksetzen erfolgreich",
"passwordText":"Passwort",
"passwordlessText":"Passwortlose Authentifizierung",
"registrationOptionText":"Registrierungsoptionen",
"registrationOrgText":"Organisation registrieren",
"registrationUserText":"Benutzer registrieren",
"selectAccountText":"Account wählen",
"successLoginText":"Erfolgreiche Anmeldung",
"usernameChangeDoneText":"Benutzernamenwechsel erfolgreich",
"usernameChangeText":"Benutzernamenwechsel",
"verifyMfaOtpText":"OTP Verifikation",
"verifyMfaU2fText":"Universeller Zweitfaktor Verifikation"
}
},
"MESSAGE_TEXTS": {
"TITLE":"Email Texte",
"DESCRIPTION": "Definiere die Texte für deine Benachrichtigungsmails",
"TYPES": {
"INIT":"Initialisierung",
"VE":"Emailverifikation",
"VP":"Telefonnummerverifikation",
"PR":"Password Wiederherstellung",
"DC":"Domainbeanspruchung"
},
"CHIPS": {
"firstname":"Vorname",
"lastname":"Nachname",
"code":"Code",
"preferredLoginName":"Bevorzugter Anmeldename"
},
"TOAST": {
"UPDATED":"Benutzerdefinierte Texte gespeichert."
}
},
"DEFAULTLABEL": "Die aktuelle Richtlinie entspricht der IAM-Standard Einstellung.",
"BTN_INSTALL": "Installieren",
"BTN_EDIT": "Modifizieren",

View File

@@ -60,7 +60,7 @@
"IAM": "Identity and Access Management",
"IAM_DESC": "Manage your organisations and administrators.",
"IAM_BUTTON": "Manage ZITADEL",
"WELCOME": "Welcome",
"WELCOME": "Get started with ZITADEL",
"WELCOMESENTENCE": "Here you can find recommended actions based on your last acquired permissions. Note that you may have to select your organisation in the header above.",
"DISCLAIMER": "You can only see settings of your current organisation specified in the header. ZITADEL treats your data confidentially and securely.",
"DISCLAIMERLINK": "Further information",
@@ -107,6 +107,9 @@
},
"ACTIONS": {
"SET":"Set",
"RESETDEFAULT":"Reset to Default",
"RESETTO":"Reset to: ",
"RESETCURRENT":"Reset to current",
"SHOW":"Show",
"HIDE":"Hide",
"SAVE": "Save",
@@ -136,7 +139,9 @@
"PIN": "Pin / Unpin",
"CONFIGURE": "Configure",
"SEND": "Send",
"NEWVALUE": "New Value"
"NEWVALUE": "New Value",
"RESTORE":"Restore",
"CONTINUEWITHOUTSAVE":"Continue without saving"
},
"RESOURCEID": "Resource Id",
"TABLE": {
@@ -628,7 +633,8 @@
"LABELPOLICYPRIVATELABEL": "Label Policy - custom",
"LABELPOLICYWATERMARK": "Label Policy - watermark",
"CUSTOMDOMAIN": "Domain Verification - available",
"CUSTOMTEXT": "Custom texts"
"CUSTOMTEXT": "Custom texts",
"PRIVACYPOLICY":"Privacy Policy and TOS - custom"
},
"TIERSTATES": {
"0": "Active",
@@ -701,6 +707,87 @@
"DESCRIPTIONCREATEMGMT": "Users can choose from the available identity providers below. Note: You can use System-set providers as well as providers set for your organisation only.",
"SAVED": "Saved successfully!"
},
"PRIVACY_POLICY": {
"TITLE": "Privacy Policy and TOS",
"DESCRIPTION": "Set your Privacy Policy and Terms of Service Links",
"TOSLINK":"Link to Terms of Service",
"POLICYLINK":"Link to Privacy Policy",
"SAVED": "Saved successfully!",
"RESET_TITLE":"Restore Default Values",
"RESET_DESCRIPTION":"You are about to restore the default Links for TOS and Privacy Policy. Do you really want to continue?"
},
"LOGIN_TEXTS": {
"TITLE":"Login Interface Texts",
"DESCRIPTION": "Define your texts for the login interfaces. If texts are empty, the default Value shown as the placeholder will be used.",
"DESCRIPTION_SHORT":"Define your texts for the login interfaces.",
"NEWERVERSIONEXISTS":"Newer Version exists",
"CURRENTDATE":"Current configuration",
"CHANGEDATE":"Newer Version from",
"KEYNAME":"Login Screen / Interface",
"RESET_TITLE":"Restore Default Values",
"RESET_DESCRIPTION":"You are about to restore all default values. All changes you have made will be permanently deleted. Do you really want to continue?",
"UNSAVED_TITLE":"Continue without saving?",
"UNSAVED_DESCRIPTION":"Your have made changes without saving. Do you want to save now?",
"LOCALE":"Locale Code",
"LOCALES": {
"de":"German",
"en":"English",
"it":"Italian",
"fr":"French"
},
"KEYS": {
"emailVerificationDoneText":"Email verification done",
"emailVerificationText":"Email verification",
"externalUserNotFoundText":"External user not found",
"footerText":"Footer",
"initMfaDoneText":"Initialize MFA done",
"initMfaOtpText":"Initialize MFA",
"initMfaPromptText":"Initialize MFA Prompt",
"initMfaU2fText":"Initialize Universal Second Factor",
"initPasswordDoneText":"Initialize password done",
"initPasswordText":"Initialize password",
"initializeDoneText":"Initialize user done",
"initializeUserText":"Initialize user",
"linkingUserDoneText":"Linking user done",
"loginText":"Login",
"logoutText":"Logout",
"mfaProvidersText":"MFA Providers",
"passwordChangeDoneText":"Password change done",
"passwordChangeText":"Password change",
"passwordResetDoneText":"Password reset done",
"passwordText":"Password",
"passwordlessText":"Passwordless",
"registrationOptionText":"Registration Options",
"registrationOrgText":"Register Org",
"registrationUserText":"Register User",
"selectAccountText":"Select Account",
"successLoginText":"Login with success",
"usernameChangeDoneText":"Username change done",
"usernameChangeText":"Username change",
"verifyMfaOtpText":"Verify OTP",
"verifyMfaU2fText":"Verify Universal Second Factor"
}
},
"MESSAGE_TEXTS": {
"TITLE":"Message Texts",
"DESCRIPTION": "Define your texts for your notication mails.",
"TYPES": {
"INIT":"Initialization",
"VE":"Verify Email",
"VP":"Verify Phone",
"PR":"Password Reset",
"DC":"Domain Claim"
},
"CHIPS": {
"firstname":"Firstname",
"lastname":"Lastname",
"code":"Code",
"preferredLoginName":"Preferred Login Name"
},
"TOAST": {
"UPDATED":"Custom Texts saved."
}
},
"DEFAULTLABEL": "The currently set guideline corresponds to the standard setting set by the IAM Administrator.",
"BTN_INSTALL": "Setup",
"BTN_EDIT": "Modify",

View File

@@ -152,6 +152,10 @@ $custom-typography: mat.define-typography-config($font-family: 'Lato');
@include mat.core($custom-typography);
// textarea {
// font-family: 'Lato';
// }
// default theme
@include component-themes($dark-theme);
@include mat.all-component-themes($dark-theme);

View File

@@ -12,7 +12,7 @@
"importHelpers": true,
"target": "es2015",
"typeRoots": ["node_modules/@types"],
"lib": ["es2018", "dom"],
"lib": ["es2019", "dom"],
"strict": true
},
"angularCompilerOptions": {