mirror of
https://github.com/zitadel/zitadel.git
synced 2025-04-16 08:41:30 +00:00
feat(console-v2): login policy extension, domain policy, filter and UI fixes (#3644)
* show filter count when set * toast contrast color * fix notification settings, password dialog * app-create, user-create layout * domain policy * login-policy, project grid loader, i18n * login policy * login policy save lifetimes * private labeling optim * granted project grantId * smtp address matching * i18n * i18n * i18n * replace url strategy * fix privatelabeling color picker saving * stylelint Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
parent
8d0cf9f368
commit
8baf0fe08c
@ -47,9 +47,9 @@
|
||||
width: 32px;
|
||||
line-height: 1rem;
|
||||
border-radius: 50%;
|
||||
-webkit-box-shadow: 2px 0 7px -1px rgba(33, 34, 36, 0.5);
|
||||
-moz-box-shadow: 2px 0 7px -1px rgba(33, 34, 36, 0.5);
|
||||
box-shadow: 2px 0 7px -1px rgba(33, 34, 36, 0.5);
|
||||
-webkit-box-shadow: 1px 0 3px -1px rgba(33, 34, 36, 0.5);
|
||||
-moz-box-shadow: 1px 0 3px -1px rgba(33, 34, 36, 0.5);
|
||||
box-shadow: 1px 0 3px -1px rgba(33, 34, 36, 0.5);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -1,8 +1,12 @@
|
||||
<cnsl-filter (resetted)="resetFilter()" (trigger)="emitFilter()">
|
||||
<cnsl-filter (resetted)="resetFilter()" (trigger)="emitFilter()" [queryCount]="searchQueries.length">
|
||||
<div class="filter-row" id="filtercomp">
|
||||
<div class="email-query">
|
||||
<mat-checkbox id="name" class="cb" [checked]="getSubFilter(SubQuery.NAME)"
|
||||
(change)="changeCheckbox(SubQuery.NAME, $event )">{{'FILTER.ORGNAME' | translate}}
|
||||
<mat-checkbox
|
||||
id="name"
|
||||
class="cb"
|
||||
[checked]="getSubFilter(SubQuery.NAME)"
|
||||
(change)="changeCheckbox(SubQuery.NAME, $event)"
|
||||
>{{ 'FILTER.ORGNAME' | translate }}
|
||||
</mat-checkbox>
|
||||
<div class="subquery" *ngIf="getSubFilter(SubQuery.NAME) as eq">
|
||||
<cnsl-form-field class="filter-select-method">
|
||||
|
@ -50,7 +50,7 @@ export class FilterOrgComponent extends FilterComponent implements OnInit {
|
||||
});
|
||||
|
||||
this.searchQueries = orgQueries.filter((q) => q !== undefined) as OrgQuery[];
|
||||
this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
this.filterChanged.emit(this.searchQueries ? this.searchQueries : undefined);
|
||||
// this.showFilter = true;
|
||||
// this.filterOpen.emit(true);
|
||||
}
|
||||
@ -88,11 +88,9 @@ export class FilterOrgComponent extends FilterComponent implements OnInit {
|
||||
switch (subquery) {
|
||||
case SubQuery.NAME:
|
||||
(query as OrgNameQuery).setName(value);
|
||||
this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
|
||||
break;
|
||||
}
|
||||
|
||||
this.filterCount$.next(this.filterCount);
|
||||
}
|
||||
|
||||
public getSubFilter(subquery: SubQuery): any {
|
||||
@ -109,23 +107,17 @@ export class FilterOrgComponent extends FilterComponent implements OnInit {
|
||||
|
||||
public setMethod(query: any, event: any) {
|
||||
(query as UserNameQuery).setMethod(event.value);
|
||||
this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
|
||||
}
|
||||
|
||||
public emitFilter(): void {
|
||||
this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
|
||||
this.showFilter = false;
|
||||
this.filterOpen.emit(false);
|
||||
|
||||
this.filterCount$.next(this.filterCount);
|
||||
}
|
||||
|
||||
public resetFilter(): void {
|
||||
this.searchQueries = [];
|
||||
this.emitFilter();
|
||||
}
|
||||
|
||||
public get filterCount(): number {
|
||||
return this.searchQueries.length;
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,12 @@
|
||||
<cnsl-filter (resetted)="resetFilter()" (trigger)="emitFilter()">
|
||||
<cnsl-filter (resetted)="resetFilter()" (trigger)="emitFilter()" [queryCount]="searchQueries.length">
|
||||
<div class="filter-row" id="filtercomp">
|
||||
|
||||
<!-- <div class="name-query">
|
||||
<mat-checkbox id="resourceowner" class="cb" [checked]="getSubFilter(SubQuery.RESOURCEOWNER)"
|
||||
(change)="changeCheckbox(SubQuery.RESOURCEOWNER, $event )">{{'FILTER.RESOURCEOWNER' | translate}}
|
||||
</mat-checkbox>
|
||||
<div class="subquery" *ngIf="getSubFilter(SubQuery.RESOURCEOWNER) as ronq">
|
||||
<span class="nomethod cnsl-secondary-text">
|
||||
{{ 'FILTER.METHODS.1' | translate }}
|
||||
</span>
|
||||
|
||||
<cnsl-form-field class="filter-input-value">
|
||||
<input cnslInput name="value" [value]="ronq.getResourceOwner()"
|
||||
(change)="setValue(SubQuery.RESOURCEOWNER, ronq, $event)" />
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="email-query">
|
||||
<mat-checkbox id="name" class="cb" [checked]="getSubFilter(SubQuery.NAME)"
|
||||
(change)="changeCheckbox(SubQuery.NAME, $event )">{{'FILTER.PROJECTNAME' | translate}}
|
||||
<mat-checkbox
|
||||
id="name"
|
||||
class="cb"
|
||||
[checked]="getSubFilter(SubQuery.NAME)"
|
||||
(change)="changeCheckbox(SubQuery.NAME, $event)"
|
||||
>{{ 'FILTER.PROJECTNAME' | translate }}
|
||||
</mat-checkbox>
|
||||
<div class="subquery" *ngIf="getSubFilter(SubQuery.NAME) as eq">
|
||||
<cnsl-form-field class="filter-select-method">
|
||||
|
@ -50,7 +50,7 @@ export class FilterProjectComponent extends FilterComponent implements OnInit {
|
||||
});
|
||||
|
||||
this.searchQueries = projectQueries.filter((q) => q !== undefined) as ProjectQuery[];
|
||||
this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
|
||||
// this.showFilter = true;
|
||||
// this.filterOpen.emit(true);
|
||||
}
|
||||
@ -60,15 +60,6 @@ export class FilterProjectComponent extends FilterComponent implements OnInit {
|
||||
public changeCheckbox(subquery: SubQuery, event: MatCheckboxChange) {
|
||||
if (event.checked) {
|
||||
switch (subquery) {
|
||||
// case SubQuery.RESOURCEOWNER:
|
||||
// const ronq = new ProjectResourceOwnerQuery();
|
||||
// ronq.setResourceOwner('');
|
||||
|
||||
// const ro_sq = new ProjectQuery();
|
||||
// ro_sq.setProjectResourceOwnerQuery(ronq);
|
||||
|
||||
// this.searchQueries.push(ro_sq);
|
||||
// break;
|
||||
case SubQuery.NAME:
|
||||
const nq = new ProjectNameQuery();
|
||||
nq.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE);
|
||||
@ -82,14 +73,6 @@ export class FilterProjectComponent extends FilterComponent implements OnInit {
|
||||
}
|
||||
} else {
|
||||
switch (subquery) {
|
||||
// case SubQuery.RESOURCEOWNER:
|
||||
// const index_s = this.searchQueries.findIndex(
|
||||
// (q) => (q as ProjectQuery).toObject().projectResourceOwnerQuery !== undefined,
|
||||
// );
|
||||
// if (index_s > -1) {
|
||||
// this.searchQueries.splice(index_s, 1);
|
||||
// }
|
||||
// break;
|
||||
case SubQuery.NAME:
|
||||
const index_dn = this.searchQueries.findIndex((q) => (q as ProjectQuery).toObject().nameQuery !== undefined);
|
||||
if (index_dn > -1) {
|
||||
@ -103,28 +86,15 @@ export class FilterProjectComponent extends FilterComponent implements OnInit {
|
||||
public setValue(subquery: SubQuery, query: any, event: any) {
|
||||
const value = event?.target?.value ?? event.value;
|
||||
switch (subquery) {
|
||||
// case SubQuery.RESOURCEOWNER:
|
||||
// (query as ProjectResourceOwnerQuery).setResourceOwner(value);
|
||||
// this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
// break;
|
||||
case SubQuery.NAME:
|
||||
(query as ProjectNameQuery).setName(value);
|
||||
this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
|
||||
break;
|
||||
}
|
||||
|
||||
this.filterCount$.next(this.filterCount);
|
||||
}
|
||||
|
||||
public getSubFilter(subquery: SubQuery): any {
|
||||
switch (subquery) {
|
||||
// case SubQuery.RESOURCEOWNER:
|
||||
// const s = this.searchQueries.find((q) => (q as ProjectQuery).toObject().projectResourceOwnerQuery !== undefined);
|
||||
// if (s) {
|
||||
// return (s as ProjectQuery).getProjectResourceOwnerQuery();
|
||||
// } else {
|
||||
// return undefined;
|
||||
// }
|
||||
case SubQuery.NAME:
|
||||
const dn = this.searchQueries.find((q) => (q as ProjectQuery).toObject().nameQuery !== undefined);
|
||||
if (dn) {
|
||||
@ -137,15 +107,13 @@ export class FilterProjectComponent extends FilterComponent implements OnInit {
|
||||
|
||||
public setMethod(query: any, event: any) {
|
||||
(query as UserNameQuery).setMethod(event.value);
|
||||
this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
|
||||
}
|
||||
|
||||
public emitFilter(): void {
|
||||
this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
|
||||
this.showFilter = false;
|
||||
this.filterOpen.emit(false);
|
||||
|
||||
this.filterCount$.next(this.filterCount);
|
||||
}
|
||||
|
||||
public resetFilter(): void {
|
||||
@ -153,7 +121,7 @@ export class FilterProjectComponent extends FilterComponent implements OnInit {
|
||||
this.emitFilter();
|
||||
}
|
||||
|
||||
public get filterCount(): number {
|
||||
public get filterCounter(): number {
|
||||
return this.searchQueries.length;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
<cnsl-filter (resetted)="resetFilter()" (trigger)="emitFilter()">
|
||||
<cnsl-filter (resetted)="resetFilter()" (trigger)="emitFilter()" [queryCount]="searchQueries.length">
|
||||
<div class="filter-row" id="filtercomp">
|
||||
<div class="name-query">
|
||||
<mat-checkbox
|
||||
|
@ -82,7 +82,7 @@ export class FilterUserGrantsComponent extends FilterComponent implements OnInit
|
||||
});
|
||||
|
||||
this.searchQueries = userQueries.filter((q) => q !== undefined) as UserGrantQuery[];
|
||||
this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
|
||||
// this.showFilter = true;
|
||||
// this.filterOpen.emit(true);
|
||||
}
|
||||
@ -170,23 +170,21 @@ export class FilterUserGrantsComponent extends FilterComponent implements OnInit
|
||||
switch (subquery) {
|
||||
case SubQuery.DISPLAYNAME:
|
||||
(query as DisplayNameQuery).setDisplayName(event?.target?.value);
|
||||
this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
|
||||
break;
|
||||
case SubQuery.USERNAME:
|
||||
(query as UserNameQuery).setUserName(event?.target?.value);
|
||||
this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
|
||||
break;
|
||||
case SubQuery.ORGNAME:
|
||||
(query as UserGrantOrgNameQuery).setOrgName(event?.target?.value);
|
||||
this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
|
||||
break;
|
||||
case SubQuery.PROJECTNAME:
|
||||
(query as UserGrantProjectNameQuery).setProjectName(event?.target?.value);
|
||||
this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
|
||||
break;
|
||||
}
|
||||
|
||||
this.filterCount$.next(this.filterCount);
|
||||
}
|
||||
|
||||
public getSubFilter(subquery: SubQuery): any {
|
||||
@ -225,23 +223,17 @@ export class FilterUserGrantsComponent extends FilterComponent implements OnInit
|
||||
|
||||
public setMethod(query: any, event: any) {
|
||||
(query as UserNameQuery).setMethod(event.value);
|
||||
this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
|
||||
}
|
||||
|
||||
public emitFilter(): void {
|
||||
this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
|
||||
this.showFilter = false;
|
||||
this.filterOpen.emit(false);
|
||||
|
||||
this.filterCount$.next(this.filterCount);
|
||||
}
|
||||
|
||||
public resetFilter(): void {
|
||||
this.searchQueries = [];
|
||||
this.emitFilter();
|
||||
}
|
||||
|
||||
public get filterCount(): number {
|
||||
return this.searchQueries.length;
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,12 @@
|
||||
<cnsl-filter (resetted)="resetFilter()" (trigger)="emitFilter()">
|
||||
<cnsl-filter (resetted)="resetFilter()" (trigger)="emitFilter()" [queryCount]="searchQueries.length">
|
||||
<div class="filter-row" id="filtercomp">
|
||||
<div class="name-query">
|
||||
<mat-checkbox id="state" class="cb" [checked]="getSubFilter(SubQuery.STATE)"
|
||||
(change)="changeCheckbox(SubQuery.STATE, $event )">{{'FILTER.STATE' | translate}}
|
||||
<mat-checkbox
|
||||
id="state"
|
||||
class="cb"
|
||||
[checked]="getSubFilter(SubQuery.STATE)"
|
||||
(change)="changeCheckbox(SubQuery.STATE, $event)"
|
||||
>{{ 'FILTER.STATE' | translate }}
|
||||
</mat-checkbox>
|
||||
<div class="subquery" *ngIf="getSubFilter(SubQuery.STATE) as sq">
|
||||
<span class="nomethod cnsl-secondary-text">
|
||||
@ -20,8 +24,12 @@
|
||||
</div>
|
||||
|
||||
<div class="name-query">
|
||||
<mat-checkbox id="displayname" class="cb" [checked]="getSubFilter(SubQuery.DISPLAYNAME)"
|
||||
(change)="changeCheckbox(SubQuery.DISPLAYNAME, $event )">{{'FILTER.DISPLAYNAME' | translate}}
|
||||
<mat-checkbox
|
||||
id="displayname"
|
||||
class="cb"
|
||||
[checked]="getSubFilter(SubQuery.DISPLAYNAME)"
|
||||
(change)="changeCheckbox(SubQuery.DISPLAYNAME, $event)"
|
||||
>{{ 'FILTER.DISPLAYNAME' | translate }}
|
||||
</mat-checkbox>
|
||||
<div class="subquery" *ngIf="getSubFilter(SubQuery.DISPLAYNAME) as dnq">
|
||||
<cnsl-form-field class="filter-select-method" appearance="outline">
|
||||
@ -33,15 +41,23 @@
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="filter-input-value" appearance="outline">
|
||||
<input cnslInput name="value" [value]="dnq.getDisplayName()"
|
||||
(change)="setValue(SubQuery.DISPLAYNAME, dnq, $event)" />
|
||||
<input
|
||||
cnslInput
|
||||
name="value"
|
||||
[value]="dnq.getDisplayName()"
|
||||
(change)="setValue(SubQuery.DISPLAYNAME, dnq, $event)"
|
||||
/>
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="email-query">
|
||||
<mat-checkbox id="email" class="cb" [checked]="getSubFilter(SubQuery.EMAIL)"
|
||||
(change)="changeCheckbox(SubQuery.EMAIL, $event )">{{'FILTER.EMAIL' | translate}}
|
||||
<mat-checkbox
|
||||
id="email"
|
||||
class="cb"
|
||||
[checked]="getSubFilter(SubQuery.EMAIL)"
|
||||
(change)="changeCheckbox(SubQuery.EMAIL, $event)"
|
||||
>{{ 'FILTER.EMAIL' | translate }}
|
||||
</mat-checkbox>
|
||||
<div class="subquery" *ngIf="getSubFilter(SubQuery.EMAIL) as eq">
|
||||
<cnsl-form-field class="filter-select-method" appearance="outline">
|
||||
@ -53,15 +69,18 @@
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="filter-input-value" appearance="outline">
|
||||
<input cnslInput name="value" [value]="eq.getEmailAddress()"
|
||||
(change)="setValue(SubQuery.EMAIL, eq, $event)" />
|
||||
<input cnslInput name="value" [value]="eq.getEmailAddress()" (change)="setValue(SubQuery.EMAIL, eq, $event)" />
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="usernane-query">
|
||||
<mat-checkbox id="username" class="cb" [checked]="getSubFilter(SubQuery.USERNAME)"
|
||||
(change)="changeCheckbox(SubQuery.USERNAME, $event )">{{'FILTER.USERNAME' | translate}}
|
||||
<mat-checkbox
|
||||
id="username"
|
||||
class="cb"
|
||||
[checked]="getSubFilter(SubQuery.USERNAME)"
|
||||
(change)="changeCheckbox(SubQuery.USERNAME, $event)"
|
||||
>{{ 'FILTER.USERNAME' | translate }}
|
||||
</mat-checkbox>
|
||||
<div class="subquery" *ngIf="getSubFilter(SubQuery.USERNAME) as unq">
|
||||
<cnsl-form-field class="filter-select-method" appearance="outline">
|
||||
@ -73,8 +92,7 @@
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="filter-input-value" appearance="outline">
|
||||
<input cnslInput name="value" [value]="unq.getUserName()"
|
||||
(change)="setValue(SubQuery.USERNAME, unq, $event)" />
|
||||
<input cnslInput name="value" [value]="unq.getUserName()" (change)="setValue(SubQuery.USERNAME, unq, $event)" />
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -91,7 +91,7 @@ export class FilterUserComponent extends FilterComponent implements OnInit {
|
||||
});
|
||||
|
||||
this.searchQueries = userQueries.filter((q) => q !== undefined) as UserSearchQuery[];
|
||||
this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
|
||||
// this.showFilter = true;
|
||||
// this.filterOpen.emit(true);
|
||||
}
|
||||
@ -181,23 +181,21 @@ export class FilterUserComponent extends FilterComponent implements OnInit {
|
||||
switch (subquery) {
|
||||
case SubQuery.STATE:
|
||||
(query as StateQuery).setState(value);
|
||||
this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
|
||||
break;
|
||||
case SubQuery.DISPLAYNAME:
|
||||
(query as DisplayNameQuery).setDisplayName(value);
|
||||
this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
|
||||
break;
|
||||
case SubQuery.EMAIL:
|
||||
(query as EmailQuery).setEmailAddress(value);
|
||||
this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
|
||||
break;
|
||||
case SubQuery.USERNAME:
|
||||
(query as UserNameQuery).setUserName(value);
|
||||
this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
|
||||
break;
|
||||
}
|
||||
|
||||
this.filterCount$.next(this.filterCount);
|
||||
}
|
||||
|
||||
public getSubFilter(subquery: SubQuery): any {
|
||||
@ -235,23 +233,17 @@ export class FilterUserComponent extends FilterComponent implements OnInit {
|
||||
|
||||
public setMethod(query: any, event: any) {
|
||||
(query as UserNameQuery).setMethod(event.value);
|
||||
this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
|
||||
}
|
||||
|
||||
public emitFilter(): void {
|
||||
this.filterChanged.emit(this.filterCount ? this.searchQueries : undefined);
|
||||
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
|
||||
this.showFilter = false;
|
||||
this.filterOpen.emit(false);
|
||||
|
||||
this.filterCount$.next(this.filterCount);
|
||||
}
|
||||
|
||||
public resetFilter(): void {
|
||||
this.searchQueries = [];
|
||||
this.emitFilter();
|
||||
}
|
||||
|
||||
public get filterCount(): number {
|
||||
return this.searchQueries.length;
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,33 @@
|
||||
<div class="cnsl-filter-button-wrapper">
|
||||
<button mat-stroked-button class="filter-toggle cnsl-action-button" (click)="toggleFilter()" cdkOverlayOrigin
|
||||
#trigger="cdkOverlayOrigin">
|
||||
<button
|
||||
mat-stroked-button
|
||||
class="filter-toggle cnsl-action-button"
|
||||
(click)="toggleFilter()"
|
||||
cdkOverlayOrigin
|
||||
#trigger="cdkOverlayOrigin"
|
||||
>
|
||||
<i class="las la-filter no-margin"></i>
|
||||
<span>{{ 'ACTIONS.FILTER' | translate }}</span>
|
||||
<span *ngIf="(filterCount$ | async)" class="filter-count">{{filterCount$ | async}}</span>
|
||||
<span *ngIf="queryCount" class="filter-count">{{ queryCount }}</span>
|
||||
<cnsl-action-keys [doNotUseContrast]="true" [type]="ActionKeysType.FILTER" (actionTriggered)="toggleFilter()">
|
||||
</cnsl-action-keys>
|
||||
</button>
|
||||
<ng-template cdkConnectedOverlay [cdkConnectedOverlayHasBackdrop]="true" [flexibleDimensions]="true"
|
||||
[lockPosition]="true" [cdkConnectedOverlayOffsetY]="10" [cdkConnectedOverlayPositions]="positions"
|
||||
[cdkConnectedOverlayOrigin]="trigger" [cdkConnectedOverlayOpen]="showFilter"
|
||||
cdkConnectedOverlayBackdropClass="transparent-backdrop" (backdropClick)="showFilter= false"
|
||||
(detach)="showFilter = false">
|
||||
<ng-template
|
||||
cdkConnectedOverlay
|
||||
[cdkConnectedOverlayHasBackdrop]="true"
|
||||
[flexibleDimensions]="true"
|
||||
[lockPosition]="true"
|
||||
[cdkConnectedOverlayOffsetY]="10"
|
||||
[cdkConnectedOverlayPositions]="positions"
|
||||
[cdkConnectedOverlayOrigin]="trigger"
|
||||
[cdkConnectedOverlayOpen]="showFilter"
|
||||
cdkConnectedOverlayBackdropClass="transparent-backdrop"
|
||||
(backdropClick)="showFilter = false"
|
||||
(detach)="showFilter = false"
|
||||
>
|
||||
<div class="cnsl-filter-wrapper" cdkTrapFocus>
|
||||
<div class="filter-top">
|
||||
<button mat-stroked-button (click)="resetted.emit()">{{'ACTIONS.RESET' |
|
||||
translate}}</button>
|
||||
<button mat-stroked-button (click)="resetted.emit()">{{ 'ACTIONS.RESET' | translate }}</button>
|
||||
<span class="filter-middle">{{ 'FILTER.TITLE' | translate }}</span>
|
||||
<button mat-raised-button color="primary" (click)="emitFilter()">{{ 'ACTIONS.FINISH' | translate }}</button>
|
||||
</div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ConnectedPosition, ConnectionPositionPair } from '@angular/cdk/overlay';
|
||||
import { Component, EventEmitter, OnDestroy, Output } from '@angular/core';
|
||||
import { Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BehaviorSubject, Subject, takeUntil } from 'rxjs';
|
||||
import { Observable, Subject, takeUntil } from 'rxjs';
|
||||
import { SearchQuery as MemberSearchQuery } from 'src/app/proto/generated/zitadel/member_pb';
|
||||
import { TextQueryMethod } from 'src/app/proto/generated/zitadel/object_pb';
|
||||
import { OrgQuery } from 'src/app/proto/generated/zitadel/org_pb';
|
||||
@ -24,15 +24,16 @@ type FilterSearchQueryAsObject =
|
||||
styleUrls: ['./filter.component.scss'],
|
||||
})
|
||||
export class FilterComponent implements OnDestroy {
|
||||
@Output() public filterChanged: EventEmitter<FilterSearchQuery[] | undefined> = new EventEmitter();
|
||||
@Output() public filterChanged: EventEmitter<FilterSearchQuery[]> = new EventEmitter();
|
||||
@Output() public filterOpen: EventEmitter<boolean> = new EventEmitter<boolean>(false);
|
||||
|
||||
@Output() public resetted: EventEmitter<void> = new EventEmitter();
|
||||
@Output() public trigger: EventEmitter<void> = new EventEmitter();
|
||||
|
||||
private destroy$: Subject<void> = new Subject();
|
||||
@Input() public queryCount: number = 0;
|
||||
|
||||
public filterCount$: BehaviorSubject<number> = new BehaviorSubject(0);
|
||||
private destroy$: Subject<void> = new Subject();
|
||||
public filterChanged$: Observable<FilterSearchQuery[]> = this.filterChanged.asObservable();
|
||||
|
||||
public showFilter: boolean = false;
|
||||
public methods: TextQueryMethod[] = [
|
||||
@ -59,7 +60,6 @@ export class FilterComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.filterCount$.complete();
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
@ -83,6 +83,7 @@ export class FilterComponent implements OnDestroy {
|
||||
queryParams: {
|
||||
['filter']: JSON.stringify(filters),
|
||||
},
|
||||
replaceUrl: true,
|
||||
queryParamsHandling: 'merge',
|
||||
skipLocationChange: false,
|
||||
});
|
||||
|
@ -68,9 +68,16 @@ export class HeaderComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
public get isOnInstance(): boolean {
|
||||
return (
|
||||
['/instance', '/views', '/orgs', '/settings', '/failed-events', '/instance/members'].includes(this.router.url) ||
|
||||
this.router.url.includes('/settings')
|
||||
);
|
||||
const pages: string[] = [
|
||||
'/instance',
|
||||
'/settings',
|
||||
'/views',
|
||||
'/orgs',
|
||||
'/settings',
|
||||
'/failed-events',
|
||||
'/instance/members',
|
||||
];
|
||||
|
||||
return pages.findIndex((p) => this.router.url.includes(p)) > -1;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,11 @@
|
||||
<cnsl-refresh-table *ngIf="dataSource" (refreshed)="changePage()" [dataSize]="dataSource.totalResult"
|
||||
[timestamp]="dataSource.viewTimestamp" [selection]="selection" [loading]="dataSource?.loading$ | async">
|
||||
|
||||
<cnsl-refresh-table
|
||||
*ngIf="dataSource"
|
||||
(refreshed)="changePage()"
|
||||
[dataSize]="dataSource.totalResult"
|
||||
[timestamp]="dataSource.viewTimestamp"
|
||||
[selection]="selection"
|
||||
[loading]="dataSource?.loading$ | async"
|
||||
>
|
||||
<ng-container actions *ngIf="selection.hasValue()">
|
||||
<ng-content select="[selectactions]"></ng-content>
|
||||
</ng-container>
|
||||
@ -13,17 +18,32 @@
|
||||
<table mat-table class="table" aria-label="Elements" [dataSource]="dataSource">
|
||||
<ng-container matColumnDef="select">
|
||||
<th class="selection" mat-header-cell *matHeaderCellDef>
|
||||
<mat-checkbox [disabled]="!canWrite" color="primary" (change)="$event ? masterToggle() : null"
|
||||
<mat-checkbox
|
||||
[disabled]="!canWrite"
|
||||
color="primary"
|
||||
(change)="$event ? masterToggle() : null"
|
||||
[checked]="selection.hasValue() && isAllSelected()"
|
||||
[indeterminate]="selection.hasValue() && !isAllSelected()">
|
||||
[indeterminate]="selection.hasValue() && !isAllSelected()"
|
||||
>
|
||||
</mat-checkbox>
|
||||
</th>
|
||||
<td class="selection" mat-cell *matCellDef="let row">
|
||||
<mat-checkbox [disabled]="!canWrite" color="primary" (click)="$event.stopPropagation()"
|
||||
(change)="$event ? selection.toggle(row) : null" [checked]="selection.isSelected(row)">
|
||||
<cnsl-avatar *ngIf="row?.displayName && row.firstName && row.lastName; else cog" class="avatar"
|
||||
[name]="row.displayName" [avatarUrl]="row.avatarUrl || ''" [avatarUrl]="row.avatarUrl|| ''"
|
||||
[forColor]="row?.preferredLoginName" [size]="32">
|
||||
<mat-checkbox
|
||||
[disabled]="!canWrite"
|
||||
color="primary"
|
||||
(click)="$event.stopPropagation()"
|
||||
(change)="$event ? selection.toggle(row) : null"
|
||||
[checked]="selection.isSelected(row)"
|
||||
>
|
||||
<cnsl-avatar
|
||||
*ngIf="row?.displayName && row.firstName && row.lastName; else cog"
|
||||
class="avatar"
|
||||
[name]="row.displayName"
|
||||
[avatarUrl]="row.avatarUrl || ''"
|
||||
[avatarUrl]="row.avatarUrl || ''"
|
||||
[forColor]="row?.preferredLoginName"
|
||||
[size]="32"
|
||||
>
|
||||
</cnsl-avatar>
|
||||
<ng-template #cog>
|
||||
<cnsl-avatar [forColor]="row.preferredLoginName" [isMachine]="true">
|
||||
@ -37,19 +57,22 @@
|
||||
<ng-container matColumnDef="userId">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'PROJECT.MEMBER.USERID' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let member">
|
||||
{{member.userId}} </td>
|
||||
{{ member.userId }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="displayName">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'PROJECT.MEMBER.DISPLAYNAME' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let member">
|
||||
{{member.displayName}} </td>
|
||||
{{ member.displayName }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="loginname">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'PROJECT.MEMBER.LOGINNAME' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let member">
|
||||
{{member.preferredLoginName}} </td>
|
||||
{{ member.preferredLoginName }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="email">
|
||||
@ -63,11 +86,18 @@
|
||||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let member" class="member-action-tr">
|
||||
<cnsl-table-actions [hasActions]="true">
|
||||
<button actions matTooltip="{{'ACTIONS.REMOVE' | translate}}" color="warn"
|
||||
(click)="triggerDeleteMember(member)" mat-icon-button><i class="las la-trash"></i></button>
|
||||
<button
|
||||
actions
|
||||
matTooltip="{{ 'ACTIONS.REMOVE' | translate }}"
|
||||
color="warn"
|
||||
(click)="$event.stopPropagation(); triggerDeleteMember(member)"
|
||||
mat-icon-button
|
||||
>
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
|
||||
<button menuActions mat-menu-item [routerLink]="['/users', member.userId]">
|
||||
{{'ACTIONS.TABLE.SHOWUSER' | translate : ({value: member.displayName})}}
|
||||
{{ 'ACTIONS.TABLE.SHOWUSER' | translate: { value: member.displayName } }}
|
||||
</button>
|
||||
</cnsl-table-actions>
|
||||
</td>
|
||||
@ -77,8 +107,13 @@
|
||||
<th mat-header-cell *matHeaderCellDef class="role-row">{{ 'ROLESLABEL' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let member" class="role-row">
|
||||
<mat-chip-list class="cnsl-chip-list" aria-label="role selection">
|
||||
<mat-chip class="cnsl-chip" *ngFor="let role of member.rolesList" [removable]="true" [selectable]="false"
|
||||
(removed)="removeRole(member, role)">
|
||||
<mat-chip
|
||||
class="cnsl-chip"
|
||||
*ngFor="let role of member.rolesList"
|
||||
[removable]="true"
|
||||
[selectable]="false"
|
||||
(removed)="removeRole(member, role)"
|
||||
>
|
||||
<div class="cnsl-chip-dot" [style.background]="getColor(role)"></div>
|
||||
<span>{{ role | roletransform }}</span>
|
||||
<button matChipRemove>
|
||||
@ -90,13 +125,23 @@
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr class="highlight pointer" (click)="addRole(member)" mat-row
|
||||
*matRowDef="let member; columns: displayedColumns;">
|
||||
</tr>
|
||||
<tr
|
||||
class="highlight pointer"
|
||||
(click)="addRole(member)"
|
||||
mat-row
|
||||
*matRowDef="let member; columns: displayedColumns"
|
||||
></tr>
|
||||
</table>
|
||||
</div>
|
||||
<cnsl-paginator *ngIf="dataSource" class="paginator" #paginator [timestamp]="dataSource?.viewTimestamp"
|
||||
[pageSize]="INITIALPAGESIZE" [length]="dataSource.totalResult" [pageSizeOptions]="[25, 50, 100, 250]"
|
||||
(page)="changePage($event)">
|
||||
<cnsl-paginator
|
||||
*ngIf="dataSource"
|
||||
class="paginator"
|
||||
#paginator
|
||||
[timestamp]="dataSource?.viewTimestamp"
|
||||
[pageSize]="INITIALPAGESIZE"
|
||||
[length]="dataSource.totalResult"
|
||||
[pageSizeOptions]="[25, 50, 100, 250]"
|
||||
(page)="changePage($event)"
|
||||
>
|
||||
</cnsl-paginator>
|
||||
</cnsl-refresh-table>
|
@ -15,7 +15,14 @@
|
||||
</a>
|
||||
</ng-template>
|
||||
|
||||
<table [dataSource]="dataSource" mat-table class="table" aria-label="Elements">
|
||||
<table
|
||||
[dataSource]="dataSource"
|
||||
mat-table
|
||||
class="table"
|
||||
aria-label="Organizations"
|
||||
matSort
|
||||
(matSortChange)="sortChange($event)"
|
||||
>
|
||||
<ng-container matColumnDef="select">
|
||||
<th class="selection" mat-header-cell *matHeaderCellDef>
|
||||
{{ 'ORG.PAGES.ACTIVE' | translate }}
|
||||
@ -51,6 +58,7 @@
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="name">
|
||||
<!-- mat-sort-header -->
|
||||
<th mat-header-cell *matHeaderCellDef>
|
||||
{{ 'ORG.PAGES.NAME' | translate }}
|
||||
</th>
|
||||
|
@ -1,9 +1,11 @@
|
||||
import { LiveAnnouncer } from '@angular/cdk/a11y';
|
||||
import { Component, Input, ViewChild } from '@angular/core';
|
||||
import { MatSort, Sort } from '@angular/material/sort';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
|
||||
import { BehaviorSubject, catchError, finalize, from, map, Observable, of, Subject, switchMap, takeUntil } from 'rxjs';
|
||||
import { Org, OrgQuery, OrgState } from 'src/app/proto/generated/zitadel/org_pb';
|
||||
import { Org, OrgFieldName, OrgQuery, OrgState } from 'src/app/proto/generated/zitadel/org_pb';
|
||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@ -38,6 +40,7 @@ export class OrgTableComponent {
|
||||
public filterOpen: boolean = false;
|
||||
public OrgState: any = OrgState;
|
||||
public copied: string = '';
|
||||
@ViewChild(MatSort) public sort!: MatSort;
|
||||
|
||||
private searchQueries: OrgQuery[] = [];
|
||||
private destroy$: Subject<void> = new Subject();
|
||||
@ -48,7 +51,12 @@ export class OrgTableComponent {
|
||||
});
|
||||
private requestOrgsObservable$ = this.requestOrgs$.pipe(takeUntil(this.destroy$));
|
||||
|
||||
constructor(private authService: GrpcAuthService, private router: Router, private toast: ToastService) {
|
||||
constructor(
|
||||
private authService: GrpcAuthService,
|
||||
private router: Router,
|
||||
private toast: ToastService,
|
||||
private _liveAnnouncer: LiveAnnouncer,
|
||||
) {
|
||||
this.requestOrgs$.next({ limit: this.initialLimit, offset: 0, queries: this.searchQueries });
|
||||
this.authService.getActiveOrg().then((org) => (this.activeOrg = org));
|
||||
|
||||
@ -60,7 +68,17 @@ export class OrgTableComponent {
|
||||
public loadOrgs(request: Request): Observable<Org.AsObject[]> {
|
||||
this.loadingSubject.next(true);
|
||||
|
||||
return from(this.authService.listMyProjectOrgs(request.limit, request.offset, request.queries)).pipe(
|
||||
let sortingField: OrgFieldName | undefined = undefined;
|
||||
if (this.sort?.active && this.sort?.direction)
|
||||
switch (this.sort.active) {
|
||||
case 'name':
|
||||
sortingField = OrgFieldName.ORG_FIELD_NAME_NAME;
|
||||
break;
|
||||
}
|
||||
|
||||
return from(
|
||||
this.authService.listMyProjectOrgs(request.limit, request.offset, request.queries, sortingField, this.sort?.direction),
|
||||
).pipe(
|
||||
map((resp) => {
|
||||
this.timestamp = resp.details?.viewTimestamp;
|
||||
this.totalResult = resp.details?.totalResult ?? 0;
|
||||
@ -86,6 +104,15 @@ export class OrgTableComponent {
|
||||
});
|
||||
}
|
||||
|
||||
public sortChange(sortState: Sort) {
|
||||
if (sortState.direction && sortState.active) {
|
||||
this._liveAnnouncer.announce(`Sorted ${sortState.direction}ending`);
|
||||
this.refresh();
|
||||
} else {
|
||||
this._liveAnnouncer.announce('Sorting cleared');
|
||||
}
|
||||
}
|
||||
|
||||
public applySearchQuery(searchQueries: OrgQuery[]): void {
|
||||
this.searchQueries = searchQueries;
|
||||
this.requestOrgs$.next({
|
||||
|
@ -0,0 +1,52 @@
|
||||
<div class="spinner-wr">
|
||||
<mat-spinner diameter="30" *ngIf="loading" color="primary"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<h2>{{ 'POLICY.DOMAIN_POLICY.TITLE' | translate }}</h2>
|
||||
<cnsl-info-section *ngIf="isDefault"> {{ 'POLICY.DEFAULTLABEL' | translate }}</cnsl-info-section>
|
||||
|
||||
<!-- <ng-template cnslHasRole [hasRole]="['domain.policy.delete']"> -->
|
||||
<button
|
||||
*ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
|
||||
matTooltip="{{ 'POLICY.RESET' | translate }}"
|
||||
color="warn"
|
||||
(click)="removePolicy()"
|
||||
mat-stroked-button
|
||||
>
|
||||
{{ 'POLICY.RESET' | translate }}
|
||||
</button>
|
||||
<!-- </ng-template> -->
|
||||
|
||||
<!-- [disabled]="(['domain.policy.write'] | hasRole | async) === false" -->
|
||||
<div class="content" *ngIf="domainData">
|
||||
<div class="row">
|
||||
<mat-checkbox color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="domainData.userLoginMustBeDomain">
|
||||
{{ 'POLICY.DATA.USERLOGINMUSTBEDOMAIN' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
<!-- [disabled]="(['domain.policy.write'] | hasRole | async) === false" -->
|
||||
<div class="row">
|
||||
<mat-checkbox color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="domainData.validateOrgDomains">
|
||||
{{ 'POLICY.DATA.VALIDATEORGDOMAINS' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<mat-checkbox
|
||||
color="primary"
|
||||
name="hasNumber"
|
||||
ngDefaultControl
|
||||
[(ngModel)]="domainData.smtpSenderAddressMatchesInstanceDomain"
|
||||
>
|
||||
{{ 'POLICY.DATA.SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- [disabled]="(['domain.policy.write'] | hasRole | async) === false" -->
|
||||
<div class="btn-container">
|
||||
<button (click)="savePolicy()" color="primary" type="submit" mat-raised-button>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { DomainPolicyComponent } from './domain-policy.component';
|
||||
|
||||
describe('DomainPolicyComponent', () => {
|
||||
let component: DomainPolicyComponent;
|
||||
let fixture: ComponentFixture<DomainPolicyComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [DomainPolicyComponent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DomainPolicyComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,32 +1,43 @@
|
||||
import { Component, Injector, Input, OnDestroy, OnInit, Type } from '@angular/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { GetCustomOrgIAMPolicyResponse } from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import {
|
||||
AddCustomDomainPolicyRequest,
|
||||
GetCustomOrgIAMPolicyResponse,
|
||||
UpdateDomainPolicyRequest,
|
||||
} from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import { GetOrgIAMPolicyResponse } from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { Org } from 'src/app/proto/generated/zitadel/org_pb';
|
||||
import { OrgIAMPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
|
||||
import { DomainPolicy, OrgIAMPolicy } 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 { StorageLocation, StorageService } from 'src/app/services/storage.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-org-iam-policy',
|
||||
templateUrl: './org-iam-policy.component.html',
|
||||
styleUrls: ['./org-iam-policy.component.scss'],
|
||||
selector: 'cnsl-domain-policy',
|
||||
templateUrl: './domain-policy.component.html',
|
||||
styleUrls: ['./domain-policy.component.scss'],
|
||||
})
|
||||
export class OrgIamPolicyComponent implements OnInit, OnDestroy {
|
||||
export class DomainPolicyComponent implements OnInit, OnDestroy {
|
||||
private managementService!: ManagementService;
|
||||
@Input() public serviceType!: PolicyComponentServiceType;
|
||||
|
||||
public iamData!: OrgIAMPolicy.AsObject;
|
||||
public domainData!: DomainPolicy.AsObject;
|
||||
|
||||
public loading: boolean = false;
|
||||
private sub: Subscription = new Subscription();
|
||||
private org!: Org.AsObject;
|
||||
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
|
||||
constructor(private toast: ToastService, private injector: Injector, private adminService: AdminService) {}
|
||||
constructor(
|
||||
private toast: ToastService,
|
||||
private injector: Injector,
|
||||
private adminService: AdminService,
|
||||
private storageService: StorageService,
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
@ -40,22 +51,32 @@ export class OrgIamPolicyComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public fetchData(): void {
|
||||
this.getData().then((resp) => {
|
||||
this.loading = true;
|
||||
this.getData()
|
||||
.then((resp) => {
|
||||
this.loading = false;
|
||||
if (resp?.policy) {
|
||||
this.iamData = resp.policy;
|
||||
this.domainData = resp.policy;
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.loading = false;
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
private async getData(): Promise<GetCustomOrgIAMPolicyResponse.AsObject | GetOrgIAMPolicyResponse.AsObject | any> {
|
||||
const org: Org.AsObject | null = this.storageService.getItem('organization', StorageLocation.session);
|
||||
|
||||
if (org?.id) {
|
||||
this.org = org;
|
||||
}
|
||||
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return this.managementService.getOrgIAMPolicy();
|
||||
return this.managementService.getDomainPolicy();
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
if (this.org?.id) {
|
||||
return this.adminService.getCustomOrgIAMPolicy(this.org.id);
|
||||
}
|
||||
break;
|
||||
return this.adminService.getCustomDomainPolicy(this.org.id);
|
||||
default:
|
||||
return Promise.reject();
|
||||
}
|
||||
@ -64,23 +85,33 @@ export class OrgIamPolicyComponent implements OnInit, OnDestroy {
|
||||
public savePolicy(): void {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
if ((this.iamData as OrgIAMPolicy.AsObject).isDefault) {
|
||||
if ((this.domainData as OrgIAMPolicy.AsObject).isDefault) {
|
||||
const req = new AddCustomDomainPolicyRequest();
|
||||
req.setOrgId(this.org.id);
|
||||
req.setUserLoginMustBeDomain(this.domainData.userLoginMustBeDomain);
|
||||
req.setValidateOrgDomains(this.domainData.validateOrgDomains);
|
||||
req.setSmtpSenderAddressMatchesInstanceDomain(this.domainData.smtpSenderAddressMatchesInstanceDomain);
|
||||
|
||||
this.adminService
|
||||
.addCustomOrgIAMPolicy(this.org.id, this.iamData.userLoginMustBeDomain)
|
||||
.addCustomDomainPolicy(req)
|
||||
.then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
this.fetchData();
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
} else {
|
||||
const req = new AddCustomDomainPolicyRequest();
|
||||
req.setOrgId(this.org.id);
|
||||
req.setUserLoginMustBeDomain(this.domainData.userLoginMustBeDomain);
|
||||
req.setValidateOrgDomains(this.domainData.validateOrgDomains);
|
||||
req.setSmtpSenderAddressMatchesInstanceDomain(this.domainData.smtpSenderAddressMatchesInstanceDomain);
|
||||
|
||||
this.adminService
|
||||
.updateCustomOrgIAMPolicy(this.org.id, this.iamData.userLoginMustBeDomain)
|
||||
.updateCustomDomainPolicy(req)
|
||||
.then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
this.fetchData();
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
@ -88,12 +119,15 @@ export class OrgIamPolicyComponent implements OnInit, OnDestroy {
|
||||
break;
|
||||
}
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
// update Default org iam policy?
|
||||
const req = new UpdateDomainPolicyRequest();
|
||||
req.setUserLoginMustBeDomain(this.domainData.userLoginMustBeDomain);
|
||||
req.setValidateOrgDomains(this.domainData.validateOrgDomains);
|
||||
req.setSmtpSenderAddressMatchesInstanceDomain(this.domainData.smtpSenderAddressMatchesInstanceDomain);
|
||||
|
||||
this.adminService
|
||||
.updateOrgIAMPolicy(this.iamData.userLoginMustBeDomain)
|
||||
.updateDomainPolicy(req)
|
||||
.then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
this.fetchData();
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
@ -105,7 +139,7 @@ export class OrgIamPolicyComponent implements OnInit, OnDestroy {
|
||||
public removePolicy(): void {
|
||||
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
this.adminService
|
||||
.resetCustomOrgIAMPolicyToDefault(this.org.id)
|
||||
.resetCustomDomainPolicyToDefault(this.org.id)
|
||||
.then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
|
||||
setTimeout(() => {
|
||||
@ -119,8 +153,8 @@ export class OrgIamPolicyComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public get isDefault(): boolean {
|
||||
if (this.iamData && this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
return (this.iamData as OrgIAMPolicy.AsObject).isDefault;
|
||||
if (this.domainData && this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
return (this.domainData as OrgIAMPolicy.AsObject).isDefault;
|
||||
} else {
|
||||
return false;
|
||||
}
|
@ -2,8 +2,9 @@ import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
@ -13,27 +14,26 @@ import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.mod
|
||||
|
||||
import { CardModule } from '../../card/card.module';
|
||||
import { InfoSectionModule } from '../../info-section/info-section.module';
|
||||
import { OrgIamPolicyRoutingModule } from './org-iam-policy-routing.module';
|
||||
import { OrgIamPolicyComponent } from './org-iam-policy.component';
|
||||
import { DomainPolicyComponent } from './domain-policy.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [OrgIamPolicyComponent],
|
||||
declarations: [DomainPolicyComponent],
|
||||
imports: [
|
||||
OrgIamPolicyRoutingModule,
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
CardModule,
|
||||
InputModule,
|
||||
MatButtonModule,
|
||||
HasRolePipeModule,
|
||||
MatSlideToggleModule,
|
||||
MatIconModule,
|
||||
HasRoleModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatTooltipModule,
|
||||
InfoSectionModule,
|
||||
MatCheckboxModule,
|
||||
TranslateModule,
|
||||
DetailLayoutModule,
|
||||
],
|
||||
exports: [OrgIamPolicyComponent],
|
||||
exports: [DomainPolicyComponent],
|
||||
})
|
||||
export class OrgIamPolicyModule {}
|
||||
export class DomainPolicyModule {}
|
@ -45,15 +45,9 @@
|
||||
<p class="cnsl-secondary-text">{{ 'MFA.LIST.SECONDFACTORDESCRIPTION' | translate }}</p>
|
||||
|
||||
<div *ngIf="loginData" class="login-policy-row">
|
||||
<mat-slide-toggle
|
||||
card-actions
|
||||
class="login-policy-toggle"
|
||||
color="primary"
|
||||
ngDefaultControl
|
||||
[(ngModel)]="loginData.forceMfa"
|
||||
>
|
||||
<mat-checkbox card-actions class="login-policy-toggle" color="primary" ngDefaultControl [(ngModel)]="loginData.forceMfa">
|
||||
{{ 'POLICY.DATA.FORCEMFA' | translate }}
|
||||
</mat-slide-toggle>
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
<cnsl-card class="max-card-width">
|
||||
<cnsl-mfa-table
|
||||
@ -77,11 +71,60 @@
|
||||
|
||||
<br />
|
||||
|
||||
<h2>{{ 'POLICY.LOGIN_POLICY.LIFETIMEDURATIONS' | translate }}</h2>
|
||||
|
||||
<form class="lifetime-form" (ngSubmit)="savePolicy()" [formGroup]="lifetimeForm" autocomplete="off">
|
||||
<cnsl-form-field class="lifetime-form-field" label="Password Check Lifetime" required="true">
|
||||
<cnsl-label
|
||||
>{{ 'POLICY.DATA.PASSWORDCHECKLIFETIME' | translate }} <strong
|
||||
>({{ 'POLICY.DATA.INHOURS' | translate }})</strong
|
||||
></cnsl-label
|
||||
>
|
||||
<input cnslInput type="number" name="passwordCheckLifetime" formControlName="passwordCheckLifetime" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="lifetime-form-field" label="external Login Check Lifetime" required="true">
|
||||
<cnsl-label
|
||||
>{{ 'POLICY.DATA.EXTERNALLOGINCHECKLIFETIME' | translate }} <strong
|
||||
>({{ 'POLICY.DATA.INHOURS' | translate }})</strong
|
||||
></cnsl-label
|
||||
>
|
||||
<input cnslInput type="number" name="externalLoginCheckLifetime" formControlName="externalLoginCheckLifetime" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="lifetime-form-field" label="MFA Init Skip Lifetime" required="true">
|
||||
<cnsl-label
|
||||
>{{ 'POLICY.DATA.MFAINITSKIPLIFETIME' | translate }} <strong
|
||||
>({{ 'POLICY.DATA.INHOURS' | translate }})</strong
|
||||
></cnsl-label
|
||||
>
|
||||
<input cnslInput type="number" name="mfaInitSkipLifetime" formControlName="mfaInitSkipLifetime" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="lifetime-form-field" label="Second Factor Check Lifetime" required="true">
|
||||
<cnsl-label
|
||||
>{{ 'POLICY.DATA.SECONDFACTORCHECKLIFETIME' | translate }}
|
||||
<strong>({{ 'POLICY.DATA.INHOURS' | translate }})</strong></cnsl-label
|
||||
>
|
||||
<input cnslInput type="number" name="secondFactorCheckLifetime" formControlName="secondFactorCheckLifetime" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="lifetime-form-field" label="Multi Factor Check Lifetime" required="true">
|
||||
<cnsl-label
|
||||
>{{ 'POLICY.DATA.MULTIFACTORCHECKLIFETIME' | translate }}
|
||||
<strong>({{ 'POLICY.DATA.INHOURS' | translate }})</strong></cnsl-label
|
||||
>
|
||||
<input cnslInput type="number" name="multiFactorCheckLifetime" formControlName="multiFactorCheckLifetime" />
|
||||
</cnsl-form-field>
|
||||
</form>
|
||||
|
||||
<br />
|
||||
|
||||
<h2>{{ 'POLICY.LOGIN_POLICY.ADVANCED' | translate }}</h2>
|
||||
|
||||
<cnsl-card class="max-card-width login-policy-content" *ngIf="loginData">
|
||||
<div class="max-card-width login-policy-content" *ngIf="loginData">
|
||||
<div class="login-policy-row">
|
||||
<mat-slide-toggle
|
||||
<mat-checkbox
|
||||
class="login-policy-toggle"
|
||||
color="primary"
|
||||
matTooltip="{{ 'POLICY.DATA.FORCEMFA_DESC' | translate }}"
|
||||
@ -89,16 +132,16 @@
|
||||
[(ngModel)]="loginData.allowUsernamePassword"
|
||||
>
|
||||
{{ 'POLICY.DATA.ALLOWUSERNAMEPASSWORD' | translate }}
|
||||
</mat-slide-toggle>
|
||||
</mat-checkbox>
|
||||
|
||||
<!-- <cnsl-info-section class="info">
|
||||
{{ 'POLICY.DATA.ALLOWUSERNAMEPASSWORD_DESC' | translate }}
|
||||
</cnsl-info-section> -->
|
||||
</div>
|
||||
<div class="login-policy-row">
|
||||
<mat-slide-toggle class="login-policy-toggle" color="primary" ngDefaultControl [(ngModel)]="loginData.allowRegister">
|
||||
<mat-checkbox class="login-policy-toggle" color="primary" ngDefaultControl [(ngModel)]="loginData.allowRegister">
|
||||
{{ 'POLICY.DATA.ALLOWREGISTER' | translate }}
|
||||
</mat-slide-toggle>
|
||||
</mat-checkbox>
|
||||
|
||||
<!-- <ng-template #regInfo>
|
||||
<cnsl-info-section class="info">
|
||||
@ -107,9 +150,9 @@
|
||||
</ng-template> -->
|
||||
</div>
|
||||
<div class="login-policy-row">
|
||||
<mat-slide-toggle class="login-policy-toggle" color="primary" ngDefaultControl [(ngModel)]="loginData.allowExternalIdp">
|
||||
<mat-checkbox class="login-policy-toggle" color="primary" ngDefaultControl [(ngModel)]="loginData.allowExternalIdp">
|
||||
{{ 'POLICY.DATA.ALLOWEXTERNALIDP' | translate }}
|
||||
</mat-slide-toggle>
|
||||
</mat-checkbox>
|
||||
|
||||
<!-- <ng-template #idpInfo>
|
||||
<cnsl-info-section class="info">
|
||||
@ -119,9 +162,9 @@
|
||||
</div>
|
||||
|
||||
<div class="login-policy-row">
|
||||
<mat-slide-toggle class="login-policy-toggle" color="primary" ngDefaultControl [(ngModel)]="loginData.hidePasswordReset">
|
||||
<mat-checkbox class="login-policy-toggle" color="primary" ngDefaultControl [(ngModel)]="loginData.hidePasswordReset">
|
||||
{{ 'POLICY.DATA.HIDEPASSWORDRESET' | translate }}
|
||||
</mat-slide-toggle>
|
||||
</mat-checkbox>
|
||||
|
||||
<!-- <ng-template #passwordResetInfo>
|
||||
<cnsl-info-section class="info">
|
||||
@ -131,9 +174,14 @@
|
||||
</div>
|
||||
|
||||
<div class="login-policy-row">
|
||||
<mat-slide-toggle class="login-policy-toggle" color="primary" ngDefaultControl [(ngModel)]="loginData.ignoreUnknownUsernames">
|
||||
<mat-checkbox
|
||||
class="login-policy-toggle"
|
||||
color="primary"
|
||||
ngDefaultControl
|
||||
[(ngModel)]="loginData.ignoreUnknownUsernames"
|
||||
>
|
||||
{{ 'POLICY.DATA.IGNOREUNKNOWNUSERNAMES' | translate }}
|
||||
</mat-slide-toggle>
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="login-policy-row">
|
||||
@ -142,19 +190,13 @@
|
||||
<input cnslInput placeholder="https://" [(ngModel)]="loginData.defaultRedirectUri" />
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
</cnsl-card>
|
||||
|
||||
<div class="login-policy-btn-container">
|
||||
<button class="login-policy-save-button" (click)="savePolicy()" color="primary" type="submit" mat-raised-button>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="serviceType === PolicyComponentServiceType.MGMT">
|
||||
<ng-container *ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault">
|
||||
<ng-template cnslHasRole [hasRole]="['policy.delete']">
|
||||
<button
|
||||
*ngIf="!isDefault"
|
||||
color="primary"
|
||||
class="loginpolicy-reset-button"
|
||||
matTooltip="{{ 'POLICY.RESET' | translate }}"
|
||||
color="warn"
|
||||
(click)="removePolicy()"
|
||||
@ -164,3 +206,11 @@
|
||||
</button>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<br />
|
||||
|
||||
<div class="login-policy-btn-container">
|
||||
<button class="login-policy-save-button" (click)="savePolicy()" color="primary" type="submit" mat-raised-button>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -12,6 +12,20 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
.lifetime-form {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
column-gap: 1rem;
|
||||
|
||||
@media only screen and (max-width: 950px) {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 500px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.login-policy-content {
|
||||
.login-policy-row {
|
||||
padding-bottom: 0.5rem;
|
||||
@ -26,6 +40,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.loginpolicy-reset-button {
|
||||
margin: 1rem 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.login-policy-btn-container {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { Component, Injector, Input, OnInit, Type } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { Duration } from 'google-protobuf/google/protobuf/duration_pb';
|
||||
import {
|
||||
GetLoginPolicyResponse as AdminGetLoginPolicyResponse,
|
||||
UpdateLoginPolicyRequest,
|
||||
@ -37,16 +39,54 @@ export class LoginPolicyComponent implements OnInit {
|
||||
public loading: boolean = false;
|
||||
public InfoSectionType: any = InfoSectionType;
|
||||
public PasswordlessType: any = PasswordlessType;
|
||||
|
||||
constructor(private toast: ToastService, private injector: Injector) {}
|
||||
public lifetimeForm!: FormGroup;
|
||||
constructor(private toast: ToastService, private injector: Injector, private fb: FormBuilder) {
|
||||
this.lifetimeForm = this.fb.group({
|
||||
passwordCheckLifetime: [240, [Validators.required]],
|
||||
externalLoginCheckLifetime: [12, [Validators.required]],
|
||||
mfaInitSkipLifetime: [720, [Validators.required]],
|
||||
secondFactorCheckLifetime: [12, [Validators.required]],
|
||||
multiFactorCheckLifetime: [12, [Validators.required]],
|
||||
});
|
||||
}
|
||||
|
||||
private fetchData(): void {
|
||||
this.getData().then((resp) => {
|
||||
this.getData()
|
||||
.then((resp) => {
|
||||
console.log(resp);
|
||||
|
||||
if (resp.policy) {
|
||||
this.loginData = resp.policy;
|
||||
this.loading = false;
|
||||
|
||||
this.passwordCheckLifetime?.setValue(
|
||||
this.loginData.passwordCheckLifetime?.seconds ? this.loginData.passwordCheckLifetime?.seconds / 60 / 60 : 240,
|
||||
);
|
||||
|
||||
this.externalLoginCheckLifetime?.setValue(
|
||||
this.loginData.externalLoginCheckLifetime?.seconds
|
||||
? this.loginData.externalLoginCheckLifetime?.seconds / 60 / 60
|
||||
: 12,
|
||||
);
|
||||
|
||||
this.mfaInitSkipLifetime?.setValue(
|
||||
this.loginData.mfaInitSkipLifetime?.seconds ? this.loginData.mfaInitSkipLifetime?.seconds / 60 / 60 : 720,
|
||||
);
|
||||
|
||||
this.secondFactorCheckLifetime?.setValue(
|
||||
this.loginData.secondFactorCheckLifetime?.seconds
|
||||
? this.loginData.secondFactorCheckLifetime?.seconds / 60 / 60
|
||||
: 12,
|
||||
);
|
||||
|
||||
this.multiFactorCheckLifetime?.setValue(
|
||||
this.loginData.multiFactorCheckLifetime?.seconds
|
||||
? this.loginData.multiFactorCheckLifetime?.seconds / 60 / 60
|
||||
: 12,
|
||||
);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(this.toast.showError);
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
@ -88,6 +128,22 @@ export class LoginPolicyComponent implements OnInit {
|
||||
mgmtreq.setForceMfa(this.loginData.forceMfa);
|
||||
mgmtreq.setPasswordlessType(this.loginData.passwordlessType);
|
||||
mgmtreq.setHidePasswordReset(this.loginData.hidePasswordReset);
|
||||
|
||||
const pcl = new Duration().setSeconds((this.passwordCheckLifetime?.value ?? 240) * 60 * 60);
|
||||
mgmtreq.setPasswordCheckLifetime(pcl);
|
||||
|
||||
const elcl = new Duration().setSeconds((this.externalLoginCheckLifetime?.value ?? 12) * 60 * 60);
|
||||
mgmtreq.setExternalLoginCheckLifetime(elcl);
|
||||
|
||||
const misl = new Duration().setSeconds((this.mfaInitSkipLifetime?.value ?? 720) * 60 * 60);
|
||||
mgmtreq.setMfaInitSkipLifetime(misl);
|
||||
|
||||
const sfcl = new Duration().setSeconds((this.secondFactorCheckLifetime?.value ?? 12) * 60 * 60);
|
||||
mgmtreq.setSecondFactorCheckLifetime(sfcl);
|
||||
|
||||
const mficl = new Duration().setSeconds((this.multiFactorCheckLifetime?.value ?? 12) * 60 * 60);
|
||||
mgmtreq.setMultiFactorCheckLifetime(mficl);
|
||||
|
||||
mgmtreq.setIgnoreUnknownUsernames(this.loginData.ignoreUnknownUsernames);
|
||||
mgmtreq.setDefaultRedirectUri(this.loginData.defaultRedirectUri);
|
||||
|
||||
@ -107,6 +163,21 @@ export class LoginPolicyComponent implements OnInit {
|
||||
adminreq.setForceMfa(this.loginData.forceMfa);
|
||||
adminreq.setPasswordlessType(this.loginData.passwordlessType);
|
||||
adminreq.setHidePasswordReset(this.loginData.hidePasswordReset);
|
||||
|
||||
const admin_pcl = new Duration().setSeconds((this.passwordCheckLifetime?.value ?? 240) * 60 * 60);
|
||||
adminreq.setPasswordCheckLifetime(admin_pcl);
|
||||
|
||||
const admin_elcl = new Duration().setSeconds((this.externalLoginCheckLifetime?.value ?? 12) * 60 * 60);
|
||||
adminreq.setExternalLoginCheckLifetime(admin_elcl);
|
||||
|
||||
const admin_misl = new Duration().setSeconds((this.mfaInitSkipLifetime?.value ?? 720) * 60 * 60);
|
||||
adminreq.setMfaInitSkipLifetime(admin_misl);
|
||||
|
||||
const admin_sfcl = new Duration().setSeconds((this.secondFactorCheckLifetime?.value ?? 12) * 60 * 60);
|
||||
adminreq.setSecondFactorCheckLifetime(admin_sfcl);
|
||||
|
||||
const admin_mficl = new Duration().setSeconds((this.multiFactorCheckLifetime?.value ?? 12) * 60 * 60);
|
||||
adminreq.setMultiFactorCheckLifetime(admin_mficl);
|
||||
adminreq.setIgnoreUnknownUsernames(this.loginData.ignoreUnknownUsernames);
|
||||
adminreq.setDefaultRedirectUri(this.loginData.defaultRedirectUri);
|
||||
// adminreq.setPasswordCheckLifetime(this.loginData.passwordCheckLifetime);
|
||||
@ -153,4 +224,24 @@ export class LoginPolicyComponent implements OnInit {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public get passwordCheckLifetime(): AbstractControl | null {
|
||||
return this.lifetimeForm.get('passwordCheckLifetime');
|
||||
}
|
||||
|
||||
public get externalLoginCheckLifetime(): AbstractControl | null {
|
||||
return this.lifetimeForm.get('externalLoginCheckLifetime');
|
||||
}
|
||||
|
||||
public get mfaInitSkipLifetime(): AbstractControl | null {
|
||||
return this.lifetimeForm.get('mfaInitSkipLifetime');
|
||||
}
|
||||
|
||||
public get secondFactorCheckLifetime(): AbstractControl | null {
|
||||
return this.lifetimeForm.get('secondFactorCheckLifetime');
|
||||
}
|
||||
|
||||
public get multiFactorCheckLifetime(): AbstractControl | null {
|
||||
return this.lifetimeForm.get('multiFactorCheckLifetime');
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatRippleModule } from '@angular/material/core';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
@ -30,6 +31,7 @@ import { MfaTableComponent } from './mfa-table/mfa-table.component';
|
||||
InfoSectionModule,
|
||||
FormsModule,
|
||||
CardModule,
|
||||
ReactiveFormsModule,
|
||||
InputModule,
|
||||
MatIconModule,
|
||||
MatButtonModule,
|
||||
@ -37,6 +39,7 @@ import { MfaTableComponent } from './mfa-table/mfa-table.component';
|
||||
HasRoleModule,
|
||||
MatDialogModule,
|
||||
HasRolePipeModule,
|
||||
MatCheckboxModule,
|
||||
MatTooltipModule,
|
||||
DetailLayoutModule,
|
||||
MatProgressSpinnerModule,
|
||||
|
@ -1,12 +1,16 @@
|
||||
<div class="spinner-wr">
|
||||
<mat-spinner diameter="30" *ngIf="loading" color="primary"></mat-spinner>
|
||||
<mat-spinner diameter="30" *ngIf="smtpLoading || smsProvidersLoading" color="primary"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<h2>{{ 'SETTING.SMTP.TITLE' | translate }}</h2>
|
||||
|
||||
<cnsl-info-section *ngIf="!form.valid" class="info-section-warn" [fitWidth]="true" [type]="InfoSectionType.ALERT">{{
|
||||
'SETTING.SMTP.REQUIREDWARN' | translate
|
||||
}}</cnsl-info-section>
|
||||
<cnsl-info-section
|
||||
*ngIf="!smtpLoading && !form.valid"
|
||||
class="info-section-warn"
|
||||
[fitWidth]="true"
|
||||
[type]="InfoSectionType.ALERT"
|
||||
>{{ 'SETTING.SMTP.REQUIREDWARN' | translate }}</cnsl-info-section
|
||||
>
|
||||
|
||||
<form (ngSubmit)="savePolicy()" [formGroup]="form" autocomplete="off">
|
||||
<cnsl-form-field class="smtp-form-field" label="Sender Address" required="true">
|
||||
@ -33,17 +37,9 @@
|
||||
<input id="smtp-user" cnslInput name="smtp-user" autocomplete="smtp-user" formControlName="user" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="smtp-form-field" label="Password" required="true">
|
||||
<cnsl-label>{{ 'SETTING.SMTP.PASSWORD' | translate }}</cnsl-label>
|
||||
<input
|
||||
id="smtp-password"
|
||||
cnslInput
|
||||
name="smtp-password"
|
||||
autocomplete="smtp-password"
|
||||
type="password"
|
||||
formControlName="password"
|
||||
/>
|
||||
</cnsl-form-field>
|
||||
<button class="set-password-btn" (click)="setSMTPPassword()" mat-stroked-button>
|
||||
{{ 'SETTING.SMTP.SETPASSWORD' | translate }}
|
||||
</button>
|
||||
|
||||
<div class="general-btn-container">
|
||||
<button class="save-button" (click)="savePolicy()" color="primary" type="submit" mat-raised-button>
|
||||
|
@ -18,6 +18,10 @@
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.set-password-btn {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.general-btn-container {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
|
@ -14,6 +14,7 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
import { InfoSectionType } from '../../info-section/info-section.component';
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
import { DialogAddSMSProviderComponent } from './dialog-add-sms-provider/dialog-add-sms-provider.component';
|
||||
import { SMTPPasswordDialogComponent } from './smtp-password-dialog/smtp-password-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-notification-settings',
|
||||
@ -25,7 +26,12 @@ export class NotificationSettingsComponent implements OnInit {
|
||||
public smsProviders: SMSProvider.AsObject[] = [];
|
||||
public logNotificationProvider!: DebugNotificationProvider.AsObject;
|
||||
public fileNotificationProvider!: DebugNotificationProvider.AsObject;
|
||||
public loading: boolean = false;
|
||||
|
||||
public smtpLoading: boolean = false;
|
||||
public smsProvidersLoading: boolean = false;
|
||||
public logProviderLoading: boolean = false;
|
||||
public fileProviderLoading: boolean = false;
|
||||
|
||||
public form!: FormGroup;
|
||||
|
||||
public SMSProviderConfigState: any = SMSProviderConfigState;
|
||||
@ -45,7 +51,6 @@ export class NotificationSettingsComponent implements OnInit {
|
||||
tls: [true, [Validators.required]],
|
||||
host: ['', [Validators.required]],
|
||||
user: ['', [Validators.required]],
|
||||
password: ['', [Validators.required]],
|
||||
});
|
||||
}
|
||||
|
||||
@ -54,47 +59,61 @@ export class NotificationSettingsComponent implements OnInit {
|
||||
}
|
||||
|
||||
private fetchData(): void {
|
||||
this.smtpLoading = true;
|
||||
this.service
|
||||
.getSMTPConfig()
|
||||
.then((smtpConfig) => {
|
||||
this.smtpLoading = false;
|
||||
if (smtpConfig.smtpConfig) {
|
||||
this.form.patchValue(smtpConfig.smtpConfig);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.smtpLoading = false;
|
||||
if (error && error.code === 5) {
|
||||
console.log(error);
|
||||
}
|
||||
});
|
||||
|
||||
this.service.listSMSProviders().then((smsProviders) => {
|
||||
this.smsProvidersLoading = true;
|
||||
this.service
|
||||
.listSMSProviders()
|
||||
.then((smsProviders) => {
|
||||
this.smsProvidersLoading = false;
|
||||
if (smsProviders.resultList) {
|
||||
this.smsProviders = smsProviders.resultList;
|
||||
console.log(this.smsProviders);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.smsProvidersLoading = false;
|
||||
this.toast.showError(error);
|
||||
});
|
||||
|
||||
this.logProviderLoading = true;
|
||||
this.service
|
||||
.getLogNotificationProvider()
|
||||
.then((logNotificationProvider) => {
|
||||
this.logProviderLoading = false;
|
||||
if (logNotificationProvider.provider) {
|
||||
this.logNotificationProvider = logNotificationProvider.provider;
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.logProviderLoading = false;
|
||||
this.toast.showError(error);
|
||||
});
|
||||
|
||||
this.fileProviderLoading = true;
|
||||
this.service
|
||||
.getFileSystemNotificationProvider()
|
||||
.then((fileNotificationProvider) => {
|
||||
this.fileProviderLoading = false;
|
||||
if (fileNotificationProvider.provider) {
|
||||
console.log(fileNotificationProvider);
|
||||
this.fileNotificationProvider = fileNotificationProvider.provider;
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log('hehe');
|
||||
this.fileProviderLoading = false;
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
@ -107,16 +126,7 @@ export class NotificationSettingsComponent implements OnInit {
|
||||
req.setTls(this.tls?.value ?? false);
|
||||
req.setUser(this.user?.value ?? '');
|
||||
|
||||
return this.service.updateSMTPConfig(req).then(() => {
|
||||
let passwordReq: UpdateSMTPConfigPasswordRequest;
|
||||
if (this.password) {
|
||||
passwordReq = new UpdateSMTPConfigPasswordRequest();
|
||||
passwordReq.setPassword(this.password.value);
|
||||
return this.service.updateSMTPConfigPassword(passwordReq);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
});
|
||||
return this.service.updateSMTPConfig(req).catch(this.toast.showError);
|
||||
}
|
||||
|
||||
public savePolicy(): void {
|
||||
@ -125,7 +135,6 @@ export class NotificationSettingsComponent implements OnInit {
|
||||
prom
|
||||
.then(() => {
|
||||
this.toast.showInfo('SETTING.SMTP.SAVED', true);
|
||||
this.loading = true;
|
||||
setTimeout(() => {
|
||||
this.fetchData();
|
||||
}, 2000);
|
||||
@ -155,6 +164,28 @@ export class NotificationSettingsComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
public setSMTPPassword(): void {
|
||||
const dialogRef = this.dialog.open(SMTPPasswordDialogComponent, {
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe((password: string) => {
|
||||
if (password) {
|
||||
const passwordReq = new UpdateSMTPConfigPasswordRequest();
|
||||
passwordReq.setPassword(password);
|
||||
|
||||
this.service
|
||||
.updateSMTPConfigPassword(passwordReq)
|
||||
.then(() => {
|
||||
this.toast.showInfo('SETTING.SMTP.PASSWORDSET', true);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public get twilio(): SMSProvider.AsObject | undefined {
|
||||
return this.smsProviders.find((p) => p.twilio);
|
||||
}
|
||||
@ -178,8 +209,4 @@ export class NotificationSettingsComponent implements OnInit {
|
||||
public get host(): AbstractControl | null {
|
||||
return this.form.get('host');
|
||||
}
|
||||
|
||||
public get password(): AbstractControl | null {
|
||||
return this.form.get('password');
|
||||
}
|
||||
}
|
||||
|
@ -14,9 +14,10 @@ import { InfoSectionModule } from '../../info-section/info-section.module';
|
||||
import { InputModule } from '../../input/input.module';
|
||||
import { DialogAddSMSProviderComponent } from './dialog-add-sms-provider/dialog-add-sms-provider.component';
|
||||
import { NotificationSettingsComponent } from './notification-settings.component';
|
||||
import { SMTPPasswordDialogComponent } from './smtp-password-dialog/smtp-password-dialog.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [NotificationSettingsComponent, DialogAddSMSProviderComponent],
|
||||
declarations: [NotificationSettingsComponent, DialogAddSMSProviderComponent, SMTPPasswordDialogComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
CardModule,
|
||||
|
@ -0,0 +1,25 @@
|
||||
<h1 mat-dialog-title>
|
||||
<span>{{ 'SETTING.SMTP.SETPASSWORD' | translate }} {{ data?.number }}</span>
|
||||
</h1>
|
||||
<div mat-dialog-content>
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'SETTING.SMTP.PASSWORD' | translate }}</cnsl-label>
|
||||
<input cnslInput [(ngModel)]="password" />
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
<div mat-dialog-actions class="action">
|
||||
<button color="primary" mat-stroked-button class="ok-button" (click)="closeDialog()">
|
||||
{{ 'ACTIONS.CLOSE' | translate }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
[disabled]="!password"
|
||||
cdkFocusInitial
|
||||
color="primary"
|
||||
mat-raised-button
|
||||
class="ok-button"
|
||||
(click)="closeDialog(password)"
|
||||
>
|
||||
{{ 'ACTIONS.OK' | translate }}
|
||||
</button>
|
||||
</div>
|
@ -0,0 +1,25 @@
|
||||
h1 {
|
||||
font-size: 1.5rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.formfield {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
.ok-button {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { SMTPPasswordDialogComponent } from './smtp-password-dialog.component';
|
||||
|
||||
describe('CodeDialogComponent', () => {
|
||||
let component: SMTPPasswordDialogComponent;
|
||||
let fixture: ComponentFixture<SMTPPasswordDialogComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [SMTPPasswordDialogComponent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SMTPPasswordDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,16 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-smtp-password-dialog',
|
||||
templateUrl: './smtp-password-dialog.component.html',
|
||||
styleUrls: ['./smtp-password-dialog.component.scss'],
|
||||
})
|
||||
export class SMTPPasswordDialogComponent {
|
||||
public password: string = '';
|
||||
constructor(public dialogRef: MatDialogRef<SMTPPasswordDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) {}
|
||||
|
||||
closeDialog(password: string = ''): void {
|
||||
this.dialogRef.close(password);
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { OrgIamPolicyComponent } from './org-iam-policy.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: OrgIamPolicyComponent,
|
||||
data: {
|
||||
animation: 'DetailPage',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class OrgIamPolicyRoutingModule { }
|
@ -1,41 +0,0 @@
|
||||
<h2>{{ 'POLICY.IAM_POLICY.TITLE' | translate }}</h2>
|
||||
<p class="cnsl-secondary-text">{{ 'POLICY.IAM_POLICY.DESCRIPTION' | translate }}</p>
|
||||
<cnsl-info-section *ngIf="isDefault"> {{ 'POLICY.DEFAULTLABEL' | translate }}</cnsl-info-section>
|
||||
|
||||
<ng-template cnslHasRole [hasRole]="['iam.policy.delete']">
|
||||
<button
|
||||
*ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
|
||||
matTooltip="{{ 'POLICY.RESET' | translate }}"
|
||||
color="warn"
|
||||
(click)="removePolicy()"
|
||||
mat-stroked-button
|
||||
>
|
||||
{{ 'POLICY.RESET' | translate }}
|
||||
</button>
|
||||
</ng-template>
|
||||
|
||||
<div class="content" *ngIf="iamData">
|
||||
<div class="row">
|
||||
<mat-slide-toggle
|
||||
color="primary"
|
||||
name="hasNumber"
|
||||
ngDefaultControl
|
||||
[disabled]="(['iam.policy.write'] | hasRole | async) === false"
|
||||
[(ngModel)]="iamData.userLoginMustBeDomain"
|
||||
>
|
||||
{{ 'POLICY.DATA.USERLOGINMUSTBEDOMAIN' | translate }}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-container">
|
||||
<button
|
||||
(click)="savePolicy()"
|
||||
[disabled]="(['iam.policy.write'] | hasRole | async) === false"
|
||||
color="primary"
|
||||
type="submit"
|
||||
mat-raised-button
|
||||
>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
@ -1,25 +0,0 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { OrgIamPolicyComponent } from './org-iam-policy.component';
|
||||
|
||||
describe('OrgIamPolicyComponent', () => {
|
||||
let component: OrgIamPolicyComponent;
|
||||
let fixture: ComponentFixture<OrgIamPolicyComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [OrgIamPolicyComponent],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(OrgIamPolicyComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -40,7 +40,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<mat-slide-toggle
|
||||
<mat-checkbox
|
||||
class="slide-toggle"
|
||||
color="primary"
|
||||
name="hasNumber"
|
||||
@ -52,10 +52,10 @@
|
||||
<mat-icon class="icon" svgIcon="mdi_numeric"></mat-icon>
|
||||
<span class="left-desc">{{ 'POLICY.DATA.HASNUMBER' | translate }}</span>
|
||||
</div>
|
||||
</mat-slide-toggle>
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
<div class="row">
|
||||
<mat-slide-toggle
|
||||
<mat-checkbox
|
||||
class="slide-toggle"
|
||||
color="primary"
|
||||
name="hasSymbol"
|
||||
@ -67,10 +67,10 @@
|
||||
<mat-icon class="icon" svgIcon="mdi_symbol"></mat-icon>
|
||||
<span class="left-desc">{{ 'POLICY.DATA.HASSYMBOL' | translate }}</span>
|
||||
</div>
|
||||
</mat-slide-toggle>
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
<div class="row">
|
||||
<mat-slide-toggle
|
||||
<mat-checkbox
|
||||
class="slide-toggle"
|
||||
color="primary"
|
||||
name="hasLowercase"
|
||||
@ -82,10 +82,10 @@
|
||||
<mat-icon class="icon" svgIcon="mdi_format-letter-case-lower"></mat-icon>
|
||||
<span class="left-desc">{{ 'POLICY.DATA.HASLOWERCASE' | translate }}</span>
|
||||
</div>
|
||||
</mat-slide-toggle>
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
<div class="row">
|
||||
<mat-slide-toggle
|
||||
<mat-checkbox
|
||||
class="slide-toggle"
|
||||
color="primary"
|
||||
name="hasUppercase"
|
||||
@ -97,7 +97,7 @@
|
||||
<mat-icon class="icon" svgIcon="mdi_format-letter-case-upper"></mat-icon>
|
||||
<span class="left-desc">{{ 'POLICY.DATA.HASUPPERCASE' | translate }}</span>
|
||||
</div>
|
||||
</mat-slide-toggle>
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</cnsl-card>
|
||||
|
@ -32,12 +32,12 @@
|
||||
}
|
||||
|
||||
.slide-toggle {
|
||||
margin-left: 1.75rem;
|
||||
margin-left: 2.25rem;
|
||||
|
||||
.slide-toggle-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 1.5rem;
|
||||
margin-left: 2.25rem;
|
||||
|
||||
.icon {
|
||||
margin-right: 1rem;
|
||||
|
@ -2,9 +2,9 @@ import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
@ -25,10 +25,10 @@ import { PasswordComplexityPolicyComponent } from './password-complexity-policy.
|
||||
FormsModule,
|
||||
InputModule,
|
||||
MatButtonModule,
|
||||
MatSlideToggleModule,
|
||||
MatIconModule,
|
||||
HasRoleModule,
|
||||
MatTooltipModule,
|
||||
MatCheckboxModule,
|
||||
HasRolePipeModule,
|
||||
TranslateModule,
|
||||
DetailLayoutModule,
|
||||
|
@ -2,18 +2,30 @@
|
||||
<p class="name cnsl-secondary-text">{{ name }}</p>
|
||||
|
||||
<div class="wrapper">
|
||||
<button [disabled]="disabled" class="color-shadow" [style.background-color]="previewColor"
|
||||
(click)="isOpen = !isOpen" cdkOverlayOrigin #trigger="cdkOverlayOrigin"
|
||||
matTooltip="{{'ACTIONS.SET' | translate}}"> </button>
|
||||
<button
|
||||
[disabled]="disabled"
|
||||
class="color-shadow"
|
||||
[style.background-color]="previewColor"
|
||||
(click)="isOpen = !isOpen"
|
||||
cdkOverlayOrigin
|
||||
#trigger="cdkOverlayOrigin"
|
||||
matTooltip="{{ 'ACTIONS.SET' | translate }}"
|
||||
></button>
|
||||
|
||||
<div class="hex-wrapper" [ngClass]="{'pointer': !disabled}" (click)="disabled ? null : (isOpen = !isOpen)">
|
||||
<div class="hex-wrapper" [ngClass]="{ pointer: !disabled }" (click)="disabled ? null : (isOpen = !isOpen)">
|
||||
<i class="las la-hashtag"></i>
|
||||
<span class="hex">{{ previewColorCropped }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template cdkConnectedOverlay [cdkConnectedOverlayOrigin]="trigger" [cdkConnectedOverlayOpen]="isOpen"
|
||||
(overlayOutsideClick)="isOpen = false">
|
||||
<ng-template
|
||||
cdkConnectedOverlay
|
||||
(detach)="submitColor()"
|
||||
[cdkConnectedOverlayOrigin]="trigger"
|
||||
[cdkConnectedOverlayOpen]="isOpen"
|
||||
(overlayOutsideClick)="isOpen = false"
|
||||
>
|
||||
<color-chrome class="picker" [color]="previewColor" (onChangeComplete)="changeComplete($event)"> </color-chrome>
|
||||
<button class="close-icon" mat-mini-fab color="primary" (click)="submitColor()"><mat-icon>check</mat-icon></button>
|
||||
</ng-template>
|
@ -42,12 +42,20 @@
|
||||
}
|
||||
|
||||
.picker {
|
||||
margin: 1rem 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
transform: translateX(50%) translateY(-50%);
|
||||
}
|
||||
|
||||
// stylelint-disable
|
||||
::ng-deep .chrome-picker {
|
||||
border-radius: 0.5rem !important;
|
||||
box-shadow: 0 0 1px #00000020, 0 1px 3px #00000020 !important;
|
||||
}
|
||||
|
||||
::ng-deep .saturation {
|
||||
|
@ -123,7 +123,12 @@ export class ColorComponent implements OnInit {
|
||||
}
|
||||
|
||||
public changeComplete(event: ColorEvent): void {
|
||||
this.emitPreview(event.color.hex);
|
||||
this.color = event.color.hex;
|
||||
}
|
||||
|
||||
public submitColor(): void {
|
||||
this.emitPreview(this.color);
|
||||
this.isOpen = false;
|
||||
}
|
||||
|
||||
public get previewColorCropped(): string {
|
||||
|
@ -1,17 +1,26 @@
|
||||
@use '@angular/material' as mat;
|
||||
|
||||
@mixin preview-theme($theme) {
|
||||
@import '../../../../../styles/input.scss';
|
||||
@import '../../../label/label.component.scss';
|
||||
|
||||
@mixin preview-theme($show-dark) {
|
||||
$theme: if($show-dark, $caos-dark-app-theme, $caos-light-app-theme);
|
||||
|
||||
@include input-theme($theme);
|
||||
@include cnsl-label-theme($theme);
|
||||
@include mat.all-component-themes($theme);
|
||||
|
||||
$primary: map-get($theme, primary);
|
||||
$primary-color: mat.get-color-from-palette($primary, 500);
|
||||
$is-dark-theme: map-get($theme, is-dark);
|
||||
$background: map-get($theme, background);
|
||||
$foreground: map-get($theme, foreground);
|
||||
$p-border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2));
|
||||
|
||||
.preview {
|
||||
pointer-events: none;
|
||||
border-radius: 0.5rem;
|
||||
transform: scale(0.9);
|
||||
color: map-get($foreground, base);
|
||||
|
||||
* {
|
||||
pointer-events: none;
|
||||
@ -77,18 +86,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.light {
|
||||
.login-wrapper *:not(.cnsl-label):not(.mat-button-wrapper) {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
&.dark {
|
||||
.login-wrapper *:not(.cnsl-label):not(.mat-button-wrapper) {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,10 @@
|
||||
<mat-button-toggle [value]="View.PREVIEW">
|
||||
<div class="toggle-row">
|
||||
<span>{{ 'POLICY.PRIVATELABELING.VIEWS.PREVIEW' | translate }}</span>
|
||||
<i
|
||||
class="info-i las la-question-circle"
|
||||
matTooltip="{{ 'POLICY.PRIVATELABELING.PREVIEW_DESCRIPTION' | translate }}"
|
||||
></i>
|
||||
<div *ngIf="view === View.PREVIEW" class="current-dot"></div>
|
||||
</div>
|
||||
</mat-button-toggle>
|
||||
@ -71,10 +75,6 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<cnsl-info-section *ngIf="view === View.PREVIEW" class="desc cnsl-secondary-text">
|
||||
{{ 'POLICY.PRIVATELABELING.PREVIEW_DESCRIPTION' | translate }}
|
||||
</cnsl-info-section>
|
||||
|
||||
<div *ngIf="previewData && data" class="lab-policy-content">
|
||||
<mat-accordion class="settings">
|
||||
<mat-expansion-panel class="expansion">
|
||||
@ -223,7 +223,7 @@
|
||||
<cnsl-color
|
||||
[disabled]="view === View.CURRENT"
|
||||
[colorType]="ColorType.BACKGROUNDDARK"
|
||||
(previewChanged)="previewData.backgroundColorDark = $event; savePolicy()"
|
||||
(previewChanged)="previewData.backgroundColorDark !== $event ? setDarkBackgroundColorAndSave($event) : null"
|
||||
name="Background Color"
|
||||
[color]="data.backgroundColorDark"
|
||||
[previewColor]="previewData.backgroundColorDark"
|
||||
@ -234,7 +234,7 @@
|
||||
<cnsl-color
|
||||
[disabled]="view === View.CURRENT"
|
||||
[colorType]="ColorType.PRIMARY"
|
||||
(previewChanged)="previewData.primaryColorDark = $event; savePolicy()"
|
||||
(previewChanged)="previewData.primaryColorDark !== $event ? setDarkPrimaryColorAndSave($event) : null"
|
||||
name="Primary Color"
|
||||
[color]="data.primaryColorDark"
|
||||
[previewColor]="previewData.primaryColorDark"
|
||||
@ -246,7 +246,7 @@
|
||||
<cnsl-color
|
||||
[disabled]="view === View.CURRENT"
|
||||
[colorType]="ColorType.WARN"
|
||||
(previewChanged)="previewData.warnColorDark = $event; savePolicy()"
|
||||
(previewChanged)="previewData.warnColorDark !== $event ? setDarkWarnColorAndSave($event) : null"
|
||||
name="Warn Color"
|
||||
[color]="data.warnColorDark"
|
||||
[previewColor]="previewData.warnColorDark"
|
||||
@ -258,7 +258,7 @@
|
||||
<cnsl-color
|
||||
[disabled]="view === View.CURRENT"
|
||||
[colorType]="ColorType.FONTDARK"
|
||||
(previewChanged)="previewData.fontColorDark = $event; savePolicy()"
|
||||
(previewChanged)="previewData.fontColorDark !== $event ? setDarkFontColorAndSave($event) : null"
|
||||
name="Font Color"
|
||||
[color]="data.fontColorDark"
|
||||
[previewColor]="previewData.fontColorDark"
|
||||
@ -274,7 +274,7 @@
|
||||
<cnsl-color
|
||||
[disabled]="view === View.CURRENT"
|
||||
[colorType]="ColorType.BACKGROUNDLIGHT"
|
||||
(previewChanged)="previewData.backgroundColor = $event; savePolicy()"
|
||||
(previewChanged)="previewData.backgroundColor !== $event ? setBackgroundColorAndSave($event) : null"
|
||||
name="Background Color"
|
||||
[color]="data.backgroundColor"
|
||||
[previewColor]="previewData.backgroundColor"
|
||||
@ -285,7 +285,7 @@
|
||||
<cnsl-color
|
||||
[disabled]="view === View.CURRENT"
|
||||
[colorType]="ColorType.PRIMARY"
|
||||
(previewChanged)="previewData.primaryColor = $event; savePolicy()"
|
||||
(previewChanged)="previewData.primaryColor !== $event ? setPrimaryColorAndSave($event) : null"
|
||||
name="Primary Color"
|
||||
[color]="data.primaryColor"
|
||||
[previewColor]="previewData.primaryColor"
|
||||
@ -298,7 +298,7 @@
|
||||
[disabled]="view === View.CURRENT"
|
||||
[colorType]="ColorType.WARN"
|
||||
name="Warn Color"
|
||||
(previewChanged)="previewData.warnColor = $event; savePolicy()"
|
||||
(previewChanged)="previewData.warnColor !== $event ? setWarnColorAndSave($event) : null"
|
||||
[color]="data.warnColor"
|
||||
[previewColor]="previewData.warnColor"
|
||||
></cnsl-color>
|
||||
@ -308,7 +308,7 @@
|
||||
<cnsl-color
|
||||
[disabled]="view === View.CURRENT"
|
||||
[colorType]="ColorType.FONTLIGHT"
|
||||
(previewChanged)="previewData.fontColor = $event; savePolicy()"
|
||||
(previewChanged)="previewData.fontColor !== $event ? setFontColorAndSave($event) : null"
|
||||
name="Font Color"
|
||||
[color]="data.fontColor"
|
||||
[previewColor]="previewData.fontColor"
|
||||
@ -420,6 +420,7 @@
|
||||
[refresh]="refreshPreview"
|
||||
[theme]="theme"
|
||||
class="preview"
|
||||
[ngClass]="{ darkmode: theme === Theme.DARK, lightmode: theme === Theme.LIGHT }"
|
||||
[policy]="view === View.PREVIEW ? previewData : data"
|
||||
>
|
||||
</cnsl-preview>
|
||||
|
@ -1,5 +1,7 @@
|
||||
@use '@angular/material' as mat;
|
||||
|
||||
@import './preview/preview.component.scss';
|
||||
|
||||
@mixin private-label-theme($theme) {
|
||||
$primary: map-get($theme, primary);
|
||||
$primary-color: mat.get-color-from-palette($primary, 500);
|
||||
@ -68,6 +70,12 @@
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.info-i {
|
||||
font-size: 1.2rem;
|
||||
margin-left: 0.5rem;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.current-dot {
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
@ -314,6 +322,16 @@
|
||||
min-height: 600px;
|
||||
width: fit-content;
|
||||
margin: auto;
|
||||
|
||||
.preview {
|
||||
&.lightmode {
|
||||
@include preview-theme(false);
|
||||
}
|
||||
|
||||
&.darkmode {
|
||||
@include preview-theme(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -473,6 +473,46 @@ export class PrivateLabelingPolicyComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
public setDarkBackgroundColorAndSave($event: string): void {
|
||||
this.previewData.backgroundColorDark = $event;
|
||||
this.savePolicy();
|
||||
}
|
||||
|
||||
public setDarkPrimaryColorAndSave($event: string): void {
|
||||
this.previewData.primaryColorDark = $event;
|
||||
this.savePolicy();
|
||||
}
|
||||
|
||||
public setDarkWarnColorAndSave($event: string): void {
|
||||
this.previewData.warnColorDark = $event;
|
||||
this.savePolicy();
|
||||
}
|
||||
|
||||
public setDarkFontColorAndSave($event: string): void {
|
||||
this.previewData.fontColorDark = $event;
|
||||
this.savePolicy();
|
||||
}
|
||||
|
||||
public setBackgroundColorAndSave($event: string): void {
|
||||
this.previewData.backgroundColor = $event;
|
||||
this.savePolicy();
|
||||
}
|
||||
|
||||
public setPrimaryColorAndSave($event: string): void {
|
||||
this.previewData.primaryColor = $event;
|
||||
this.savePolicy();
|
||||
}
|
||||
|
||||
public setWarnColorAndSave($event: string): void {
|
||||
this.previewData.warnColor = $event;
|
||||
this.savePolicy();
|
||||
}
|
||||
|
||||
public setFontColorAndSave($event: string): void {
|
||||
this.previewData.fontColor = $event;
|
||||
this.savePolicy();
|
||||
}
|
||||
|
||||
public overwriteValues(req: AddCustomLabelPolicyRequest | UpdateCustomLabelPolicyRequest): void {
|
||||
req.setBackgroundColorDark(this.previewData.backgroundColorDark);
|
||||
req.setBackgroundColor(this.previewData.backgroundColor);
|
||||
|
@ -1,34 +1,88 @@
|
||||
<cnsl-detail-layout *ngIf="project" [hasBackButton]="true"
|
||||
title="{{projectName}} {{ 'PROJECT.MEMBER.TITLE' | translate }}">
|
||||
<cnsl-detail-layout
|
||||
*ngIf="project"
|
||||
[hasBackButton]="true"
|
||||
title="{{ projectName }} {{ 'PROJECT.MEMBER.TITLE' | translate }}"
|
||||
>
|
||||
<p class="subinfo" sub>
|
||||
<span class="cnsl-secondary-text">{{ 'PROJECT.MEMBER.DESCRIPTION' | translate }}</span>
|
||||
<a mat-icon-button href="https://docs.zitadel.ch/docs/manuals/admin-managers" target="_blank">
|
||||
<i class="las la-info-circle"></i>
|
||||
</a>
|
||||
</p>
|
||||
<cnsl-members-table *ngIf="project" [dataSource]="dataSource" [memberRoleOptions]="memberRoleOptions"
|
||||
(updateRoles)="updateRoles($event.member, $event.change)" [factoryLoadFunc]="changePageFactory"
|
||||
(changedSelection)="selection = $event" [refreshTrigger]="changePage"
|
||||
[canWrite]="['project.member.write$', 'project.member.write:'+ (projectType === ProjectType.PROJECTTYPE_OWNED) ? $any(project)?.id : (projectType === ProjectType.PROJECTTYPE_GRANTED) ? $any(project)?.projectId: ''] | hasRole | async"
|
||||
[canDelete]="['project.member.delete$', 'project.member.delete:'+(projectType === ProjectType.PROJECTTYPE_OWNED) ? $any(project)?.id : (projectType === ProjectType.PROJECTTYPE_GRANTED) ? $any(project)?.projectId: ''] | hasRole | async"
|
||||
(deleteMember)="removeProjectMember($event)">
|
||||
<ng-template cnslHasRole selectactions
|
||||
[hasRole]="['project.member.delete:' + (projectType === ProjectType.PROJECTTYPE_OWNED) ? $any(project)?.id : (projectType === ProjectType.PROJECTTYPE_GRANTED) ? $any(project)?.projectId: '', 'project.member.delete']">
|
||||
<button (click)="removeProjectMemberSelection()" color="warn"
|
||||
matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" mat-raised-button>
|
||||
<cnsl-members-table
|
||||
*ngIf="project"
|
||||
[dataSource]="dataSource"
|
||||
[memberRoleOptions]="memberRoleOptions"
|
||||
(updateRoles)="updateRoles($event.member, $event.change)"
|
||||
[factoryLoadFunc]="changePageFactory"
|
||||
(changedSelection)="selection = $event"
|
||||
[refreshTrigger]="changePage"
|
||||
[canWrite]="
|
||||
[
|
||||
'project.member.write$',
|
||||
'project.member.write:' + (projectType === ProjectType.PROJECTTYPE_OWNED)
|
||||
? $any(project)?.id
|
||||
: projectType === ProjectType.PROJECTTYPE_GRANTED
|
||||
? $any(project)?.projectId
|
||||
: ''
|
||||
]
|
||||
| hasRole
|
||||
| async
|
||||
"
|
||||
[canDelete]="
|
||||
[
|
||||
'project.member.delete$',
|
||||
'project.member.delete:' + (projectType === ProjectType.PROJECTTYPE_OWNED)
|
||||
? $any(project)?.id
|
||||
: projectType === ProjectType.PROJECTTYPE_GRANTED
|
||||
? $any(project)?.projectId
|
||||
: ''
|
||||
]
|
||||
| hasRole
|
||||
| async
|
||||
"
|
||||
(deleteMember)="removeProjectMember($event)"
|
||||
>
|
||||
<ng-template
|
||||
cnslHasRole
|
||||
selectactions
|
||||
[hasRole]="[
|
||||
'project.member.delete:' + (projectType === ProjectType.PROJECTTYPE_OWNED)
|
||||
? $any(project)?.id
|
||||
: projectType === ProjectType.PROJECTTYPE_GRANTED
|
||||
? $any(project)?.projectId
|
||||
: '',
|
||||
'project.member.delete'
|
||||
]"
|
||||
>
|
||||
<button
|
||||
(click)="($event.stopPropagation); removeProjectMemberSelection()"
|
||||
color="warn"
|
||||
matTooltip="{{ 'ORG_DETAIL.TABLE.DELETE' | translate }}"
|
||||
mat-raised-button
|
||||
>
|
||||
<i class="las la-trash"></i>
|
||||
<span>{{ 'ACTIONS.SELECTIONDELETE' | translate }}</span>
|
||||
<cnsl-action-keys [type]="ActionKeysType.DELETE" (actionTriggered)="removeProjectMemberSelection()">
|
||||
</cnsl-action-keys>
|
||||
</button>
|
||||
</ng-template>
|
||||
<ng-template cnslHasRole writeactions
|
||||
[hasRole]="['project.member.write:'+(projectType === ProjectType.PROJECTTYPE_OWNED) ? $any(project)?.id : (projectType === ProjectType.PROJECTTYPE_GRANTED) ? $any(project)?.projectId: '','project.member.write']">
|
||||
<ng-template
|
||||
cnslHasRole
|
||||
writeactions
|
||||
[hasRole]="[
|
||||
'project.member.write:' + (projectType === ProjectType.PROJECTTYPE_OWNED)
|
||||
? $any(project)?.id
|
||||
: projectType === ProjectType.PROJECTTYPE_GRANTED
|
||||
? $any(project)?.projectId
|
||||
: '',
|
||||
'project.member.write'
|
||||
]"
|
||||
>
|
||||
<button color="primary" (click)="openAddMember()" class="cnsl-action-button" mat-raised-button>
|
||||
<mat-icon class="icon">add</mat-icon>
|
||||
<span>{{ 'ACTIONS.NEW' | translate }}</span>
|
||||
<cnsl-action-keys (actionTriggered)="openAddMember()">
|
||||
</cnsl-action-keys>
|
||||
<cnsl-action-keys (actionTriggered)="openAddMember()"> </cnsl-action-keys>
|
||||
</button>
|
||||
</ng-template>
|
||||
</cnsl-members-table>
|
||||
|
@ -103,6 +103,19 @@ export class ProjectMembersComponent {
|
||||
this.grantId,
|
||||
);
|
||||
};
|
||||
|
||||
const breadcrumbs = [
|
||||
new Breadcrumb({
|
||||
type: BreadcrumbType.ORG,
|
||||
routerLink: ['/org'],
|
||||
}),
|
||||
new Breadcrumb({
|
||||
type: BreadcrumbType.GRANTEDPROJECT,
|
||||
param: { key: 'projectid', value: (this.project as GrantedProject.AsObject).projectId },
|
||||
routerLink: ['/projects', (this.project as GrantedProject.AsObject).projectId],
|
||||
}),
|
||||
];
|
||||
breadcrumbService.setBreadcrumb(breadcrumbs);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -16,9 +16,11 @@
|
||||
<cnsl-password-lockout-policy [serviceType]="serviceType"></cnsl-password-lockout-policy>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="currentSetting === 'login'">
|
||||
<!-- <cnsl-org-iam-policy [serviceType]="serviceType"></cnsl-org-iam-policy> -->
|
||||
<cnsl-login-policy [serviceType]="serviceType"></cnsl-login-policy>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="currentSetting === 'domain'">
|
||||
<cnsl-domain-policy [serviceType]="serviceType"></cnsl-domain-policy>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="currentSetting === 'idp'">
|
||||
<cnsl-idp-settings [serviceType]="serviceType"></cnsl-idp-settings>
|
||||
</ng-container>
|
||||
|
@ -5,6 +5,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
||||
|
||||
import { CardModule } from '../card/card.module';
|
||||
import { DomainPolicyModule } from '../policies/domain-policy/domain-policy.module';
|
||||
import { GeneralSettingsModule } from '../policies/general-settings/general-settings.module';
|
||||
import { IdpSettingsModule } from '../policies/idp-settings/idp-settings.module';
|
||||
import { LoginPolicyModule } from '../policies/login-policy/login-policy.module';
|
||||
@ -12,7 +13,6 @@ import { LoginTextsPolicyModule } from '../policies/login-texts/login-texts.modu
|
||||
import { MessageTextsPolicyModule } from '../policies/message-texts/message-texts.module';
|
||||
import { NotificationSettingsModule } from '../policies/notification-settings/notification-settings.module';
|
||||
import { OIDCConfigurationModule } from '../policies/oidc-configuration/oidc-configuration.module';
|
||||
import { OrgIamPolicyModule } from '../policies/org-iam-policy/org-iam-policy.module';
|
||||
import { PasswordComplexityPolicyModule } from '../policies/password-complexity-policy/password-complexity-policy.module';
|
||||
import { PasswordLockoutPolicyModule } from '../policies/password-lockout-policy/password-lockout-policy.module';
|
||||
import { PrivacyPolicyModule } from '../policies/privacy-policy/privacy-policy.module';
|
||||
@ -37,7 +37,7 @@ import { SettingsListComponent } from './settings-list.component';
|
||||
PrivacyPolicyModule,
|
||||
MessageTextsPolicyModule,
|
||||
LoginTextsPolicyModule,
|
||||
OrgIamPolicyModule,
|
||||
DomainPolicyModule,
|
||||
TranslateModule,
|
||||
HasRolePipeModule,
|
||||
NotificationSettingsModule,
|
||||
|
@ -19,10 +19,12 @@ export const LOGIN: SidenavSetting = {
|
||||
id: 'login',
|
||||
i18nKey: 'SETTINGS.LIST.LOGIN',
|
||||
groupI18nKey: 'SETTINGS.GROUPS.LOGIN',
|
||||
// requiredRoles: {
|
||||
// [PolicyComponentServiceType.ADMIN]: true,
|
||||
// [PolicyComponentServiceType.MGMT]: true,
|
||||
// }
|
||||
};
|
||||
|
||||
export const DOMAIN: SidenavSetting = {
|
||||
id: 'domain',
|
||||
i18nKey: 'SETTINGS.LIST.DOMAIN',
|
||||
groupI18nKey: 'SETTINGS.GROUPS.DOMAIN',
|
||||
};
|
||||
|
||||
export const LOCKOUT: SidenavSetting = {
|
||||
|
@ -55,6 +55,7 @@ export class SidenavComponent implements ControlValueAccessor, OnInit {
|
||||
queryParams: {
|
||||
[this.queryParam]: setting,
|
||||
},
|
||||
replaceUrl: true,
|
||||
queryParamsHandling: 'merge',
|
||||
skipLocationChange: false,
|
||||
});
|
||||
|
@ -1,6 +1,12 @@
|
||||
<cnsl-refresh-table [loading]="dataSource?.loading$ | async" (refreshed)="changePage()" [hideRefresh]="true"
|
||||
[emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes" [timestamp]="dataSource?.viewTimestamp"
|
||||
[dataSize]="dataSource?.totalResult ?? 0" [selection]="selection">
|
||||
<cnsl-refresh-table
|
||||
[loading]="dataSource?.loading$ | async"
|
||||
(refreshed)="changePage()"
|
||||
[hideRefresh]="true"
|
||||
[emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes"
|
||||
[timestamp]="dataSource?.viewTimestamp"
|
||||
[dataSize]="dataSource?.totalResult ?? 0"
|
||||
[selection]="selection"
|
||||
>
|
||||
<!-- <div leftActions class="user-grants-table-left-actions">
|
||||
<button class="user-grant-type-button" [ngClass]="{'active': type === undefined}"
|
||||
(click)="setType(undefined)">{{'PROJECT.GRANT.ALL' | translate}}</button>
|
||||
@ -10,19 +16,36 @@
|
||||
(click)="setType(Type.TYPE_MACHINE)">{{'USER.TABLE.TYPES.MACHINE' | translate}}</button>
|
||||
</div> -->
|
||||
|
||||
<button color="warn" matTooltip="{{'GRANTS.DELETE' | translate}}" class="cnsl-action-button" mat-raised-button actions
|
||||
(click)="deleteGrantSelection()" *ngIf="selection.hasValue() && disableDelete === false">
|
||||
<button
|
||||
color="warn"
|
||||
matTooltip="{{ 'GRANTS.DELETE' | translate }}"
|
||||
class="cnsl-action-button"
|
||||
mat-raised-button
|
||||
actions
|
||||
(click)="deleteGrantSelection()"
|
||||
*ngIf="selection.hasValue() && disableDelete === false"
|
||||
>
|
||||
<i class="las la-trash"></i>
|
||||
<span>{{ 'ACTIONS.DELETE' | translate }}</span>
|
||||
<cnsl-action-keys (actionTriggered)="deleteGrantSelection()" [type]="ActionKeysType.DELETE"></cnsl-action-keys>
|
||||
</button>
|
||||
|
||||
<cnsl-filter-user-grants actions *ngIf="!selection.hasValue()" (filterChanged)="applySearchQuery($any($event))"
|
||||
(filterOpen)="filterOpen = $event"></cnsl-filter-user-grants>
|
||||
<cnsl-filter-user-grants
|
||||
actions
|
||||
*ngIf="!selection.hasValue()"
|
||||
(filterChanged)="applySearchQuery($any($event))"
|
||||
(filterOpen)="filterOpen = $event"
|
||||
></cnsl-filter-user-grants>
|
||||
|
||||
<a actions *ngIf="disableWrite === false && (selection.hasValue() === false)"
|
||||
matTooltip="{{'GRANTS.ADD' | translate}}" color="primary" class="cnsl-action-button" mat-raised-button
|
||||
[routerLink]="routerLink">
|
||||
<a
|
||||
actions
|
||||
*ngIf="disableWrite === false && selection.hasValue() === false"
|
||||
matTooltip="{{ 'GRANTS.ADD' | translate }}"
|
||||
color="primary"
|
||||
class="cnsl-action-button"
|
||||
mat-raised-button
|
||||
[routerLink]="routerLink"
|
||||
>
|
||||
<mat-icon class="icon">add</mat-icon>
|
||||
<span>{{ 'GRANTS.ADD_BTN' | translate }}</span>
|
||||
<cnsl-action-keys (actionTriggered)="gotoCreateLink(routerLink)" [type]="ActionKeysType.ADD"></cnsl-action-keys>
|
||||
@ -32,23 +55,47 @@
|
||||
<table mat-table multiTemplateDataRows class="table" aria-label="Elements" [dataSource]="dataSource">
|
||||
<ng-container matColumnDef="select">
|
||||
<th class="selection" mat-header-cell *matHeaderCellDef>
|
||||
<mat-checkbox [disabled]="disableWrite" color="primary" (change)="$event ? masterToggle() : null"
|
||||
<mat-checkbox
|
||||
[disabled]="disableWrite"
|
||||
color="primary"
|
||||
(change)="$event ? masterToggle() : null"
|
||||
[checked]="selection.hasValue() && isAllSelected()"
|
||||
[indeterminate]="selection.hasValue() && !isAllSelected()">
|
||||
[indeterminate]="selection.hasValue() && !isAllSelected()"
|
||||
>
|
||||
</mat-checkbox>
|
||||
</th>
|
||||
<td class="selection" mat-cell *matCellDef="let row">
|
||||
<mat-checkbox
|
||||
[disabled]="disableWrite || !((['user.grant.write$'] | hasRole | async) || ((context === UserGrantContext.OWNED_PROJECT ? ['user.grant.write:' + row?.projectId] : context === UserGrantContext.GRANTED_PROJECT ? ['user.grant.write:' + row?.id] : []) | hasRole | async))"
|
||||
color="primary" (click)="$event.stopPropagation()" (change)="$event ? selection.toggle(row) : null"
|
||||
[checked]="selection.isSelected(row)">
|
||||
<cnsl-avatar *ngIf="row && row?.displayName && row.firstName && row.lastName; else cog" class="avatar"
|
||||
[name]="row.displayName" [avatarUrl]="row.avatarUrl || ''" [forColor]="row?.preferredLoginName"
|
||||
[size]="32">
|
||||
[disabled]="
|
||||
disableWrite ||
|
||||
!(
|
||||
(['user.grant.write$'] | hasRole | async) ||
|
||||
((context === UserGrantContext.OWNED_PROJECT
|
||||
? ['user.grant.write:' + row?.projectId]
|
||||
: context === UserGrantContext.GRANTED_PROJECT
|
||||
? ['user.grant.write:' + row?.id]
|
||||
: []
|
||||
)
|
||||
| hasRole
|
||||
| async)
|
||||
)
|
||||
"
|
||||
color="primary"
|
||||
(click)="$event.stopPropagation()"
|
||||
(change)="$event ? selection.toggle(row) : null"
|
||||
[checked]="selection.isSelected(row)"
|
||||
>
|
||||
<cnsl-avatar
|
||||
*ngIf="row && row?.displayName && row.firstName && row.lastName; else cog"
|
||||
class="avatar"
|
||||
[name]="row.displayName"
|
||||
[avatarUrl]="row.avatarUrl || ''"
|
||||
[forColor]="row?.preferredLoginName"
|
||||
[size]="32"
|
||||
>
|
||||
</cnsl-avatar>
|
||||
<ng-template #cog>
|
||||
<cnsl-avatar class="avatar" [isMachine]="true" [forColor]="row.preferredLoginName" [size]="32">
|
||||
</cnsl-avatar>
|
||||
<cnsl-avatar class="avatar" [isMachine]="true" [forColor]="row.preferredLoginName" [size]="32"> </cnsl-avatar>
|
||||
</ng-template>
|
||||
</mat-checkbox>
|
||||
</td>
|
||||
@ -57,21 +104,22 @@
|
||||
<ng-container matColumnDef="user">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'PROJECT.GRANT.USER' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let grant">
|
||||
<a class="user">{{grant.displayName ? grant.displayName :
|
||||
grant.userName ? grant.userName : ''}}</a>
|
||||
<a class="user">{{ grant.displayName ? grant.displayName : grant.userName ? grant.userName : '' }}</a>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="org">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'PROJECT.GRANT.ORG' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let grant">
|
||||
{{grant.orgName}} </td>
|
||||
{{ grant.orgName }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="projectId">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'PROJECT.GRANT.PROJECTNAME' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let grant">
|
||||
{{grant.projectName}} </td>
|
||||
{{ grant.projectName }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="type">
|
||||
@ -84,16 +132,14 @@
|
||||
<ng-container matColumnDef="creationDate">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'PROJECT.GRANT.CREATIONDATE' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let grant">
|
||||
<span class="no-break">{{grant.details.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm'
|
||||
}}</span>
|
||||
<span class="no-break">{{ grant.details.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }}</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="changeDate">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'PROJECT.GRANT.CHANGEDATE' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let grant">
|
||||
<span class="no-break">{{grant.details.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm'
|
||||
}}</span>
|
||||
<span class="no-break">{{ grant.details.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }}</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
@ -103,7 +149,8 @@
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let grant; let i = index" class="role-data">
|
||||
<div class="flex-row">
|
||||
<cnsl-project-role-chip [roleName]="role" *ngFor="let role of grant.roleKeysList">{{ role }}
|
||||
<cnsl-project-role-chip [roleName]="role" *ngFor="let role of grant.roleKeysList"
|
||||
>{{ role }}
|
||||
</cnsl-project-role-chip>
|
||||
</div>
|
||||
</td>
|
||||
@ -113,32 +160,45 @@
|
||||
<th mat-header-cell *matHeaderCellDef class="user-tr-actions"></th>
|
||||
<td mat-cell class="user-tr-actions" *matCellDef="let grant; let i = index">
|
||||
<cnsl-table-actions [hasActions]="true">
|
||||
<button actions matTooltip="{{'ACTIONS.REMOVE' | translate}}" color="warn" (click)="deleteGrant(grant)"
|
||||
mat-icon-button>
|
||||
<button
|
||||
actions
|
||||
matTooltip="{{ 'ACTIONS.REMOVE' | translate }}"
|
||||
color="warn"
|
||||
(click)="deleteGrant($event, grant)"
|
||||
mat-icon-button
|
||||
>
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
|
||||
<button menuActions mat-menu-item [routerLink]="['/users', grant.userId]">
|
||||
{{'ACTIONS.TABLE.SHOWUSER' | translate : ({value: grant.displayName})}}
|
||||
{{ 'ACTIONS.TABLE.SHOWUSER' | translate: { value: grant.displayName } }}
|
||||
</button>
|
||||
</cnsl-table-actions>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr (click)="openEditDialog(grant)" class="highlight pointer" mat-row
|
||||
*matRowDef="let grant; columns: displayedColumns;">
|
||||
</tr>
|
||||
<tr
|
||||
(click)="openEditDialog(grant)"
|
||||
class="highlight pointer"
|
||||
mat-row
|
||||
*matRowDef="let grant; columns: displayedColumns"
|
||||
></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div *ngIf="(dataSource.loading$ | async) === false && !dataSource?.totalResult" class="no-content-row">
|
||||
<i class="las la-exclamation"></i>
|
||||
<span>{{ 'GRANTS.EMPTY' | translate }}</span>
|
||||
</div>
|
||||
<cnsl-paginator class="paginator" #paginator [timestamp]="dataSource?.viewTimestamp" [length]="dataSource.totalResult"
|
||||
[pageSize]="INITIAL_PAGE_SIZE" [length]="dataSource.totalResult" [pageSizeOptions]="[2, 3, 25, 50, 100, 250]"
|
||||
(page)="changePage($event)">
|
||||
<cnsl-paginator
|
||||
class="paginator"
|
||||
#paginator
|
||||
[timestamp]="dataSource?.viewTimestamp"
|
||||
[length]="dataSource.totalResult"
|
||||
[pageSize]="INITIAL_PAGE_SIZE"
|
||||
[length]="dataSource.totalResult"
|
||||
[pageSizeOptions]="[2, 3, 25, 50, 100, 250]"
|
||||
(page)="changePage($event)"
|
||||
>
|
||||
</cnsl-paginator>
|
||||
|
||||
</cnsl-refresh-table>
|
@ -200,7 +200,9 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
|
||||
});
|
||||
}
|
||||
|
||||
public deleteGrant(grant: UserGrant.AsObject): void {
|
||||
public deleteGrant(event: any, grant: UserGrant.AsObject): void {
|
||||
event.stopPropagation();
|
||||
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.DELETE',
|
||||
|
@ -8,6 +8,7 @@ import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/
|
||||
import {
|
||||
BRANDING,
|
||||
COMPLEXITY,
|
||||
DOMAIN,
|
||||
GENERAL,
|
||||
IDP,
|
||||
LOCKOUT,
|
||||
@ -31,12 +32,14 @@ export class InstanceSettingsComponent {
|
||||
public settingsList: SidenavSetting[] = [
|
||||
GENERAL,
|
||||
// notifications
|
||||
{ showWarn: true, ...NOTIFICATIONS },
|
||||
// { showWarn: true, ...NOTIFICATIONS },
|
||||
NOTIFICATIONS,
|
||||
// login
|
||||
LOGIN,
|
||||
COMPLEXITY,
|
||||
LOCKOUT,
|
||||
IDP,
|
||||
DOMAIN,
|
||||
// appearance
|
||||
BRANDING,
|
||||
MESSAGETEXTS,
|
||||
|
@ -8,6 +8,7 @@ import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/
|
||||
import {
|
||||
BRANDING,
|
||||
COMPLEXITY,
|
||||
DOMAIN,
|
||||
IDP,
|
||||
LOCKOUT,
|
||||
LOGIN,
|
||||
@ -29,6 +30,7 @@ export class OrgSettingsComponent {
|
||||
COMPLEXITY,
|
||||
LOCKOUT,
|
||||
IDP,
|
||||
DOMAIN,
|
||||
BRANDING,
|
||||
MESSAGETEXTS,
|
||||
LOGINTEXTS,
|
||||
|
@ -1,28 +1,35 @@
|
||||
<div class="app-create-container">
|
||||
<div class="max-width-container">
|
||||
<div class="enlarged-container">
|
||||
<div class="abort-container">
|
||||
<button (click)="close()" mat-icon-button>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
<span class="abort">{{ 'APP.PAGES.CREATE_OIDC' | translate }}</span>
|
||||
<span class="abort">{{ 'APP.PAGES.CREATE_OIDC' | translate }}</span
|
||||
><span class="abort-2">Step {{ currentCreateStep }} of {{ createSteps }}</span>
|
||||
</div>
|
||||
|
||||
<div class="app-create-content">
|
||||
<h1>{{ 'APP.PAGES.CREATE_OIDC_DESC_TITLE' | translate }}</h1>
|
||||
<p class="desc cnsl-secondary-text">{{'APP.PAGES.CREATE_OIDC_DESC_SUB' | translate}}</p>
|
||||
|
||||
<mat-progress-bar class="progress-bar" color="primary" *ngIf="loading" mode="indeterminate"></mat-progress-bar>
|
||||
|
||||
<mat-checkbox class="proswitch" color="primary" [(ngModel)]="devmode">
|
||||
{{ 'APP.OIDC.PROSWITCH' | translate }}
|
||||
</mat-checkbox>
|
||||
|
||||
<mat-horizontal-stepper class="stepper" *ngIf="!devmode" linear #stepper labelPosition="bottom"
|
||||
(selectionChange)="changeStep($event)">
|
||||
<mat-horizontal-stepper
|
||||
class="stepper"
|
||||
*ngIf="!devmode"
|
||||
linear
|
||||
#stepper
|
||||
labelPosition="bottom"
|
||||
(selectionChange)="changeStep($event)"
|
||||
>
|
||||
<mat-step [stepControl]="firstFormGroup" [editable]="true">
|
||||
<form [formGroup]="firstFormGroup">
|
||||
<ng-template matStepLabel>{{ 'APP.OIDC.NAMEANDTYPESECTION' | translate }}</ng-template>
|
||||
|
||||
<p class="step-title">{{ 'APP.OIDC.TITLEFIRST' | translate }}</p>
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-form-field appearance="outline" class="name-formfield">
|
||||
<cnsl-label>{{ 'APP.NAME' | translate }}</cnsl-label>
|
||||
<input cnslInput cdkFocusInitial formControlName="name" />
|
||||
<span cnslError *ngIf="name?.errors?.required">{{ 'PROJECT.APP.NAMEREQUIRED' | translate }}</span>
|
||||
@ -33,29 +40,47 @@
|
||||
<cnsl-type-radio [types]="appTypes" (selectedType)="appType?.setValue($event)" [selected]="appType?.value">
|
||||
</cnsl-type-radio>
|
||||
<div class="app-create-actions">
|
||||
<span class="fill-space"></span>
|
||||
<button mat-raised-button [disabled]="firstFormGroup.invalid" color="primary" matStepperNext
|
||||
[attr.data-e2e]="'continue-button-nameandtype'">{{'ACTIONS.CONTINUE' | translate}}</button>
|
||||
<button
|
||||
mat-raised-button
|
||||
[disabled]="firstFormGroup.invalid"
|
||||
color="primary"
|
||||
matStepperNext
|
||||
[attr.data-e2e]="'continue-button-nameandtype'"
|
||||
>
|
||||
{{ 'ACTIONS.CONTINUE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</mat-step>
|
||||
|
||||
<!-- skip for native applications -->
|
||||
<mat-step *ngIf="oidcAppRequest.appType !== OIDCAppType.OIDC_APP_TYPE_NATIVE" [stepControl]="secondFormGroup"
|
||||
[editable]="true">
|
||||
<mat-step
|
||||
*ngIf="oidcAppRequest.appType !== OIDCAppType.OIDC_APP_TYPE_NATIVE"
|
||||
[stepControl]="secondFormGroup"
|
||||
[editable]="true"
|
||||
>
|
||||
<form [formGroup]="secondFormGroup">
|
||||
<ng-template matStepLabel>{{ 'APP.AUTHMETHODSECTION' | translate }}</ng-template>
|
||||
|
||||
<cnsl-auth-method-radio [authMethods]="authMethods" [selected]="authMethod?.value"
|
||||
[isOIDC]="appType?.value?.createType === AppCreateType.OIDC" (selectedMethod)="authMethod?.setValue($event)">
|
||||
<cnsl-auth-method-radio
|
||||
[authMethods]="authMethods"
|
||||
[selected]="authMethod?.value"
|
||||
[isOIDC]="appType?.value?.createType === AppCreateType.OIDC"
|
||||
(selectedMethod)="authMethod?.setValue($event)"
|
||||
>
|
||||
</cnsl-auth-method-radio>
|
||||
|
||||
<div class="app-create-actions">
|
||||
<button mat-stroked-button color="primary" matStepperPrevious>{{'ACTIONS.BACK' |
|
||||
translate}}</button>
|
||||
<span class="fill-space"></span>
|
||||
<button mat-raised-button color="primary" [disabled]="secondFormGroup.invalid" matStepperNext
|
||||
[attr.data-e2e]="'continue-button-authmethod'">{{'ACTIONS.CONTINUE' | translate}}</button>
|
||||
<button class="bck-button" mat-stroked-button matStepperPrevious>{{ 'ACTIONS.BACK' | translate }}</button>
|
||||
<button
|
||||
mat-raised-button
|
||||
color="primary"
|
||||
[disabled]="secondFormGroup.invalid"
|
||||
matStepperNext
|
||||
[attr.data-e2e]="'continue-button-authmethod'"
|
||||
>
|
||||
{{ 'ACTIONS.CONTINUE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</mat-step>
|
||||
@ -65,42 +90,63 @@
|
||||
<ng-template matStepLabel>{{ 'APP.OIDC.REDIRECTSECTION' | translate }}</ng-template>
|
||||
|
||||
<p class="step-title">{{ 'APP.OIDC.REDIRECTTITLE' | translate }}</p>
|
||||
<p class="step-description cnsl-secondary-text"
|
||||
*ngIf="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE">
|
||||
{{'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate}}</p>
|
||||
<p
|
||||
class="step-description cnsl-secondary-text"
|
||||
*ngIf="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE"
|
||||
>
|
||||
{{ 'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate }}
|
||||
</p>
|
||||
<p class="step-description cnsl-secondary-text" *ngIf="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_WEB">
|
||||
{{'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate}}</p>
|
||||
{{ 'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate }}
|
||||
</p>
|
||||
|
||||
<cnsl-redirect-uris class="redirect-section" [canWrite]="true"
|
||||
<cnsl-redirect-uris
|
||||
class="redirect-section"
|
||||
[canWrite]="true"
|
||||
[isNative]="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE"
|
||||
(changedUris)="oidcAppRequest.redirectUrisList = $any($event)" [urisList]="oidcAppRequest.redirectUrisList"
|
||||
[getValues]="requestRedirectValuesSubject$" title="{{ 'APP.OIDC.REDIRECT' | translate }}">
|
||||
(changedUris)="oidcAppRequest.redirectUrisList = $any($event)"
|
||||
[urisList]="oidcAppRequest.redirectUrisList"
|
||||
[getValues]="requestRedirectValuesSubject$"
|
||||
title="{{ 'APP.OIDC.REDIRECT' | translate }}"
|
||||
>
|
||||
</cnsl-redirect-uris>
|
||||
|
||||
<p class="step-title">{{ 'APP.OIDC.POSTREDIRECTTITLE' | translate }}</p>
|
||||
<p class="step-description cnsl-secondary-text"
|
||||
*ngIf="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE">
|
||||
{{'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate}}</p>
|
||||
<p class="step-description cnsl-secondary-text"
|
||||
*ngIf="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_WEB || oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_USER_AGENT">
|
||||
{{'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate}}</p>
|
||||
<p
|
||||
class="step-description cnsl-secondary-text"
|
||||
*ngIf="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE"
|
||||
>
|
||||
{{ 'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate }}
|
||||
</p>
|
||||
<p
|
||||
class="step-description cnsl-secondary-text"
|
||||
*ngIf="
|
||||
oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_WEB ||
|
||||
oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_USER_AGENT
|
||||
"
|
||||
>
|
||||
{{ 'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate }}
|
||||
</p>
|
||||
|
||||
<cnsl-redirect-uris class="redirect-section" [canWrite]="true"
|
||||
<cnsl-redirect-uris
|
||||
class="redirect-section"
|
||||
[canWrite]="true"
|
||||
(changedUris)="oidcAppRequest.postLogoutRedirectUrisList = $any($event)"
|
||||
[urisList]="oidcAppRequest.postLogoutRedirectUrisList" title="{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}"
|
||||
[urisList]="oidcAppRequest.postLogoutRedirectUrisList"
|
||||
title="{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}"
|
||||
[getValues]="requestRedirectValuesSubject$"
|
||||
[isNative]="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE">
|
||||
[isNative]="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE"
|
||||
>
|
||||
</cnsl-redirect-uris>
|
||||
|
||||
<div class="app-create-actions">
|
||||
<button mat-stroked-button color="primary" matStepperPrevious>{{'ACTIONS.BACK' | translate}}</button>
|
||||
<span class="fill-space"></span>
|
||||
<button mat-raised-button color="primary" matStepperNext
|
||||
[attr.data-e2e]="'continue-button-redirecturis'">{{'ACTIONS.CONTINUE' | translate}}</button>
|
||||
<button mat-stroked-button class="bck-button" matStepperPrevious>{{ 'ACTIONS.BACK' | translate }}</button>
|
||||
<button mat-raised-button color="primary" matStepperNext [attr.data-e2e]="'continue-button-redirecturis'">
|
||||
{{ 'ACTIONS.CONTINUE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</mat-step>
|
||||
|
||||
|
||||
<mat-step>
|
||||
<ng-template matStepLabel>{{ 'APP.OIDC.OVERVIEWSECTION' | translate }}</ng-template>
|
||||
<p class="step-title">{{ 'APP.OIDC.OVERVIEWTITLE' | translate }}</p>
|
||||
@ -129,7 +175,8 @@
|
||||
<span class="right" *ngIf="oidcAppRequest.grantTypesList && oidcAppRequest.grantTypesList.length > 0">
|
||||
[<span *ngFor="let element of oidcAppRequest.grantTypesList; index as i">
|
||||
{{ 'APP.OIDC.GRANT.' + element | translate }}
|
||||
{{i < oidcAppRequest.grantTypesList.length - 1 ? ', ' : '' }} </span>]
|
||||
{{ i < oidcAppRequest.grantTypesList.length - 1 ? ', ' : '' }} </span
|
||||
>]
|
||||
</span>
|
||||
</div>
|
||||
<div class="row cnsl-secondary-text">
|
||||
@ -138,8 +185,9 @@
|
||||
</span>
|
||||
<span class="right" *ngIf="oidcAppRequest.responseTypesList && oidcAppRequest.responseTypesList.length > 0">
|
||||
[<span *ngFor="let element of oidcAppRequest.responseTypesList; index as i">
|
||||
{{('APP.OIDC.RESPONSE.'+element | translate)}}
|
||||
{{i < oidcAppRequest.responseTypesList.length - 1 ? ', ' : '' }} </span>]
|
||||
{{ 'APP.OIDC.RESPONSE.' + element | translate }}
|
||||
{{ i < oidcAppRequest.responseTypesList.length - 1 ? ', ' : '' }} </span
|
||||
>]
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -161,7 +209,8 @@
|
||||
<span class="right" *ngIf="oidcAppRequest.redirectUrisList && oidcAppRequest.redirectUrisList.length > 0">
|
||||
[<span *ngFor="let redirect of oidcAppRequest.redirectUrisList; index as i">
|
||||
{{ redirect }}
|
||||
{{i < oidcAppRequest.redirectUrisList.length - 1 ? ', ' : '' }} </span>]
|
||||
{{ i < oidcAppRequest.redirectUrisList.length - 1 ? ', ' : '' }} </span
|
||||
>]
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -169,11 +218,14 @@
|
||||
<span class="left">
|
||||
{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}
|
||||
</span>
|
||||
<span class="right"
|
||||
*ngIf="oidcAppRequest.postLogoutRedirectUrisList && oidcAppRequest.postLogoutRedirectUrisList.length > 0">
|
||||
<span
|
||||
class="right"
|
||||
*ngIf="oidcAppRequest.postLogoutRedirectUrisList && oidcAppRequest.postLogoutRedirectUrisList.length > 0"
|
||||
>
|
||||
[<span *ngFor="let redirect of oidcAppRequest.postLogoutRedirectUrisList; index as i">
|
||||
{{ redirect }}
|
||||
{{i < oidcAppRequest.postLogoutRedirectUrisList.length - 1 ? ', ' : '' }} </span>]
|
||||
{{ i < oidcAppRequest.postLogoutRedirectUrisList.length - 1 ? ', ' : '' }} </span
|
||||
>]
|
||||
</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
@ -192,11 +244,16 @@
|
||||
</ng-container>
|
||||
|
||||
<div class="app-create-actions">
|
||||
<button mat-stroked-button color="primary" matStepperPrevious>{{'ACTIONS.BACK' | translate}}</button>
|
||||
<span class="fill-space"></span>
|
||||
<button mat-raised-button color="primary" (click)="createApp()"
|
||||
[attr.data-e2e]="'create-button'">{{'ACTIONS.CREATE' |
|
||||
translate}}</button>
|
||||
<button mat-stroked-button matStepperPrevious class="bck-button">{{ 'ACTIONS.BACK' | translate }}</button>
|
||||
<button
|
||||
mat-raised-button
|
||||
class="create-button"
|
||||
color="primary"
|
||||
(click)="createApp()"
|
||||
[attr.data-e2e]="'create-button'"
|
||||
>
|
||||
{{ 'ACTIONS.CREATE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</mat-step>
|
||||
</mat-horizontal-stepper>
|
||||
@ -223,7 +280,7 @@
|
||||
<cnsl-label>{{ 'APP.OIDC.GRANTTYPE' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="grantTypesList" multiple>
|
||||
<mat-option *ngFor="let grant of oidcGrantTypes" [value]="grant.type">
|
||||
{{ ('APP.OIDC.GRANT.' + grant.type) | translate }}
|
||||
{{ 'APP.OIDC.GRANT.' + grant.type | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
@ -251,25 +308,42 @@
|
||||
|
||||
<div class="content" *ngIf="formappType?.value?.createType === AppCreateType.OIDC">
|
||||
<div class="formfield full-width">
|
||||
<cnsl-redirect-uris class="redirect-section" [canWrite]="true"
|
||||
(changedUris)="oidcAppRequest.redirectUrisList = $any($event)" [urisList]="oidcAppRequest.redirectUrisList"
|
||||
title="{{ 'APP.OIDC.REDIRECT' | translate }}" [getValues]="requestRedirectValuesSubject$"
|
||||
[isNative]="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE">
|
||||
<cnsl-redirect-uris
|
||||
class="redirect-section"
|
||||
[canWrite]="true"
|
||||
(changedUris)="oidcAppRequest.redirectUrisList = $any($event)"
|
||||
[urisList]="oidcAppRequest.redirectUrisList"
|
||||
title="{{ 'APP.OIDC.REDIRECT' | translate }}"
|
||||
[getValues]="requestRedirectValuesSubject$"
|
||||
[isNative]="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE"
|
||||
>
|
||||
</cnsl-redirect-uris>
|
||||
|
||||
<cnsl-redirect-uris class="redirect-section" [canWrite]="true"
|
||||
<cnsl-redirect-uris
|
||||
class="redirect-section"
|
||||
[canWrite]="true"
|
||||
(changedUris)="oidcAppRequest.postLogoutRedirectUrisList = $any($event)"
|
||||
[urisList]="oidcAppRequest.postLogoutRedirectUrisList"
|
||||
title="{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}" [getValues]="requestRedirectValuesSubject$"
|
||||
[isNative]="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE">
|
||||
title="{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}"
|
||||
[getValues]="requestRedirectValuesSubject$"
|
||||
[isNative]="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE"
|
||||
>
|
||||
</cnsl-redirect-uris>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button color="primary" mat-raised-button class="continue-button" [disabled]="form.invalid" cdkFocusInitial
|
||||
type="submit">
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
<button
|
||||
color="primary"
|
||||
mat-raised-button
|
||||
class="continue-button"
|
||||
[disabled]="form.invalid"
|
||||
cdkFocusInitial
|
||||
type="submit"
|
||||
>
|
||||
{{ 'ACTIONS.CREATE' | translate }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -12,13 +12,6 @@ p.desc {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.app-create-container {
|
||||
padding-top: 2rem;
|
||||
|
||||
.progress-bar {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.abort-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -35,6 +28,21 @@ p.desc {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.app-create-content {
|
||||
padding-left: 4.5rem;
|
||||
|
||||
.name-formfield {
|
||||
max-width: 400px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.app-create-container {
|
||||
padding-top: 2rem;
|
||||
|
||||
.progress-bar {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.margin-right {
|
||||
@ -87,9 +95,14 @@ p.desc {
|
||||
.app-create-actions {
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
.bck-button {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.create-button {
|
||||
padding: 0.5rem 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,6 +128,6 @@ p.desc {
|
||||
margin-top: 3rem;
|
||||
display: block;
|
||||
padding: 0.5rem 4rem;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,9 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
||||
public projectId: string = '';
|
||||
public loading: boolean = false;
|
||||
|
||||
public createSteps: number = 4;
|
||||
public currentCreateStep: number = 1;
|
||||
|
||||
public oidcAppRequest: AddOIDCAppRequest.AsObject = new AddOIDCAppRequest().toObject();
|
||||
public apiAppRequest: AddAPIAppRequest.AsObject = new AddAPIAppRequest().toObject();
|
||||
|
||||
@ -270,6 +273,8 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public changeStep(event: StepperSelectionEvent): void {
|
||||
this.currentCreateStep = event.selectedIndex + 1;
|
||||
|
||||
if (event.selectedIndex >= 2) {
|
||||
this.requestRedirectValuesSubject$.next();
|
||||
}
|
||||
|
@ -1,17 +1,32 @@
|
||||
<cnsl-top-view title="{{project?.projectName}}" [hasActions]="false"
|
||||
<cnsl-top-view
|
||||
title="{{ project?.projectName }}"
|
||||
[hasActions]="false"
|
||||
docLink="https://docs.zitadel.ch/docs/guides/basics/projects#what-is-a-granted-project"
|
||||
sub="{{'PROJECT.PAGES.TYPE.GRANTED_SINGULAR' | translate}} {{'ACTIONS.OF' | translate}} <strong>{{project?.projectOwnerName}}</strong>"
|
||||
sub="{{ 'PROJECT.PAGES.TYPE.GRANTED_SINGULAR' | translate }} {{ 'ACTIONS.OF' | translate }} <strong>{{
|
||||
project?.projectOwnerName
|
||||
}}</strong>"
|
||||
[isActive]="project?.state === ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE"
|
||||
[isInactive]="project?.state === ProjectGrantState.PROJECT_GRANT_STATE_INACTIVE"
|
||||
stateTooltip="{{'ORG.STATE.'+project.state | translate}}" [hasContributors]="true">
|
||||
stateTooltip="{{ 'ORG.STATE.' + project?.state | translate }}"
|
||||
[hasContributors]="true"
|
||||
>
|
||||
<p topContent *ngIf="isZitadel" class="granted-project-sub zitadel-warning">
|
||||
{{ 'PROJECT.PAGES.ZITADELPROJECT' | translate }}
|
||||
</p>
|
||||
<cnsl-contributors topContributors class="project-contributors" *ngIf="project" [loading]="loading$ | async"
|
||||
[totalResult]="totalMemberResult" [membersSubject]="membersSubject" title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
|
||||
description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}" (addClicked)="openAddMember()"
|
||||
(showDetailClicked)="showDetail()" (refreshClicked)="loadMembers()"
|
||||
[disabled]="(['project.member.write$', 'project.member.write:'+ project.projectId]| hasRole | async) === false">
|
||||
<cnsl-contributors
|
||||
topContributors
|
||||
class="project-contributors"
|
||||
*ngIf="project"
|
||||
[loading]="loading$ | async"
|
||||
[totalResult]="totalMemberResult"
|
||||
[membersSubject]="membersSubject"
|
||||
title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
|
||||
description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}"
|
||||
(addClicked)="openAddMember()"
|
||||
(showDetailClicked)="showDetail()"
|
||||
(refreshClicked)="loadMembers()"
|
||||
[disabled]="(['project.member.write$', 'project.member.write:' + project.projectId] | hasRole | async) === false"
|
||||
>
|
||||
</cnsl-contributors>
|
||||
<cnsl-info-row topContent *ngIf="project" [grantedProject]="project"></cnsl-info-row>
|
||||
</cnsl-top-view>
|
||||
@ -19,14 +34,21 @@
|
||||
<div class="max-width-container">
|
||||
<cnsl-meta-layout>
|
||||
<ng-template cnslHasRole [hasRole]="['user.grant.read', 'user.grant.read:' + grantId]">
|
||||
<cnsl-card *ngIf="project?.projectId" title="{{ 'GRANTS.PROJECT.TITLE' | translate }}"
|
||||
description="{{'GRANTS.PROJECT.DESCRIPTION' | translate }}">
|
||||
<cnsl-user-grants *ngIf="projectId && grantId" [context]="UserGrantContext.GRANTED_PROJECT"
|
||||
[projectId]="projectId" [grantId]="grantId"
|
||||
<cnsl-card
|
||||
*ngIf="project?.projectId"
|
||||
title="{{ 'GRANTS.PROJECT.TITLE' | translate }}"
|
||||
description="{{ 'GRANTS.PROJECT.DESCRIPTION' | translate }}"
|
||||
>
|
||||
<cnsl-user-grants
|
||||
*ngIf="projectId && grantId"
|
||||
[context]="UserGrantContext.GRANTED_PROJECT"
|
||||
[projectId]="projectId"
|
||||
[grantId]="grantId"
|
||||
[displayedColumns]="['select', 'user', 'projectId', 'creationDate', 'changeDate', 'roleNamesList', 'actions']"
|
||||
[disableWrite]="(['user.grant.write$', 'user.grant.write:' + grantId] | hasRole | async) === false"
|
||||
[disableDelete]="(['user.grant.delete$', 'user.grant.delete:' + grantId] | hasRole | async) === false"
|
||||
[refreshOnPreviousRoutes]="['/grant-create/project/{{projectId}}/grant/{{grantId}}']">
|
||||
[refreshOnPreviousRoutes]="['/grant-create/project/{{projectId}}/grant/{{grantId}}']"
|
||||
>
|
||||
</cnsl-user-grants>
|
||||
</cnsl-card>
|
||||
</ng-template>
|
||||
@ -34,6 +56,5 @@
|
||||
<div metainfo>
|
||||
<cnsl-changes *ngIf="project" [changeType]="ChangeType.PROJECT" [id]="project.projectId"></cnsl-changes>
|
||||
</div>
|
||||
|
||||
</cnsl-meta-layout>
|
||||
</div>
|
@ -89,7 +89,7 @@ export class ProjectRoleCreateComponent implements OnInit, OnDestroy {
|
||||
.bulkAddProjectRoles(this.projectId, rolesToAdd)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.ROLESCREATED', true);
|
||||
this.router.navigate(['projects', this.projectId, 'roles']);
|
||||
this.router.navigate(['projects', this.projectId], { queryParams: { id: 'roles' } });
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
|
@ -4,9 +4,7 @@
|
||||
<button (click)="close()" mat-icon-button>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
<span class="abort">{{ 'PROJECT.PAGES.CREATE' | translate }}</span><span class="abort-2">Step
|
||||
{{ currentCreateStep }} of
|
||||
{{ createSteps }}</span>
|
||||
<span class="abort">{{ 'PROJECT.PAGES.CREATE' | translate }}</span>
|
||||
</div>
|
||||
|
||||
<div class="project-create-content">
|
||||
@ -15,13 +13,19 @@
|
||||
<div class="column">
|
||||
<cnsl-form-field class="formfield" hintLabel="The name is required!">
|
||||
<cnsl-label>{{ 'PROJECT.NAME' | translate }}</cnsl-label>
|
||||
<input cnslInput cdkFocusInitial autofocus [(ngModel)]="project.name"
|
||||
[ngModelOptions]="{ standalone: true }" />
|
||||
<input cnslInput cdkFocusInitial autofocus [(ngModel)]="project.name" [ngModelOptions]="{ standalone: true }" />
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
|
||||
<button color="primary" mat-raised-button class="continue-button" [disabled]="!project.name" cdkFocusInitial
|
||||
type="submit" [attr.data-e2e]="'continue-button'">
|
||||
<button
|
||||
color="primary"
|
||||
mat-raised-button
|
||||
class="continue-button"
|
||||
[disabled]="!project.name"
|
||||
cdkFocusInitial
|
||||
type="submit"
|
||||
[attr.data-e2e]="'continue-button'"
|
||||
>
|
||||
{{ 'ACTIONS.CONTINUE' | translate }}
|
||||
</button>
|
||||
</form>
|
||||
|
@ -11,12 +11,6 @@ h1 {
|
||||
font-size: 1.2rem;
|
||||
margin-left: 2rem;
|
||||
}
|
||||
|
||||
.abort-2 {
|
||||
font-size: 1.2rem;
|
||||
margin-left: 2rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.project-create-content {
|
||||
|
@ -28,9 +28,6 @@ export class ProjectCreateComponent {
|
||||
breadcrumbService.setBreadcrumb([bread]);
|
||||
}
|
||||
|
||||
public createSteps: number = 1;
|
||||
public currentCreateStep: number = 1;
|
||||
|
||||
public saveProject(): void {
|
||||
this.mgmtService
|
||||
.addProject(this.project)
|
||||
|
@ -1,30 +1,38 @@
|
||||
<div class="grid-main-container" *ngIf="projectType$ | async as type">
|
||||
<div class="loading-sp-wrapper">
|
||||
<mat-progress-spinner diameter="25" *ngIf="(loading$| async) === false" class="spinner" color="primary">
|
||||
</mat-progress-spinner>
|
||||
<div class="loading-sp-wrapper" *ngIf="loading$ | async">
|
||||
<mat-spinner diameter="25" class="spinner" color="primary"> </mat-spinner>
|
||||
</div>
|
||||
|
||||
<div class="owned-project-grid-container">
|
||||
<div class="item card" matRipple *ngFor="let item of selection.selected; index as i"
|
||||
<div
|
||||
class="item card"
|
||||
matRipple
|
||||
*ngFor="let item of selection.selected; index as i"
|
||||
(click)="navigateToProject(type, item, $event)"
|
||||
[ngClass]="{ inactive: item.state !== ProjectState.PROJECT_STATE_ACTIVE}">
|
||||
[ngClass]="{ inactive: item.state !== ProjectState.PROJECT_STATE_ACTIVE }"
|
||||
>
|
||||
<div class="text-part">
|
||||
<span *ngIf="item.details && item.details.changeDate"
|
||||
class="top cnsl-secondary-text">{{'PROJECT.PAGES.LASTMODIFIED' |
|
||||
translate}}
|
||||
{{ item.details.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}</span>
|
||||
<span *ngIf="item.details && item.details.changeDate" class="top cnsl-secondary-text"
|
||||
>{{ 'PROJECT.PAGES.LASTMODIFIED' | translate }}
|
||||
{{ item.details.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}</span
|
||||
>
|
||||
<div class="name-row">
|
||||
<span class="name" *ngIf="$any(item).name">{{ $any(item).name }}</span>
|
||||
<span class="name" *ngIf="$any(item).projectName">{{ $any(item).projectName }}</span>
|
||||
|
||||
<div class="state-dot"
|
||||
[ngClass]="{'active': item.state === ProjectState.PROJECT_STATE_ACTIVE, 'inactive': item.state === ProjectState.PROJECT_STATE_INACTIVE}">
|
||||
</div>
|
||||
<div
|
||||
class="state-dot"
|
||||
[ngClass]="{
|
||||
active: item.state === ProjectState.PROJECT_STATE_ACTIVE,
|
||||
inactive: item.state === ProjectState.PROJECT_STATE_INACTIVE
|
||||
}"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<span *ngIf="item.details && item.details.creationDate" class="created">{{'PROJECT.PAGES.CREATEDON' |
|
||||
translate}}
|
||||
{{ item.details.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}</span>
|
||||
<span *ngIf="item.details && item.details.creationDate" class="created"
|
||||
>{{ 'PROJECT.PAGES.CREATEDON' | translate }}
|
||||
{{ item.details.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}</span
|
||||
>
|
||||
<span class="fill-space"></span>
|
||||
</div>
|
||||
|
||||
@ -33,29 +41,37 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="owned-project-grid-container">
|
||||
<div class="item card" matRipple *ngFor="let item of notPinned; index as i"
|
||||
<div
|
||||
class="item card"
|
||||
matRipple
|
||||
*ngFor="let item of notPinned; index as i"
|
||||
(click)="navigateToProject(type, $any(item), $event)"
|
||||
[ngClass]="{ inactive: item.state !== ProjectState.PROJECT_STATE_ACTIVE}" [attr.data-e2e]="'grid-card'">
|
||||
[ngClass]="{ inactive: item.state !== ProjectState.PROJECT_STATE_ACTIVE }"
|
||||
[attr.data-e2e]="'grid-card'"
|
||||
>
|
||||
<div class="text-part">
|
||||
<span *ngIf="item.details && item.details.changeDate"
|
||||
class="top cnsl-secondary-text">{{'PROJECT.PAGES.LASTMODIFIED' | translate}}
|
||||
{{ item.details.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}</span>
|
||||
<span *ngIf="item.details && item.details.changeDate" class="top cnsl-secondary-text"
|
||||
>{{ 'PROJECT.PAGES.LASTMODIFIED' | translate }}
|
||||
{{ item.details.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}</span
|
||||
>
|
||||
<div class="name-row">
|
||||
<span class="name" *ngIf="$any(item).name">{{ $any(item).name }}</span>
|
||||
<span class="name" *ngIf="$any(item).projectName">{{ $any(item).projectName }}</span>
|
||||
|
||||
<div class="state-dot"
|
||||
[ngClass]="{'active': item.state === ProjectState.PROJECT_STATE_ACTIVE, 'inactive': item.state === ProjectState.PROJECT_STATE_INACTIVE}">
|
||||
</div>
|
||||
<div
|
||||
class="state-dot"
|
||||
[ngClass]="{
|
||||
active: item.state === ProjectState.PROJECT_STATE_ACTIVE,
|
||||
inactive: item.state === ProjectState.PROJECT_STATE_INACTIVE
|
||||
}"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<span class="owning-org" *ngIf="$any(item).projectOwnerName">{{ $any(item).projectOwnerName }}</span>
|
||||
<span *ngIf="item.details && item.details.creationDate"
|
||||
class="created cnsl-secondary-text">{{'PROJECT.PAGES.CREATEDON' |
|
||||
translate}}
|
||||
{{
|
||||
item.details.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'
|
||||
}}</span>
|
||||
<span *ngIf="item.details && item.details.creationDate" class="created cnsl-secondary-text"
|
||||
>{{ 'PROJECT.PAGES.CREATEDON' | translate }}
|
||||
{{ item.details.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}</span
|
||||
>
|
||||
<span class="fill-space"></span>
|
||||
</div>
|
||||
|
||||
@ -66,8 +82,8 @@
|
||||
</div>
|
||||
|
||||
<p class="n-items cnsl-secondary-text" *ngIf="(loading$ | async) === false && projectList.length === 0">
|
||||
{{'PROJECT.PAGES.NOITEMS' |
|
||||
translate}}</p>
|
||||
{{ 'PROJECT.PAGES.NOITEMS' | translate }}
|
||||
</p>
|
||||
|
||||
<ng-container *ngIf="type === ProjectType.PROJECTTYPE_OWNED">
|
||||
<ng-template cnslHasRole [hasRole]="['project.create']">
|
||||
@ -83,16 +99,27 @@
|
||||
</div>
|
||||
|
||||
<ng-template #deleteButton let-key="key">
|
||||
<button *ngIf="key.id !== zitadelProjectId" matTooltip="{{'ACTIONS.DELETE' | translate}}" color="warn"
|
||||
(click)="deleteProject($event, key)" class="delete-button" mat-icon-button
|
||||
[attr.data-e2e]="'delete-project-button'">
|
||||
<button
|
||||
*ngIf="key.id !== zitadelProjectId"
|
||||
matTooltip="{{ 'ACTIONS.DELETE' | translate }}"
|
||||
color="warn"
|
||||
(click)="deleteProject($event, key)"
|
||||
class="delete-button"
|
||||
mat-icon-button
|
||||
[attr.data-e2e]="'delete-project-button'"
|
||||
>
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #toggleButton let-key="key">
|
||||
<button matTooltip="{{'ACTIONS.PIN' | translate}}" [ngClass]="{ selected: selection.isSelected(key)}"
|
||||
(click)="toggle(key,$event)" class="edit-button" mat-icon-button>
|
||||
<button
|
||||
matTooltip="{{ 'ACTIONS.PIN' | translate }}"
|
||||
[ngClass]="{ selected: selection.isSelected(key) }"
|
||||
(click)="toggle(key, $event)"
|
||||
class="edit-button"
|
||||
mat-icon-button
|
||||
>
|
||||
<mat-icon *ngIf="selection.isSelected(key)" svgIcon="mdi_pin"></mat-icon>
|
||||
<mat-icon svgIcon="mdi_pin_outline" *ngIf="!selection.isSelected(key)"></mat-icon>
|
||||
</button>
|
||||
|
@ -25,17 +25,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
.spinner {
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.grid-main-container {
|
||||
position: relative;
|
||||
|
||||
.loading-sp-wrapper {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: block;
|
||||
margin: 1rem 0.5rem 0 0.5rem;
|
||||
}
|
||||
|
||||
.owned-project-grid-container {
|
||||
|
@ -144,7 +144,6 @@ export class ProjectGridComponent implements OnInit, OnDestroy {
|
||||
this.loadingSubject.next(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
this.toast.showError(error);
|
||||
this.loadingSubject.next(false);
|
||||
});
|
||||
|
@ -10,12 +10,20 @@
|
||||
|
||||
<div class="projects-controls">
|
||||
<div class="project-type-actions">
|
||||
<button class="type-button" [ngClass]="{'active': (projectType$ | async) === ProjectType.PROJECTTYPE_OWNED}"
|
||||
(click)="setType(ProjectType.PROJECTTYPE_OWNED)">{{'PROJECT.PAGES.TYPE.OWNED' | translate}}
|
||||
({{((mgmtService?.ownedProjectsCount | async) ?? 0)}})</button>
|
||||
<button class="type-button" [ngClass]="{'active': (projectType$ | async) === ProjectType.PROJECTTYPE_GRANTED}"
|
||||
(click)="setType(ProjectType.PROJECTTYPE_GRANTED)">{{'PROJECT.PAGES.TYPE.GRANTED' | translate}}
|
||||
({{((mgmtService?.grantedProjectsCount | async) ?? 0)}})</button>
|
||||
<button
|
||||
class="type-button"
|
||||
[ngClass]="{ active: (projectType$ | async) === ProjectType.PROJECTTYPE_OWNED }"
|
||||
(click)="setType(ProjectType.PROJECTTYPE_OWNED)"
|
||||
>
|
||||
{{ 'PROJECT.PAGES.TYPE.OWNED' | translate }} ({{ (mgmtService?.ownedProjectsCount | async) ?? 0 }})
|
||||
</button>
|
||||
<button
|
||||
class="type-button"
|
||||
[ngClass]="{ active: (projectType$ | async) === ProjectType.PROJECTTYPE_GRANTED }"
|
||||
(click)="setType(ProjectType.PROJECTTYPE_GRANTED)"
|
||||
>
|
||||
{{ 'PROJECT.PAGES.TYPE.GRANTED' | translate }} ({{ (mgmtService?.grantedProjectsCount | async) ?? 0 }})
|
||||
</button>
|
||||
</div>
|
||||
<span class="fill-space"></span>
|
||||
<button class="grid-btn" (click)="grid = !grid" mat-icon-button [attr.data-e2e]="'toggle-grid'">
|
||||
@ -24,8 +32,12 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<cnsl-project-grid *ngIf="grid" [projectType$]="projectType$" [zitadelProjectId]="zitadelProjectId"
|
||||
(emitAddProject)="addProject()">
|
||||
<cnsl-project-grid
|
||||
*ngIf="grid"
|
||||
[projectType$]="projectType$"
|
||||
[zitadelProjectId]="zitadelProjectId"
|
||||
(emitAddProject)="addProject()"
|
||||
>
|
||||
</cnsl-project-grid>
|
||||
|
||||
<cnsl-project-list *ngIf="!grid" [projectType$]="projectType$" [zitadelProjectId]="zitadelProjectId">
|
||||
|
@ -53,6 +53,7 @@ export class ProjectsComponent {
|
||||
type:
|
||||
type === ProjectType.PROJECTTYPE_OWNED ? 'owned' : type === ProjectType.PROJECTTYPE_GRANTED ? 'granted' : 'owned',
|
||||
},
|
||||
replaceUrl: true,
|
||||
queryParamsHandling: 'merge',
|
||||
skipLocationChange: false,
|
||||
});
|
||||
|
@ -4,32 +4,35 @@
|
||||
<button (click)="close()" mat-icon-button>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
<h1 class="abort">{{ 'GRANTS.CREATE.TITLE' | translate }}</h1><span class="abort-2">Step
|
||||
{{ currentCreateStep }} of
|
||||
{{ STEPS }}</span>
|
||||
<h1 class="abort">{{ 'GRANTS.CREATE.TITLE' | translate }}</h1>
|
||||
<span class="abort-2">Step {{ currentCreateStep }} of {{ STEPS }}</span>
|
||||
</div>
|
||||
|
||||
<div class="user-grant-create-content">
|
||||
<ng-container *ngIf="currentCreateStep === 1">
|
||||
<p class="user-grant-create-desc cnsl-secondary-text">
|
||||
{{ 'PROJECT.GRANT.CREATE.ORG_DESCRIPTION' | translate: org }}
|
||||
<br>
|
||||
<br />
|
||||
{{ 'PROJECT.GRANT.CREATE.ORG_DESCRIPTION_DESC' | translate }}
|
||||
</p>
|
||||
|
||||
<ng-container>
|
||||
<h2>{{ 'PROJECT.GRANT.CREATE.SEL_USER' | translate }}</h2>
|
||||
|
||||
<cnsl-search-user-autocomplete [editState]="context !== UserGrantContext.USER" class="block"
|
||||
[users]="user ? [user] : []" (selectionChanged)="selectUsers($event)" [target]="UserTarget.SELF">
|
||||
<cnsl-search-user-autocomplete
|
||||
[editState]="context !== UserGrantContext.USER"
|
||||
class="block"
|
||||
[users]="user ? [user] : []"
|
||||
(selectionChanged)="selectUsers($event)"
|
||||
[target]="UserTarget.SELF"
|
||||
>
|
||||
</cnsl-search-user-autocomplete>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="context && (context === UserGrantContext.USER || context === UserGrantContext.NONE)">
|
||||
<h2 class="project-search">{{ 'PROJECT.GRANT.CREATE.SEL_PROJECT' | translate }}</h2>
|
||||
|
||||
<cnsl-search-project-autocomplete class="block"
|
||||
(selectionChanged)="selectProject($event.project, $event.type)">
|
||||
<cnsl-search-project-autocomplete class="block" (selectionChanged)="selectProject($event.project, $event.type)">
|
||||
</cnsl-search-project-autocomplete>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
@ -38,19 +41,27 @@
|
||||
<h1>{{ 'PROJECT.GRANT.CREATE.SEL_ROLES' | translate }}</h1>
|
||||
|
||||
<cnsl-card>
|
||||
{{ $any(project)?.grantId }}
|
||||
<cnsl-project-roles-table
|
||||
[displayedColumns]="['select', 'key', 'displayname', 'group', 'creationDate', 'changeDate']"
|
||||
(changedSelection)="selectRoles($event)"
|
||||
[projectId]="project?.id ? project.id : grantedProject?.projectId ? grantedProject.projectId : ''"
|
||||
[grantId]="$any(project)?.grantId ? $any(project)?.grantId : ''">
|
||||
[grantId]="$any(grantedProject)?.grantId ? $any(grantedProject)?.grantId : ''"
|
||||
>
|
||||
</cnsl-project-roles-table>
|
||||
</cnsl-card>
|
||||
</ng-container>
|
||||
|
||||
<div class="btn-container">
|
||||
<ng-container *ngIf="currentCreateStep === 1">
|
||||
<button [disabled]="!org || !(project?.id || grantedProject?.projectId) || userIds.length < 1"
|
||||
(click)="next()" color="primary" mat-raised-button class="big-button" cdkFocusInitial>
|
||||
<button
|
||||
[disabled]="!org || !(project?.id || grantedProject?.projectId) || userIds.length < 1"
|
||||
(click)="next()"
|
||||
color="primary"
|
||||
mat-raised-button
|
||||
class="big-button"
|
||||
cdkFocusInitial
|
||||
>
|
||||
{{ 'ACTIONS.CONTINUE' | translate }}
|
||||
</button>
|
||||
</ng-container>
|
||||
|
@ -4,8 +4,7 @@
|
||||
<a [routerLink]="['/users']" mat-icon-button>
|
||||
<mat-icon>close</mat-icon>
|
||||
</a>
|
||||
<h1 class="abort">{{ 'USER.CREATE.TITLE' | translate }}</h1><span class="abort-2">Step
|
||||
1 of 1</span>
|
||||
<h1 class="abort">{{ 'USER.CREATE.TITLE' | translate }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="user-create-main-content">
|
||||
@ -13,8 +12,10 @@
|
||||
|
||||
<form *ngIf="userForm" [formGroup]="userForm" (ngSubmit)="createUser()" class="user-create-form">
|
||||
<div class="user-create-content">
|
||||
<p class="user-create-section cnsl-secondary-text">{{ 'USER.CREATE.NAMEANDEMAILSECTION' | translate }}</p>
|
||||
<cnsl-form-field class="formfield">
|
||||
<p class="user-create-section">{{ 'USER.CREATE.NAMEANDEMAILSECTION' | translate }}</p>
|
||||
|
||||
<div class="user-create-grid">
|
||||
<cnsl-form-field>
|
||||
<cnsl-label>{{ 'USER.PROFILE.EMAIL' | translate }}*</cnsl-label>
|
||||
<input cnslInput matRipple formControlName="email" required />
|
||||
<span cnslError *ngIf="email?.invalid && !email?.errors?.required">
|
||||
@ -24,10 +25,14 @@
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</span>
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-form-field>
|
||||
<cnsl-label>{{ 'USER.PROFILE.USERNAME' | translate }}*</cnsl-label>
|
||||
<input cnslInput formControlName="userName" required
|
||||
[ngStyle]="{'padding-right': suffixPadding ? suffixPadding : '10px'}" />
|
||||
<input
|
||||
cnslInput
|
||||
formControlName="userName"
|
||||
required
|
||||
[ngStyle]="{ 'padding-right': suffixPadding ? suffixPadding : '10px' }"
|
||||
/>
|
||||
<span #suffix *ngIf="envSuffixLabel" cnslSuffix>{{ envSuffixLabel }}</span>
|
||||
|
||||
<span cnslError *ngIf="userName?.invalid && userName?.errors?.required">
|
||||
@ -37,29 +42,29 @@
|
||||
{{ 'USER.VALIDATION.NOEMAIL' | translate }}
|
||||
</span>
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
<div class="user-create-content">
|
||||
<cnsl-form-field class="formfield">
|
||||
|
||||
<cnsl-form-field>
|
||||
<cnsl-label>{{ 'USER.PROFILE.FIRSTNAME' | translate }}*</cnsl-label>
|
||||
<input cnslInput formControlName="firstName" required />
|
||||
<span cnslError *ngIf="firstName?.invalid && firstName?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</span>
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-form-field>
|
||||
<cnsl-label>{{ 'USER.PROFILE.LASTNAME' | translate }}*</cnsl-label>
|
||||
<input cnslInput formControlName="lastName" required />
|
||||
<span cnslError *ngIf="lastName?.invalid && lastName?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</span>
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-form-field>
|
||||
<cnsl-label>{{ 'USER.PROFILE.NICKNAME' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="nickName" />
|
||||
<span cnslError *ngIf="nickName?.invalid && nickName?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</span>
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
|
||||
<div class="email-is-verified">
|
||||
<mat-checkbox class="block-checkbox" formControlName="isVerified">
|
||||
@ -77,20 +82,25 @@
|
||||
<cnsl-password-complexity-view class="complexity-view" [policy]="this.policy" [password]="password">
|
||||
</cnsl-password-complexity-view>
|
||||
|
||||
<form [formGroup]="pwdForm" class="user-create-pwd-form">
|
||||
<cnsl-form-field class="pwd-field" *ngIf="password" appearance="outline">
|
||||
<form [formGroup]="pwdForm">
|
||||
<div class="user-create-grid">
|
||||
<cnsl-form-field *ngIf="password">
|
||||
<cnsl-label>{{ 'USER.PASSWORD.NEWINITIAL' | translate }}</cnsl-label>
|
||||
<input cnslInput autocomplete="off" name="firstpassword" formControlName="password" type="password" />
|
||||
|
||||
<span cnslError *ngIf="password?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</span>
|
||||
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field class="pwd-field" *ngIf="confirmPassword" appearance="outline">
|
||||
<cnsl-form-field *ngIf="confirmPassword">
|
||||
<cnsl-label>{{ 'USER.PASSWORD.CONFIRMINITIAL' | translate }}</cnsl-label>
|
||||
<input cnslInput autocomplete="off" name="confirmPassword" formControlName="confirmPassword"
|
||||
type="password" />
|
||||
<input
|
||||
cnslInput
|
||||
autocomplete="off"
|
||||
name="confirmPassword"
|
||||
formControlName="confirmPassword"
|
||||
type="password"
|
||||
/>
|
||||
|
||||
<span cnslError *ngIf="confirmPassword?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
@ -99,12 +109,14 @@
|
||||
{{ 'USER.PASSWORD.NOTEQUAL' | translate }}
|
||||
</span>
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<p class="user-create-section cnsl-secondary-text">{{ 'USER.CREATE.GENDERLANGSECTION' | translate }}</p>
|
||||
<p class="user-create-section">{{ 'USER.CREATE.GENDERLANGSECTION' | translate }}</p>
|
||||
|
||||
<cnsl-form-field class="formfield">
|
||||
<div class="user-create-grid">
|
||||
<cnsl-form-field>
|
||||
<cnsl-label>{{ 'USER.PROFILE.GENDER' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="gender">
|
||||
<mat-option *ngFor="let gender of genders" [value]="gender">
|
||||
@ -115,7 +127,7 @@
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</span>
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-form-field>
|
||||
<cnsl-label>{{ 'USER.PROFILE.PREFERRED_LANGUAGE' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="preferredLanguage">
|
||||
<mat-option *ngFor="let language of languages" [value]="language">
|
||||
@ -126,10 +138,11 @@
|
||||
</span>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
|
||||
<p class="user-create-section cnsl-secondary-text">{{ 'USER.CREATE.ADDRESSANDPHONESECTION' | translate }}</p>
|
||||
<p class="user-create-section">{{ 'USER.CREATE.ADDRESSANDPHONESECTION' | translate }}</p>
|
||||
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-form-field>
|
||||
<cnsl-label>{{ 'USER.PROFILE.PHONE' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="phone" />
|
||||
<span cnslError *ngIf="phone?.invalid && phone?.errors?.required">
|
||||
@ -138,10 +151,15 @@
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
<div class="user-create-btn-container">
|
||||
<button [attr.data-e2e]="'create-button'" color="primary"
|
||||
[disabled]="userForm.invalid || (this.usePassword && this.pwdForm.invalid)" type="submit"
|
||||
mat-raised-button>{{ 'ACTIONS.CREATE' |
|
||||
translate }}</button>
|
||||
<button
|
||||
[attr.data-e2e]="'create-button'"
|
||||
color="primary"
|
||||
[disabled]="userForm.invalid || (this.usePassword && this.pwdForm.invalid)"
|
||||
type="submit"
|
||||
mat-raised-button
|
||||
>
|
||||
{{ 'ACTIONS.CREATE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -7,20 +7,47 @@
|
||||
font-size: 1.2rem;
|
||||
margin-left: 2rem;
|
||||
}
|
||||
|
||||
.abort-2 {
|
||||
font-size: 1.2rem;
|
||||
margin-left: 2rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.user-create-main-content {
|
||||
padding-left: 4.5rem;
|
||||
max-width: 35rem;
|
||||
|
||||
@media only screen and (max-width: 500px) {
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
|
||||
.user-create-form {
|
||||
padding-top: 1rem;
|
||||
.user-create-content {
|
||||
.user-create-section {
|
||||
padding: 1rem 0 0 0;
|
||||
flex-basis: 100%;
|
||||
font-size: 14px;
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.user-create-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
column-gap: 1rem;
|
||||
|
||||
@media only screen and (max-width: 500px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.email-is-verified,
|
||||
.use-password-block {
|
||||
flex-basis: 100%;
|
||||
margin-top: 1.5rem;
|
||||
|
||||
.block-checkbox {
|
||||
display: block;
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-create-btn-container {
|
||||
button {
|
||||
@ -32,49 +59,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
.user-create-content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
margin: 0 -0.5rem;
|
||||
|
||||
.user-create-section {
|
||||
padding: 0.5rem;
|
||||
flex-basis: 100%;
|
||||
font-size: 0.9rem;
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.formfield {
|
||||
flex: 1 0 33%;
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
|
||||
.email-is-verified,
|
||||
.use-password-block {
|
||||
margin: 0 0.5rem;
|
||||
flex-basis: 100%;
|
||||
margin-top: 1.5rem;
|
||||
|
||||
.block-checkbox {
|
||||
display: block;
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pwd-section {
|
||||
margin: 0 0.5rem;
|
||||
|
||||
.section {
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
|
||||
.user-create-pwd-form {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-gap: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ export class UserCreateComponent implements OnDestroy {
|
||||
this.loading = true;
|
||||
this.loadOrg();
|
||||
this.mgmtService
|
||||
.getOrgIAMPolicy()
|
||||
.getDomainPolicy()
|
||||
.then((resp) => {
|
||||
if (resp.policy?.userLoginMustBeDomain) {
|
||||
this.userLoginMustBeDomain = resp.policy.userLoginMustBeDomain;
|
||||
|
@ -40,7 +40,9 @@
|
||||
<mat-progress-spinner diameter="25" color="primary" mode="indeterminate"></mat-progress-spinner>
|
||||
</div>
|
||||
|
||||
<p class="no-user-error" *ngIf="!loading && !user">{{ 'USER.PAGES.NOUSER' | translate }}</p>
|
||||
<div *ngIf="!loading && !user" class="max-width-container">
|
||||
<p class="no-user-error">{{ 'USER.PAGES.NOUSER' | translate }}</p>
|
||||
</div>
|
||||
|
||||
<div class="max-width-container" *ngIf="user && (['user.write$', 'user.write:' + user.id] | hasRole) as canWrite$">
|
||||
<cnsl-meta-layout>
|
||||
|
@ -76,6 +76,7 @@ export class UserTableComponent implements OnInit {
|
||||
public ActionKeysType: any = ActionKeysType;
|
||||
public filterOpen: boolean = false;
|
||||
|
||||
private searchQueries: SearchQuery[] = [];
|
||||
constructor(
|
||||
private router: Router,
|
||||
public translate: TranslateService,
|
||||
@ -109,6 +110,7 @@ export class UserTableComponent implements OnInit {
|
||||
queryParams: {
|
||||
type: type === Type.TYPE_HUMAN ? 'human' : type === Type.TYPE_MACHINE ? 'machine' : 'human',
|
||||
},
|
||||
replaceUrl: true,
|
||||
queryParamsHandling: 'merge',
|
||||
skipLocationChange: false,
|
||||
});
|
||||
@ -225,12 +227,10 @@ export class UserTableComponent implements OnInit {
|
||||
}
|
||||
|
||||
public refreshPage(): void {
|
||||
this.getData(this.paginator.pageSize, this.paginator.pageIndex * this.paginator.pageSize, this.type);
|
||||
this.getData(this.paginator.pageSize, this.paginator.pageIndex * this.paginator.pageSize, this.type, this.searchQueries);
|
||||
}
|
||||
|
||||
public sortChange(sortState: Sort) {
|
||||
console.log(sortState.active, sortState.direction);
|
||||
|
||||
if (sortState.direction && sortState.active) {
|
||||
this._liveAnnouncer.announce(`Sorted ${sortState.direction} ending`);
|
||||
this.refreshPage();
|
||||
@ -241,6 +241,7 @@ export class UserTableComponent implements OnInit {
|
||||
|
||||
public applySearchQuery(searchQueries: SearchQuery[]): void {
|
||||
this.selection.clear();
|
||||
this.searchQueries = searchQueries;
|
||||
this.getData(
|
||||
this.paginator ? this.paginator.pageSize : this.INITIAL_PAGE_SIZE,
|
||||
this.paginator ? this.paginator.pageIndex * this.paginator.pageSize : 0,
|
||||
|
@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
|
||||
import {
|
||||
ActivateLabelPolicyRequest,
|
||||
ActivateLabelPolicyResponse,
|
||||
AddCustomOrgIAMPolicyRequest,
|
||||
AddCustomDomainPolicyRequest,
|
||||
AddCustomOrgIAMPolicyResponse,
|
||||
AddIAMMemberRequest,
|
||||
AddIAMMemberResponse,
|
||||
@ -23,12 +23,12 @@ import {
|
||||
DeactivateIDPResponse,
|
||||
GetCustomDomainClaimedMessageTextRequest,
|
||||
GetCustomDomainClaimedMessageTextResponse,
|
||||
GetCustomDomainPolicyRequest,
|
||||
GetCustomDomainPolicyResponse,
|
||||
GetCustomInitMessageTextRequest,
|
||||
GetCustomInitMessageTextResponse,
|
||||
GetCustomLoginTextsRequest,
|
||||
GetCustomLoginTextsResponse,
|
||||
GetCustomOrgIAMPolicyRequest,
|
||||
GetCustomOrgIAMPolicyResponse,
|
||||
GetCustomPasswordlessRegistrationMessageTextRequest,
|
||||
GetCustomPasswordlessRegistrationMessageTextResponse,
|
||||
GetCustomPasswordResetMessageTextRequest,
|
||||
@ -53,6 +53,8 @@ import {
|
||||
GetDefaultVerifyEmailMessageTextResponse,
|
||||
GetDefaultVerifyPhoneMessageTextRequest,
|
||||
GetDefaultVerifyPhoneMessageTextResponse,
|
||||
GetDomainPolicyRequest,
|
||||
GetDomainPolicyResponse,
|
||||
GetFileSystemNotificationProviderRequest,
|
||||
GetFileSystemNotificationProviderResponse,
|
||||
GetIDPByIDRequest,
|
||||
@ -67,8 +69,6 @@ import {
|
||||
GetLogNotificationProviderResponse,
|
||||
GetOIDCSettingsRequest,
|
||||
GetOIDCSettingsResponse,
|
||||
GetOrgIAMPolicyRequest,
|
||||
GetOrgIAMPolicyResponse,
|
||||
GetPasswordAgePolicyRequest,
|
||||
GetPasswordAgePolicyResponse,
|
||||
GetPasswordComplexityPolicyRequest,
|
||||
@ -130,10 +130,10 @@ import {
|
||||
RemoveMultiFactorFromLoginPolicyResponse,
|
||||
RemoveSecondFactorFromLoginPolicyRequest,
|
||||
RemoveSecondFactorFromLoginPolicyResponse,
|
||||
ResetCustomDomainPolicyToDefaultRequest,
|
||||
ResetCustomDomainPolicyToDefaultResponse,
|
||||
ResetCustomLoginTextsToDefaultRequest,
|
||||
ResetCustomLoginTextsToDefaultResponse,
|
||||
ResetCustomOrgIAMPolicyToDefaultRequest,
|
||||
ResetCustomOrgIAMPolicyToDefaultResponse,
|
||||
SetCustomLoginTextsRequest,
|
||||
SetCustomLoginTextsResponse,
|
||||
SetDefaultDomainClaimedMessageTextRequest,
|
||||
@ -152,8 +152,10 @@ import {
|
||||
SetDefaultVerifyPhoneMessageTextResponse,
|
||||
SetUpOrgRequest,
|
||||
SetUpOrgResponse,
|
||||
UpdateCustomOrgIAMPolicyRequest,
|
||||
UpdateCustomOrgIAMPolicyResponse,
|
||||
UpdateCustomDomainPolicyRequest,
|
||||
UpdateCustomDomainPolicyResponse,
|
||||
UpdateDomainPolicyRequest,
|
||||
UpdateDomainPolicyResponse,
|
||||
UpdateIAMMemberRequest,
|
||||
UpdateIAMMemberResponse,
|
||||
UpdateIDPJWTConfigRequest,
|
||||
@ -170,8 +172,6 @@ import {
|
||||
UpdateLoginPolicyResponse,
|
||||
UpdateOIDCSettingsRequest,
|
||||
UpdateOIDCSettingsResponse,
|
||||
UpdateOrgIAMPolicyRequest,
|
||||
UpdateOrgIAMPolicyResponse,
|
||||
UpdatePasswordAgePolicyRequest,
|
||||
UpdatePasswordAgePolicyResponse,
|
||||
UpdatePasswordComplexityPolicyRequest,
|
||||
@ -599,52 +599,35 @@ export class AdminService {
|
||||
return this.grpcService.admin.updateSecretGenerator(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
/* org iam */
|
||||
/* org domain policy */
|
||||
|
||||
public getCustomOrgIAMPolicy(orgId: string): Promise<GetCustomOrgIAMPolicyResponse.AsObject> {
|
||||
const req = new GetCustomOrgIAMPolicyRequest();
|
||||
public getDomainPolicy(): Promise<GetDomainPolicyResponse.AsObject> {
|
||||
const req = new GetDomainPolicyRequest();
|
||||
return this.grpcService.admin.getDomainPolicy(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public updateDomainPolicy(req: UpdateDomainPolicyRequest): Promise<UpdateDomainPolicyResponse.AsObject> {
|
||||
return this.grpcService.admin.updateDomainPolicy(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getCustomDomainPolicy(orgId: string): Promise<GetCustomDomainPolicyResponse.AsObject> {
|
||||
const req = new GetCustomDomainPolicyRequest();
|
||||
req.setOrgId(orgId);
|
||||
return this.grpcService.admin.getCustomOrgIAMPolicy(req, null).then((resp) => resp.toObject());
|
||||
return this.grpcService.admin.getCustomDomainPolicy(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public addCustomOrgIAMPolicy(
|
||||
orgId: string,
|
||||
userLoginMustBeDomain: boolean,
|
||||
): Promise<AddCustomOrgIAMPolicyResponse.AsObject> {
|
||||
const req = new AddCustomOrgIAMPolicyRequest();
|
||||
public addCustomDomainPolicy(req: AddCustomDomainPolicyRequest): Promise<AddCustomOrgIAMPolicyResponse.AsObject> {
|
||||
return this.grpcService.admin.addCustomDomainPolicy(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public updateCustomDomainPolicy(req: UpdateCustomDomainPolicyRequest): Promise<UpdateCustomDomainPolicyResponse.AsObject> {
|
||||
return this.grpcService.admin.updateCustomDomainPolicy(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public resetCustomDomainPolicyToDefault(orgId: string): Promise<ResetCustomDomainPolicyToDefaultResponse.AsObject> {
|
||||
const req = new ResetCustomDomainPolicyToDefaultRequest();
|
||||
req.setOrgId(orgId);
|
||||
req.setUserLoginMustBeDomain(userLoginMustBeDomain);
|
||||
|
||||
return this.grpcService.admin.addCustomOrgIAMPolicy(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public updateCustomOrgIAMPolicy(
|
||||
orgId: string,
|
||||
userLoginMustBeDomain: boolean,
|
||||
): Promise<UpdateCustomOrgIAMPolicyResponse.AsObject> {
|
||||
const req = new UpdateCustomOrgIAMPolicyRequest();
|
||||
req.setOrgId(orgId);
|
||||
req.setUserLoginMustBeDomain(userLoginMustBeDomain);
|
||||
return this.grpcService.admin.updateCustomOrgIAMPolicy(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public resetCustomOrgIAMPolicyToDefault(orgId: string): Promise<ResetCustomOrgIAMPolicyToDefaultResponse.AsObject> {
|
||||
const req = new ResetCustomOrgIAMPolicyToDefaultRequest();
|
||||
req.setOrgId(orgId);
|
||||
return this.grpcService.admin.resetCustomOrgIAMPolicyToDefault(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
/* admin iam */
|
||||
|
||||
public getOrgIAMPolicy(): Promise<GetOrgIAMPolicyResponse.AsObject> {
|
||||
const req = new GetOrgIAMPolicyRequest();
|
||||
return this.grpcService.admin.getOrgIAMPolicy(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public updateOrgIAMPolicy(userLoginMustBeDomain: boolean): Promise<UpdateOrgIAMPolicyResponse.AsObject> {
|
||||
const req = new UpdateOrgIAMPolicyRequest();
|
||||
req.setUserLoginMustBeDomain(userLoginMustBeDomain);
|
||||
return this.grpcService.admin.updateOrgIAMPolicy(req, null).then((resp) => resp.toObject());
|
||||
return this.grpcService.admin.resetCustomDomainPolicyToDefault(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
/* policies end */
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { SortDirection } from '@angular/material/sort';
|
||||
import { OAuthService } from 'angular-oauth2-oidc';
|
||||
import { BehaviorSubject, from, merge, Observable, of, Subject } from 'rxjs';
|
||||
import { catchError, filter, finalize, map, mergeMap, switchMap, take, timeout } from 'rxjs/operators';
|
||||
@ -83,7 +84,7 @@ import {
|
||||
} from '../proto/generated/zitadel/auth_pb';
|
||||
import { ChangeQuery } from '../proto/generated/zitadel/change_pb';
|
||||
import { ListQuery } from '../proto/generated/zitadel/object_pb';
|
||||
import { Org, OrgQuery } from '../proto/generated/zitadel/org_pb';
|
||||
import { Org, OrgFieldName, OrgQuery } from '../proto/generated/zitadel/org_pb';
|
||||
import { Gender, MembershipQuery, User, WebAuthNVerification } from '../proto/generated/zitadel/user_pb';
|
||||
import { GrpcService } from './grpc.service';
|
||||
import { StorageKey, StorageLocation, StorageService } from './storage.service';
|
||||
@ -262,6 +263,8 @@ export class GrpcAuthService {
|
||||
limit?: number,
|
||||
offset?: number,
|
||||
queryList?: OrgQuery[],
|
||||
sortingColumn?: OrgFieldName,
|
||||
sortingDirection?: SortDirection,
|
||||
): Promise<ListMyProjectOrgsResponse.AsObject> {
|
||||
const req = new ListMyProjectOrgsRequest();
|
||||
const query = new ListQuery();
|
||||
@ -274,6 +277,9 @@ export class GrpcAuthService {
|
||||
if (queryList) {
|
||||
req.setQueriesList(queryList);
|
||||
}
|
||||
// if (sortingColumn) {
|
||||
// req.setSortingColumn(sortingColumn);
|
||||
// }
|
||||
|
||||
req.setQuery(query);
|
||||
|
||||
|
@ -131,6 +131,8 @@ import {
|
||||
GetDefaultVerifyEmailMessageTextResponse,
|
||||
GetDefaultVerifyPhoneMessageTextRequest,
|
||||
GetDefaultVerifyPhoneMessageTextResponse,
|
||||
GetDomainPolicyRequest,
|
||||
GetDomainPolicyResponse,
|
||||
GetFlowRequest,
|
||||
GetFlowResponse,
|
||||
GetGrantedProjectByIDRequest,
|
||||
@ -155,8 +157,6 @@ import {
|
||||
GetOIDCInformationResponse,
|
||||
GetOrgByDomainGlobalRequest,
|
||||
GetOrgByDomainGlobalResponse,
|
||||
GetOrgIAMPolicyRequest,
|
||||
GetOrgIAMPolicyResponse,
|
||||
GetOrgIDPByIDRequest,
|
||||
GetOrgIDPByIDResponse,
|
||||
GetPasswordAgePolicyRequest,
|
||||
@ -1225,9 +1225,10 @@ export class ManagementService {
|
||||
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());
|
||||
|
||||
public getDomainPolicy(): Promise<GetDomainPolicyResponse.AsObject> {
|
||||
const req = new GetDomainPolicyRequest();
|
||||
return this.grpcService.mgmt.getDomainPolicy(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getPasswordAgePolicy(): Promise<GetPasswordAgePolicyResponse.AsObject> {
|
||||
|
@ -818,6 +818,7 @@
|
||||
"NOTIFICATIONS": "Benachrichtigungen",
|
||||
"MESSAGETEXTS": "Benachrichtigungstexte",
|
||||
"IDP": "Identity Provider",
|
||||
"DOMAIN": "Domain Einstellungen",
|
||||
"LOGINTEXTS": "Login Interface Texte",
|
||||
"BRANDING": "Branding",
|
||||
"PRIVACYPOLICY": "Datenschutzrichtlinie",
|
||||
@ -827,6 +828,7 @@
|
||||
"GROUPS": {
|
||||
"NOTIFICATIONS": "Benachrichtigungen",
|
||||
"LOGIN": "Login und Zugriff",
|
||||
"DOMAIN": "Domain",
|
||||
"TEXTS": "Texte und Sprachen",
|
||||
"APPEARANCE": "Erscheinungsbild",
|
||||
"OTHER": "Anderes"
|
||||
@ -846,6 +848,8 @@
|
||||
"HOST": "Host",
|
||||
"USER": "Benutzer",
|
||||
"PASSWORD": "Passwort",
|
||||
"SETPASSWORD": "SMTP Passwort setzen",
|
||||
"PASSWORDSET": "SMTP Passwort erfolgrech gesetzt.",
|
||||
"TLS": "Transport Layer Security (TLS)",
|
||||
"SAVED": "Erfolgreich gespeichert.",
|
||||
"REQUIREDWARN": "Damit Mails von Ihrer Domain verschickt werden können, müssen Sie Ihre SMTP Einstellungen konfigurieren."
|
||||
@ -949,9 +953,8 @@
|
||||
"TITLE": "Passwortsperre",
|
||||
"DESCRIPTION": "Lege eine maximale Anzahl an Passwordwiederholungen fest, nachdem Accounts gesperrt werden sollen."
|
||||
},
|
||||
"IAM_POLICY": {
|
||||
"TITLE": "Zugangseinstellungen IAM",
|
||||
"DESCRIPTION": "Definiere die Zugangseistellungen für Benutzer."
|
||||
"DOMAIN_POLICY": {
|
||||
"TITLE": "Domain Einstellungen"
|
||||
},
|
||||
"PRIVATELABELING_POLICY": {
|
||||
"TITLE": "Branding",
|
||||
@ -965,6 +968,7 @@
|
||||
"DESCRIPTIONCREATEADMIN": "Nutzer können sich mit den verfügbaren Idps authentifizieren.",
|
||||
"DESCRIPTIONCREATEMGMT": "Nutzer können sich mit den verfügbaren Idps authentifizieren. Achtung: Es kann zwischen System- und organisationsspezifischen Providern gewählt werden.",
|
||||
"ADVANCED": "Erweitert",
|
||||
"LIFETIMEDURATIONS": "Login Lifetimes",
|
||||
"SAVED": "Erfolgreich gespeichert."
|
||||
},
|
||||
"PRIVACY_POLICY": {
|
||||
@ -1080,7 +1084,9 @@
|
||||
"MAXATTEMPTS": "Maximale Anzahl an Versuchen",
|
||||
"EXPIREWARNDAYS": "Ablauf Warnung nach Tagen",
|
||||
"MAXAGEDAYS": "Maximale Gültigkeit in Tagen",
|
||||
"USERLOGINMUSTBEDOMAIN": "Benutzer-Login muss eine Domain sein",
|
||||
"USERLOGINMUSTBEDOMAIN": "Benutzer Loginname muss die Domain der Organisation beinhalten",
|
||||
"VALIDATEORGDOMAINS": "Org Domains validieren",
|
||||
"SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN": "SMTP Sender Addresse entspricht Instanzdomain",
|
||||
"ALLOWUSERNAMEPASSWORD": "Benutzername Passwort erlaubt",
|
||||
"ALLOWEXTERNALIDP": "Externer IDP erlaubt",
|
||||
"ALLOWREGISTER": "Registrieren erlaubt",
|
||||
@ -1097,7 +1103,13 @@
|
||||
"DEFAULTREDIRECTURI": "Default Redirect URI",
|
||||
"DEFAULTREDIRECTURI_DESC": "Definiert, wohin der Benutzer umgeleitet wird, wenn die Anmeldung ohne App-Kontext gestartet wurde (z. B. von Mail)",
|
||||
"ERRORMSGPOPUP": "Fehler als Dialog Fenster",
|
||||
"DISABLEWATERMARK": "Wasserzeichen ausblenden"
|
||||
"DISABLEWATERMARK": "Wasserzeichen ausblenden",
|
||||
"PASSWORDCHECKLIFETIME": "Passwort Check Lifetime",
|
||||
"EXTERNALLOGINCHECKLIFETIME": "Externer Login Check Lifetime",
|
||||
"MFAINITSKIPLIFETIME": "Multifaktor Init Lifetime",
|
||||
"SECONDFACTORCHECKLIFETIME": "Zweitfaktor Check Lifetime",
|
||||
"MULTIFACTORCHECKLIFETIME": "Multifaktor Check Lifetime",
|
||||
"INHOURS": "Stunden"
|
||||
},
|
||||
"RESET": "Auf Instanzeinstellung zurücksetzen",
|
||||
"CREATECUSTOM": "Benutzerdefinierte Richtlinie erstellen",
|
||||
|
@ -818,6 +818,7 @@
|
||||
"NOTIFICATIONS": "Notification providers and SMTP",
|
||||
"MESSAGETEXTS": "Message Texts",
|
||||
"IDP": "Identity Providers",
|
||||
"DOMAIN": "Domain settings",
|
||||
"LOGINTEXTS": "Login Interface Texts",
|
||||
"BRANDING": "Branding",
|
||||
"PRIVACYPOLICY": "Privacy Policy",
|
||||
@ -827,6 +828,7 @@
|
||||
"GROUPS": {
|
||||
"NOTIFICATIONS": "Notifications",
|
||||
"LOGIN": "Login and Access",
|
||||
"DOMAIN": "Domain",
|
||||
"TEXTS": "Texts and Languages",
|
||||
"APPEARANCE": "Appearance",
|
||||
"OTHER": "Other"
|
||||
@ -846,6 +848,8 @@
|
||||
"HOST": "Host",
|
||||
"USER": "User",
|
||||
"PASSWORD": "Password",
|
||||
"SETPASSWORD": "Set SMTP Password",
|
||||
"PASSWORDSET": "SMTP Password was set successfully.",
|
||||
"TLS": "Transport Layer Security (TLS)",
|
||||
"SAVED": "Saved successfully!",
|
||||
"REQUIREDWARN": "To send notifications from your domain, you have to enter your SMTP data."
|
||||
@ -949,9 +953,8 @@
|
||||
"TITLE": "Lockout Policy",
|
||||
"DESCRIPTION": "Set a maximum number of passwordretries, after which accounts will be blocked."
|
||||
},
|
||||
"IAM_POLICY": {
|
||||
"TITLE": "IAM Access Preferences",
|
||||
"DESCRIPTION": "Define access properties of your users."
|
||||
"DOMAIN_POLICY": {
|
||||
"TITLE": "Domain Settings"
|
||||
},
|
||||
"PRIVATELABELING_POLICY": {
|
||||
"TITLE": "Branding",
|
||||
@ -965,6 +968,7 @@
|
||||
"DESCRIPTIONCREATEADMIN": "Users can choose from the available identity providers below.",
|
||||
"DESCRIPTIONCREATEMGMT": "Users can choose from the available identity providers below. Note: You can use System-set providers as well as providers set for your organization only.",
|
||||
"ADVANCED": "Advanced",
|
||||
"LIFETIMEDURATIONS": "Login Lifetimes",
|
||||
"SAVED": "Saved successfully!"
|
||||
},
|
||||
"PRIVACY_POLICY": {
|
||||
@ -1080,7 +1084,9 @@
|
||||
"MAXATTEMPTS": "Password maximum Attempts",
|
||||
"EXPIREWARNDAYS": "Expiration Warning after day",
|
||||
"MAXAGEDAYS": "Max Age in days",
|
||||
"USERLOGINMUSTBEDOMAIN": "User Login must be Domain",
|
||||
"USERLOGINMUSTBEDOMAIN": "User Loginname must contain orgdomain",
|
||||
"VALIDATEORGDOMAINS": "Validate Org domains",
|
||||
"SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN": "SMTP Sender Address matches Instance Domain",
|
||||
"ALLOWUSERNAMEPASSWORD": "Username Password allowed",
|
||||
"ALLOWEXTERNALIDP": "External IDP allowed",
|
||||
"ALLOWREGISTER": "Register allowed",
|
||||
@ -1097,7 +1103,13 @@
|
||||
"DEFAULTREDIRECTURI": "Default Redirect URI",
|
||||
"DEFAULTREDIRECTURI_DESC": "Defines where the user will be redirected to if the login has started without an app context (e.g. from mail)",
|
||||
"ERRORMSGPOPUP": "Show Error in Dialog",
|
||||
"DISABLEWATERMARK": "Hide Watermark"
|
||||
"DISABLEWATERMARK": "Hide Watermark",
|
||||
"PASSWORDCHECKLIFETIME": "Password Check Lifetime",
|
||||
"EXTERNALLOGINCHECKLIFETIME": "External Login Check Lifetime",
|
||||
"MFAINITSKIPLIFETIME": "Multifactor Init Lifetime",
|
||||
"SECONDFACTORCHECKLIFETIME": "Second Factor Check Lifetime",
|
||||
"MULTIFACTORCHECKLIFETIME": "Multifactor Check Lifetime",
|
||||
"INHOURS": "hours"
|
||||
},
|
||||
"RESET": "Reset to Instance default",
|
||||
"CREATECUSTOM": "Create Custom Policy",
|
||||
|
@ -818,6 +818,7 @@
|
||||
"NOTIFICATIONS": "Notifiche",
|
||||
"MESSAGETEXTS": "Testi di notifica",
|
||||
"IDP": "Identity Providers",
|
||||
"DOMAIN": "Impostazioni del dominio",
|
||||
"LOGINTEXTS": "Testi dell'interfaccia login",
|
||||
"BRANDING": "Branding",
|
||||
"PRIVACYPOLICY": "Informativa sulla privacy e TOS",
|
||||
@ -827,6 +828,7 @@
|
||||
"GROUPS": {
|
||||
"NOTIFICATIONS": "Notifiche",
|
||||
"LOGIN": "Accesso e login",
|
||||
"DOMAIN": "Dominio",
|
||||
"TEXTS": "Testi e lingue",
|
||||
"APPEARANCE": "Aussehen",
|
||||
"OTHER": "Altro"
|
||||
@ -846,6 +848,8 @@
|
||||
"HOST": "Host",
|
||||
"USER": "Utente",
|
||||
"PASSWORD": "Password",
|
||||
"SETPASSWORD": "Imposta SMTP Password",
|
||||
"PASSWORDSET": "SMTP Password impostata con successo.",
|
||||
"TLS": "Transport Layer Security (TLS)",
|
||||
"SAVED": "Salvato con successo!",
|
||||
"REQUIREDWARN": "Per inviare notifiche dal tuo dominio, devi inserire i tuoi dati SMTP."
|
||||
@ -949,9 +953,8 @@
|
||||
"TITLE": "Impostazioni di blocco",
|
||||
"DESCRIPTION": "Imposta un numero massimo di tentativi di password, dopo i quali gli account saranno bloccati."
|
||||
},
|
||||
"IAM_POLICY": {
|
||||
"TITLE": "Impostazioni di accesso IAM",
|
||||
"DESCRIPTION": "Definisci le propriet\u00e0 di accesso dei tuoi utenti."
|
||||
"DOMAIN_POLICY": {
|
||||
"TITLE": "Impostazioni dominio"
|
||||
},
|
||||
"PRIVATELABELING_POLICY": {
|
||||
"TITLE": "Branding",
|
||||
@ -965,6 +968,7 @@
|
||||
"DESCRIPTIONCREATEADMIN": "Gli utenti possono scegliere tra gli IDP disponibili qui sotto.",
|
||||
"DESCRIPTIONCREATEMGMT": "Gli utenti possono scegliere tra gli IDP disponibili qui sotto. Nota: puoi usare i provider impostati nel sistema e quelli impostati della tua organizzazione.",
|
||||
"ADVANCED": "Impostazioni avanzate",
|
||||
"LIFETIMEDURATIONS": "Login Lifetimes",
|
||||
"SAVED": "Salvato con successo!"
|
||||
},
|
||||
"PRIVACY_POLICY": {
|
||||
@ -1080,7 +1084,9 @@
|
||||
"MAXATTEMPTS": "Massimo numero di tentativi di password",
|
||||
"EXPIREWARNDAYS": "Avviso scadenza dopo il giorno",
|
||||
"MAXAGEDAYS": "Lunghezza massima in giorni",
|
||||
"USERLOGINMUSTBEDOMAIN": "Loginname dell'utente deve contenere il dominio",
|
||||
"USERLOGINMUSTBEDOMAIN": "Nome utente deve contenere il dominio dell' organizzazione",
|
||||
"VALIDATEORGDOMAINS": "Verifica domini dell' organizzazione",
|
||||
"SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN": "L'indirizzo mittente SMTP corrisponde al dominio dell'istanza",
|
||||
"ALLOWUSERNAMEPASSWORD": "Autenticazione classica con password consentita",
|
||||
"ALLOWEXTERNALIDP": "IDP esterno consentito",
|
||||
"ALLOWREGISTER": "Registrazione consentita",
|
||||
@ -1097,7 +1103,13 @@
|
||||
"DEFAULTREDIRECTURI": "Default Redirect URI",
|
||||
"DEFAULTREDIRECTURI_DESC": "Definisce dove verrà reindirizzato l'utente se l'accesso è stato avviato senza un contesto dell'app (ad es. dall' email)",
|
||||
"ERRORMSGPOPUP": "Mostra l'errore nella finestra di dialogo",
|
||||
"DISABLEWATERMARK": "Nascondi la filigrana"
|
||||
"DISABLEWATERMARK": "Nascondi la filigrana",
|
||||
"PASSWORDCHECKLIFETIME": "Lifetime verificazione password",
|
||||
"EXTERNALLOGINCHECKLIFETIME": "Lifetime verificazione login esterno",
|
||||
"MFAINITSKIPLIFETIME": "Lifetime Initalizzazione Multifattore",
|
||||
"SECONDFACTORCHECKLIFETIME": "Lifetime Second Factor Lifetime",
|
||||
"MULTIFACTORCHECKLIFETIME": "Lifetime Multi Factor",
|
||||
"INHOURS": "ore"
|
||||
},
|
||||
"RESET": "Ripristina l'impostazione dell'istanza",
|
||||
"CREATECUSTOM": "Crea un'impostazione personalizzata",
|
||||
|
@ -43,7 +43,6 @@
|
||||
@import 'src/app/modules/filter/filter.component.scss';
|
||||
@import 'src/app/modules/policies/message-texts/message-texts.component.scss';
|
||||
@import 'src/app/modules/policies/private-labeling-policy/private-labeling-policy.component.scss';
|
||||
@import 'src/app/modules/policies/private-labeling-policy/preview/preview.component.scss';
|
||||
@import 'src/app/modules/info-row/info-row.component.scss';
|
||||
@import 'src/app/modules/sidenav/sidenav.component';
|
||||
@import 'src/app/modules/user-grants/user-grants.component.scss';
|
||||
@ -104,7 +103,6 @@
|
||||
@include sidenav-theme($theme);
|
||||
@include info-section-theme($theme);
|
||||
@include filter-theme($theme);
|
||||
@include preview-theme($theme);
|
||||
@include private-label-theme($theme);
|
||||
@include project-grid-theme($theme);
|
||||
@include granted-project-detail-theme($theme);
|
||||
|
@ -425,10 +425,6 @@ $custom-typography: mat.define-typography-config(
|
||||
--success: #10b981;
|
||||
$border-color: map-get($foreground, divider);
|
||||
|
||||
.main-container {
|
||||
color: map-get($foreground, base);
|
||||
}
|
||||
|
||||
.mat-menu-panel {
|
||||
background-color: map-get($background, cards);
|
||||
transition: background-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
|
@ -1,14 +1,10 @@
|
||||
@use '@angular/material' as mat;
|
||||
|
||||
@mixin toast-theme($theme) {
|
||||
$primary: map-get($theme, primary);
|
||||
$primary-color: mat.get-color-from-palette($primary, 500);
|
||||
$primary-light-color: mat.get-color-from-palette($primary, 200);
|
||||
$warn: map-get($theme, warn);
|
||||
$warn-color: mat.get-color-from-palette($warn, 500);
|
||||
$background: map-get($theme, background);
|
||||
$foreground: map-get($theme, foreground);
|
||||
$is-dark-theme: map-get($theme, is-dark);
|
||||
|
||||
// .data-e2e-success {
|
||||
// background-color: map-get($background, cards) !important;
|
||||
@ -17,6 +13,6 @@
|
||||
|
||||
.data-e2e-failure {
|
||||
background-color: $warn-color !important;
|
||||
color: map-get($foreground, text) !important;
|
||||
color: mat.get-color-from-palette($warn, default-contrast) !important;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user