mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 04:17:32 +00:00
feat(console, login): v2 notification settings, login avatar (#3606)
* instance routing * instance naming * org list * rm isonsystem * breadcrumb type * routing * instance members * fragment refresh org * settings pages * settings list, sidenav grouping, i18n * org-settings, policy changes * lint * grid * rename grid * fallback to general * cleanup * general settings, remove cards * sidenav for settings, label policy * i18n * header, nav backbuild * general, project nav rehaul * login text background adapt * org nav anim * org, instance settings, fix policy layout, roles * i18n, active route for project * lint * notification-settings * idp create redirect, sms provider create, i18n * oidc configuration * settings list * new avatar colors for login * cleaner js * avatar theme login * remove avatar elevation
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
.card {
|
||||
margin: 1rem 0;
|
||||
padding: 1.5rem;
|
||||
padding: 1rem 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
padding-top: 1rem;
|
||||
min-width: 300px;
|
||||
|
@@ -1,156 +1,179 @@
|
||||
<div class="container">
|
||||
<div class="abort-container">
|
||||
<button (click)="close()" mat-icon-button>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
<span class="abort">{{ 'IDP.CREATE.TITLE' | translate }}</span><span class="abort-2">Step
|
||||
{{ currentCreateStep }} of
|
||||
{{ createSteps }}</span>
|
||||
</div>
|
||||
|
||||
<h1>{{'IDP.CREATE.TITLE' | translate}}</h1>
|
||||
|
||||
<mat-progress-bar *ngIf="loading" color="primary" mode="indeterminate"></mat-progress-bar>
|
||||
|
||||
<ng-container *ngIf="currentCreateStep === 1">
|
||||
<p class="desc cnsl-secondary-text">{{'IDP.CREATE.DESCRIPTION' | translate}}</p>
|
||||
|
||||
<cnsl-idp-type-radio [types]="idpTypes" (selectedType)="idpType = $event" [selected]="idpType">
|
||||
</cnsl-idp-type-radio>
|
||||
|
||||
<div class="actions">
|
||||
<button mat-raised-button [disabled]="!idpType" color="primary"
|
||||
(click)="currentCreateStep = 2">{{'ACTIONS.CONTINUE' | translate}}</button>
|
||||
<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">{{ 'IDP.CREATE.TITLE' | translate }}</span
|
||||
><span class="abort-2">Step {{ currentCreateStep }} of {{ createSteps }}</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="currentCreateStep === 2 && idpType === OIDC">
|
||||
<p class="desc cnsl-secondary-text">{{'IDP.OIDC.DESCRIPTION' | translate}}</p>
|
||||
<div class="idp-create-content">
|
||||
<h1>{{ 'IDP.CREATE.TITLE' | translate }}</h1>
|
||||
|
||||
<form [formGroup]="oidcFormGroup" (ngSubmit)="addOIDCIdp()">
|
||||
<div class="idp-content">
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.NAME' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="name" />
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.ISSUER' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="issuer" />
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
<mat-progress-bar *ngIf="loading" color="primary" mode="indeterminate"></mat-progress-bar>
|
||||
|
||||
<cnsl-info-section class="auto-reg-info">
|
||||
<div>
|
||||
<p class="auto-reg-desc">{{'IDP.AUTOREGISTER_DESC' | translate}}</p>
|
||||
<mat-checkbox formControlName="autoRegister">
|
||||
{{'IDP.AUTOREGISTER' | translate}}
|
||||
</mat-checkbox>
|
||||
<ng-container *ngIf="currentCreateStep === 1">
|
||||
<p class="desc cnsl-secondary-text">{{ 'IDP.CREATE.DESCRIPTION' | translate }}</p>
|
||||
|
||||
<cnsl-idp-type-radio [types]="idpTypes" (selectedType)="idpType = $event" [selected]="idpType">
|
||||
</cnsl-idp-type-radio>
|
||||
|
||||
<div class="first-step-actions">
|
||||
<button mat-raised-button [disabled]="!idpType" color="primary" (click)="currentCreateStep = 2">
|
||||
{{ 'ACTIONS.CONTINUE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</cnsl-info-section>
|
||||
</ng-container>
|
||||
|
||||
<div class="idp-content">
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.CLIENTID' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="clientId" />
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.CLIENTSECRET' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="clientSecret" />
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
<div class="idp-content">
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.SCOPESLIST' | translate }}</cnsl-label>
|
||||
<mat-chip-list #chipScopesList aria-label="scope selection" *ngIf="scopesList">
|
||||
<mat-chip class="chip" *ngFor="let scope of scopesList.value" selectable="false" removable
|
||||
(removed)="removeScope(scope)">
|
||||
{{scope}} <mat-icon matChipRemove>cancel</mat-icon>
|
||||
</mat-chip>
|
||||
<input cnslInput [matChipInputFor]="chipScopesList" [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||
[matChipInputAddOnBlur]="true" (matChipInputTokenEnd)="addScope($event)">
|
||||
</mat-chip-list>
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
<div class="idp-content">
|
||||
<cnsl-form-field class="formfield" appearance="outline">
|
||||
<cnsl-label>{{ 'IDP.IDPDISPLAYNAMMAPPING' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="idpDisplayNameMapping">
|
||||
<mat-option *ngFor="let field of mappingFields" [value]="field">
|
||||
{{ 'IDP.MAPPINGFIELD.'+field | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field class="formfield" appearance="outline">
|
||||
<cnsl-label>{{ 'IDP.USERNAMEMAPPING' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="usernameMapping">
|
||||
<mat-option *ngFor="let field of mappingFields" [value]="field">
|
||||
{{ 'IDP.MAPPINGFIELD.'+field | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
<ng-container *ngIf="currentCreateStep === 2 && idpType === OIDC">
|
||||
<p class="desc cnsl-secondary-text">{{ 'IDP.OIDC.DESCRIPTION' | translate }}</p>
|
||||
|
||||
<div class="idp-create-actions">
|
||||
<button color="primary" (click)="currentCreateStep = 1" mat-stroked-button class="back-button" type="button">
|
||||
{{ 'ACTIONS.BACK' | translate }}
|
||||
</button>
|
||||
<button color="primary" mat-raised-button class="continue-button" [disabled]="oidcFormGroup.invalid"
|
||||
type="submit">
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</ng-container>
|
||||
<form [formGroup]="oidcFormGroup" (ngSubmit)="addOIDCIdp()">
|
||||
<div class="idp-content">
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.NAME' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="name" />
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.ISSUER' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="issuer" />
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="currentCreateStep === 2 && idpType === JWT">
|
||||
<p class="desc cnsl-secondary-text">{{'IDP.JWT.DESCRIPTION' | translate}}</p>
|
||||
<cnsl-info-section class="auto-reg-info">
|
||||
<div>
|
||||
<p class="auto-reg-desc">{{ 'IDP.AUTOREGISTER_DESC' | translate }}</p>
|
||||
<mat-checkbox formControlName="autoRegister">
|
||||
{{ 'IDP.AUTOREGISTER' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</cnsl-info-section>
|
||||
|
||||
<form [formGroup]="jwtFormGroup" (ngSubmit)="addJWTIdp()">
|
||||
<div class="idp-content">
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.NAME' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="jwtName" />
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.JWT.HEADERNAME' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="jwtHeaderName" />
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.ISSUER' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="jwtIssuer" />
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
<div class="idp-content">
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.CLIENTID' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="clientId" />
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.CLIENTSECRET' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="clientSecret" />
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
<div class="idp-content">
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.SCOPESLIST' | translate }}</cnsl-label>
|
||||
<mat-chip-list #chipScopesList aria-label="scope selection" *ngIf="scopesList">
|
||||
<mat-chip
|
||||
class="chip"
|
||||
*ngFor="let scope of scopesList.value"
|
||||
selectable="false"
|
||||
removable
|
||||
(removed)="removeScope(scope)"
|
||||
>
|
||||
{{ scope }} <mat-icon matChipRemove>cancel</mat-icon>
|
||||
</mat-chip>
|
||||
<input
|
||||
cnslInput
|
||||
[matChipInputFor]="chipScopesList"
|
||||
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||
[matChipInputAddOnBlur]="true"
|
||||
(matChipInputTokenEnd)="addScope($event)"
|
||||
/>
|
||||
</mat-chip-list>
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
<div class="idp-content">
|
||||
<cnsl-form-field class="formfield" appearance="outline">
|
||||
<cnsl-label>{{ 'IDP.IDPDISPLAYNAMMAPPING' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="idpDisplayNameMapping">
|
||||
<mat-option *ngFor="let field of mappingFields" [value]="field">
|
||||
{{ 'IDP.MAPPINGFIELD.' + field | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field class="formfield" appearance="outline">
|
||||
<cnsl-label>{{ 'IDP.USERNAMEMAPPING' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="usernameMapping">
|
||||
<mat-option *ngFor="let field of mappingFields" [value]="field">
|
||||
{{ 'IDP.MAPPINGFIELD.' + field | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
|
||||
<cnsl-info-section class="auto-reg-info">
|
||||
<div>
|
||||
<p class="auto-reg-desc">{{'IDP.AUTOREGISTER_DESC' | translate}}</p>
|
||||
<mat-checkbox formControlName="jwtAutoRegister">
|
||||
{{'IDP.AUTOREGISTER' | translate}}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</cnsl-info-section>
|
||||
<div class="idp-create-actions">
|
||||
<button color="primary" (click)="currentCreateStep = 1" mat-stroked-button class="back-button" type="button">
|
||||
{{ 'ACTIONS.BACK' | translate }}
|
||||
</button>
|
||||
<button
|
||||
color="primary"
|
||||
mat-raised-button
|
||||
class="continue-button"
|
||||
[disabled]="oidcFormGroup.invalid"
|
||||
type="submit"
|
||||
>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</ng-container>
|
||||
|
||||
<div class="idp-content">
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.JWT.JWTENDPOINT' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="jwtEndpoint" />
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.JWT.JWTKEYSENDPOINT' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="jwtKeysEndpoint" />
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
<ng-container *ngIf="currentCreateStep === 2 && idpType === JWT">
|
||||
<p class="desc cnsl-secondary-text">{{ 'IDP.JWT.DESCRIPTION' | translate }}</p>
|
||||
|
||||
<div class="idp-create-actions">
|
||||
<button color="primary" (click)="currentCreateStep = 1" mat-stroked-button class="back-button" type="button">
|
||||
{{ 'ACTIONS.BACK' | translate }}
|
||||
</button>
|
||||
<button color="primary" mat-raised-button class="continue-button" [disabled]="jwtFormGroup.invalid"
|
||||
type="submit">
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</ng-container>
|
||||
<form [formGroup]="jwtFormGroup" (ngSubmit)="addJWTIdp()">
|
||||
<div class="idp-content">
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.NAME' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="jwtName" />
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.JWT.HEADERNAME' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="jwtHeaderName" />
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.ISSUER' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="jwtIssuer" />
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<cnsl-info-section class="auto-reg-info">
|
||||
<div>
|
||||
<p class="auto-reg-desc">{{ 'IDP.AUTOREGISTER_DESC' | translate }}</p>
|
||||
<mat-checkbox formControlName="jwtAutoRegister">
|
||||
{{ 'IDP.AUTOREGISTER' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</cnsl-info-section>
|
||||
|
||||
<div class="idp-content">
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.JWT.JWTENDPOINT' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="jwtEndpoint" />
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.JWT.JWTKEYSENDPOINT' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="jwtKeysEndpoint" />
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
|
||||
<div class="idp-create-actions">
|
||||
<button color="primary" (click)="currentCreateStep = 1" mat-stroked-button class="back-button" type="button">
|
||||
{{ 'ACTIONS.BACK' | translate }}
|
||||
</button>
|
||||
<button
|
||||
color="primary"
|
||||
mat-raised-button
|
||||
class="continue-button"
|
||||
[disabled]="jwtFormGroup.invalid"
|
||||
type="submit"
|
||||
>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -2,75 +2,75 @@
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 4rem 4rem 2rem 4rem;
|
||||
.abort-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
|
||||
@media only screen and (max-width: 450px) {
|
||||
padding: 4rem 1rem 2rem 1rem;
|
||||
.abort {
|
||||
font-size: 1.2rem;
|
||||
margin-left: 2rem;
|
||||
}
|
||||
|
||||
.abort-container {
|
||||
.abort-2 {
|
||||
font-size: 1.2rem;
|
||||
margin-left: 2rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.add-line-btn {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.idp-create-content {
|
||||
padding: 0 0 0 72px;
|
||||
|
||||
.first-step-actions {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.auto-reg-info {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
.auto-reg-desc {
|
||||
margin: 0 0 1rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
.idp-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
margin: 0 -0.5rem;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.abort {
|
||||
font-size: 1.2rem;
|
||||
margin-left: 2rem;
|
||||
}
|
||||
|
||||
.abort-2 {
|
||||
font-size: 1.2rem;
|
||||
margin-left: 2rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.add-line-btn {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.auto-reg-info {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
.auto-reg-desc {
|
||||
margin: 0 0 1rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
.idp-content {
|
||||
display: flex;
|
||||
margin: 0 -0.5rem;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.desc {
|
||||
flex-basis: 100%;
|
||||
margin: 0 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.formfield {
|
||||
flex: 1;
|
||||
margin: 0 0.5rem;
|
||||
|
||||
@media only screen and (max-width: 450px) {
|
||||
.desc {
|
||||
flex-basis: 100%;
|
||||
margin: 0 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.formfield {
|
||||
flex: 1;
|
||||
margin: 0 0.5rem;
|
||||
|
||||
@media only screen and (max-width: 450px) {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.idp-create-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 1rem;
|
||||
|
||||
button[mat-stroked-button] {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
button[mat-raised-button] {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.idp-create-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 1rem;
|
||||
|
||||
button[mat-stroked-button] {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
button[mat-raised-button] {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
@@ -83,13 +83,12 @@ export class IdpCreateComponent implements OnInit, OnDestroy {
|
||||
OIDCMappingField.OIDC_MAPPING_FIELD_PREFERRED_USERNAME,
|
||||
OIDCMappingField.OIDC_MAPPING_FIELD_EMAIL,
|
||||
];
|
||||
const bread: Breadcrumb = {
|
||||
type: BreadcrumbType.ORG,
|
||||
routerLink: ['/org'],
|
||||
};
|
||||
|
||||
const iamBread = new Breadcrumb({
|
||||
type: BreadcrumbType.INSTANCE,
|
||||
name: 'Instance',
|
||||
routerLink: ['/instance'],
|
||||
});
|
||||
breadcrumbService.setBreadcrumb([iamBread]);
|
||||
breadcrumbService.setBreadcrumb([bread]);
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
this.service = this.injector.get(AdminService as Type<AdminService>);
|
||||
@@ -98,11 +97,12 @@ export class IdpCreateComponent implements OnInit, OnDestroy {
|
||||
OIDCMappingField.OIDC_MAPPING_FIELD_EMAIL,
|
||||
];
|
||||
|
||||
const bread: Breadcrumb = {
|
||||
const iamBread = new Breadcrumb({
|
||||
type: BreadcrumbType.ORG,
|
||||
routerLink: ['/org'],
|
||||
};
|
||||
breadcrumbService.setBreadcrumb([bread]);
|
||||
name: 'Instance',
|
||||
routerLink: ['/instance'],
|
||||
});
|
||||
breadcrumbService.setBreadcrumb([iamBread]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
@@ -139,15 +139,16 @@ export class IdpCreateComponent implements OnInit, OnDestroy {
|
||||
.then((idp) => {
|
||||
setTimeout(() => {
|
||||
this.loading = false;
|
||||
this.router.navigate([
|
||||
this.serviceType === PolicyComponentServiceType.MGMT
|
||||
? 'org'
|
||||
: this.serviceType === PolicyComponentServiceType.ADMIN
|
||||
? 'iam'
|
||||
: '',
|
||||
'policy',
|
||||
'login',
|
||||
]);
|
||||
this.router.navigate(
|
||||
[
|
||||
this.serviceType === PolicyComponentServiceType.MGMT
|
||||
? '/org-settings'
|
||||
: this.serviceType === PolicyComponentServiceType.ADMIN
|
||||
? '/settings'
|
||||
: '',
|
||||
],
|
||||
{ queryParams: { id: 'idp' } },
|
||||
);
|
||||
}, 2000);
|
||||
})
|
||||
.catch((error) => {
|
||||
|
@@ -1,20 +1,33 @@
|
||||
<cnsl-top-view title="{{'IDP.DETAIL.TITLE' | translate}}"
|
||||
<cnsl-top-view
|
||||
title="{{ 'IDP.DETAIL.TITLE' | translate }}"
|
||||
[sub]="idp?.oidcConfig ? ('IDP.OIDC.TITLE' | translate) : idp?.jwtConfig ? ('IDP.JWT.TITLE' | translate) : ''"
|
||||
[isActive]="idp?.state === IDPState.IDP_STATE_ACTIVE" [isInactive]="idp?.state === IDPState.IDP_STATE_INACTIVE"
|
||||
[hasContributors]="false" stateTooltip="{{'IDP.STATES.'+idp?.state | translate}}"
|
||||
[hasActions]="(serviceType === PolicyComponentServiceType.MGMT ? ['org.idp.write'] : ['iam.idp.write']) | hasRole | async">
|
||||
<ng-template topActions cnslHasRole
|
||||
[hasRole]="serviceType === PolicyComponentServiceType.MGMT ? ['org.idp.write'] : ['iam.idp.write']">
|
||||
<button mat-menu-item *ngIf="idp?.state !== IDPState.IDP_STATE_INACTIVE"
|
||||
(click)="changeState(IDPState.IDP_STATE_INACTIVE)">
|
||||
{{'ACTIONS.DEACTIVATE' | translate}}
|
||||
[isActive]="idp?.state === IDPState.IDP_STATE_ACTIVE"
|
||||
[isInactive]="idp?.state === IDPState.IDP_STATE_INACTIVE"
|
||||
[hasContributors]="false"
|
||||
stateTooltip="{{ 'IDP.STATES.' + idp?.state | translate }}"
|
||||
[hasActions]="(serviceType === PolicyComponentServiceType.MGMT ? ['org.idp.write'] : ['iam.idp.write']) | hasRole | async"
|
||||
>
|
||||
<ng-template
|
||||
topActions
|
||||
cnslHasRole
|
||||
[hasRole]="serviceType === PolicyComponentServiceType.MGMT ? ['org.idp.write'] : ['iam.idp.write']"
|
||||
>
|
||||
<button
|
||||
mat-menu-item
|
||||
*ngIf="idp?.state !== IDPState.IDP_STATE_INACTIVE"
|
||||
(click)="changeState(IDPState.IDP_STATE_INACTIVE)"
|
||||
>
|
||||
{{ 'ACTIONS.DEACTIVATE' | translate }}
|
||||
</button>
|
||||
<button mat-menu-item *ngIf="idp?.state === IDPState.IDP_STATE_INACTIVE"
|
||||
(click)="changeState(IDPState.IDP_STATE_ACTIVE)">
|
||||
{{'ACTIONS.REACTIVATE' | translate}}
|
||||
<button
|
||||
mat-menu-item
|
||||
*ngIf="idp?.state === IDPState.IDP_STATE_INACTIVE"
|
||||
(click)="changeState(IDPState.IDP_STATE_ACTIVE)"
|
||||
>
|
||||
{{ 'ACTIONS.REACTIVATE' | translate }}
|
||||
</button>
|
||||
<button mat-menu-item matTooltip="{{'IDP.DELETE' | translate}}" (click)="deleteIdp()">
|
||||
<span [style.color]="'var(--warn)'">{{'IDP.DELETE_TITLE' | translate}}</span>
|
||||
<button mat-menu-item matTooltip="{{ 'IDP.DELETE' | translate }}" (click)="deleteIdp()">
|
||||
<span [style.color]="'var(--warn)'">{{ 'IDP.DELETE_TITLE' | translate }}</span>
|
||||
</button>
|
||||
</ng-template>
|
||||
<cnsl-info-row topContent *ngIf="idp" [idp]="idp"></cnsl-info-row>
|
||||
@@ -24,8 +37,7 @@
|
||||
<form class="idp-form" (ngSubmit)="updateIdp()">
|
||||
<ng-container [formGroup]="idpForm">
|
||||
<div class="idp-content">
|
||||
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'IDP.NAME' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="name" />
|
||||
</cnsl-form-field>
|
||||
@@ -33,16 +45,16 @@
|
||||
<cnsl-label>{{ 'IDP.STYLE' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="stylingType">
|
||||
<mat-option *ngFor="let field of styleFields" [value]="field">
|
||||
{{ 'IDP.STYLEFIELD.'+field | translate }}
|
||||
{{ 'IDP.STYLEFIELD.' + field | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-info-section class="auto-reg-info">
|
||||
<div>
|
||||
<p class="auto-reg-desc">{{'IDP.AUTOREGISTER_DESC' | translate}}</p>
|
||||
<p class="auto-reg-desc">{{ 'IDP.AUTOREGISTER_DESC' | translate }}</p>
|
||||
<mat-checkbox formControlName="autoRegister" [disabled]="(canWrite | async) === false">
|
||||
{{'IDP.AUTOREGISTER' | translate}}
|
||||
{{ 'IDP.AUTOREGISTER' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</cnsl-info-section>
|
||||
@@ -50,52 +62,72 @@
|
||||
</ng-container>
|
||||
|
||||
<div class="btn-wrapper">
|
||||
<button color="primary" mat-raised-button class="continue-button"
|
||||
[disabled]="idpForm.invalid || (canWrite | async) === false" type="submit">
|
||||
<button
|
||||
color="primary"
|
||||
mat-raised-button
|
||||
class="continue-button"
|
||||
[disabled]="idpForm.invalid || (canWrite | async) === false"
|
||||
type="submit"
|
||||
>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<ng-container *ngIf="idp?.oidcConfig && oidcConfigForm">
|
||||
<h2>{{'IDP.OIDC.TITLE' | translate}}</h2>
|
||||
<p class="idp-desc cnsl-secondary-text">{{'IDP.OIDC.DESCRIPTION' | translate}}</p>
|
||||
<h2>{{ 'IDP.OIDC.TITLE' | translate }}</h2>
|
||||
<p class="idp-desc cnsl-secondary-text">{{ 'IDP.OIDC.DESCRIPTION' | translate }}</p>
|
||||
|
||||
<form (ngSubmit)="updateOidcConfig()">
|
||||
<ng-container [formGroup]="oidcConfigForm">
|
||||
<div class="idp-content">
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'IDP.ISSUER' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="issuer" />
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'IDP.CLIENTID' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="clientId" />
|
||||
</cnsl-form-field>
|
||||
<mat-checkbox class="idp-desc cnsl-secondary-text" [(ngModel)]="showIdSecretSection"
|
||||
[disabled]="(canWrite | async) === false" [ngModelOptions]="{standalone: true}">
|
||||
<mat-checkbox
|
||||
class="idp-desc cnsl-secondary-text"
|
||||
[(ngModel)]="showIdSecretSection"
|
||||
[disabled]="(canWrite | async) === false"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
>
|
||||
Update Client Secret
|
||||
</mat-checkbox>
|
||||
<cnsl-form-field appearance="outline" class="formfield" *ngIf="showIdSecretSection">
|
||||
<cnsl-form-field class="formfield" *ngIf="showIdSecretSection">
|
||||
<cnsl-label>{{ 'IDP.CLIENTSECRET' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="clientSecret" />
|
||||
</cnsl-form-field>
|
||||
<div class="line">
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'IDP.SCOPESLIST' | translate }}</cnsl-label>
|
||||
|
||||
<input cnslInput [matChipInputFor]="chipScopesList" [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||
[matChipInputAddOnBlur]="true" (matChipInputTokenEnd)="addScope($event)">
|
||||
<input
|
||||
cnslInput
|
||||
[matChipInputFor]="chipScopesList"
|
||||
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||
[matChipInputAddOnBlur]="true"
|
||||
(matChipInputTokenEnd)="addScope($event)"
|
||||
/>
|
||||
</cnsl-form-field>
|
||||
<button (click)="addScope($any($event))" mat-icon-button>
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<cnsl-form-field appearance="outline" class="formfield fullwidth">
|
||||
<cnsl-form-field class="formfield fullwidth">
|
||||
<mat-chip-list class="chip-list" #chipScopesList aria-label="scope selection">
|
||||
<mat-chip class="chip" *ngFor="let scope of scopesList?.value" selectable="false" removable
|
||||
(removed)="removeScope(scope)" [disabled]="(canWrite | async) === false">
|
||||
{{scope}} <mat-icon matChipRemove>cancel</mat-icon>
|
||||
<mat-chip
|
||||
class="chip"
|
||||
*ngFor="let scope of scopesList?.value"
|
||||
selectable="false"
|
||||
removable
|
||||
(removed)="removeScope(scope)"
|
||||
[disabled]="(canWrite | async) === false"
|
||||
>
|
||||
{{ scope }} <mat-icon matChipRemove>cancel</mat-icon>
|
||||
</mat-chip>
|
||||
</mat-chip-list>
|
||||
</cnsl-form-field>
|
||||
@@ -104,7 +136,7 @@
|
||||
<cnsl-label>{{ 'IDP.IDPDISPLAYNAMMAPPING' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="displayNameMapping">
|
||||
<mat-option *ngFor="let field of mappingFields" [value]="field">
|
||||
{{ 'IDP.MAPPINGFIELD.'+field | translate }}
|
||||
{{ 'IDP.MAPPINGFIELD.' + field | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
@@ -112,7 +144,7 @@
|
||||
<cnsl-label>{{ 'IDP.USERNAMEMAPPING' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="usernameMapping">
|
||||
<mat-option *ngFor="let field of mappingFields" [value]="field">
|
||||
{{ 'IDP.MAPPINGFIELD.'+field | translate }}
|
||||
{{ 'IDP.MAPPINGFIELD.' + field | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
@@ -120,8 +152,13 @@
|
||||
</ng-container>
|
||||
|
||||
<div class="btn-wrapper">
|
||||
<button color="primary" mat-raised-button class="continue-button"
|
||||
[disabled]="oidcConfigForm.invalid || (canWrite | async) === false" type="submit">
|
||||
<button
|
||||
color="primary"
|
||||
mat-raised-button
|
||||
class="continue-button"
|
||||
[disabled]="oidcConfigForm.invalid || (canWrite | async) === false"
|
||||
type="submit"
|
||||
>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -129,28 +166,28 @@
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="idp?.jwtConfig && jwtConfigForm">
|
||||
<h2>{{'IDP.JWT.TITLE' | translate}}</h2>
|
||||
<p>{{'IDP.JWT.DESCRIPTION' | translate}}</p>
|
||||
<h2>{{ 'IDP.JWT.TITLE' | translate }}</h2>
|
||||
<p>{{ 'IDP.JWT.DESCRIPTION' | translate }}</p>
|
||||
|
||||
<form (ngSubmit)="updateJwtConfig()">
|
||||
<ng-container [formGroup]="jwtConfigForm">
|
||||
<div class="idp-content">
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'IDP.ISSUER' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="issuer" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'IDP.JWT.HEADERNAME' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="headerName" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'IDP.JWT.JWTENDPOINT' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="jwtEndpoint" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'IDP.JWT.JWTKEYSENDPOINT' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="keysEndpoint" />
|
||||
</cnsl-form-field>
|
||||
@@ -158,11 +195,16 @@
|
||||
</ng-container>
|
||||
|
||||
<div class="btn-wrapper">
|
||||
<button color="primary" mat-raised-button class="continue-button"
|
||||
[disabled]="jwtConfigForm.invalid || (canWrite | async) === false" type="submit">
|
||||
<button
|
||||
color="primary"
|
||||
mat-raised-button
|
||||
class="continue-button"
|
||||
[disabled]="jwtConfigForm.invalid || (canWrite | async) === false"
|
||||
type="submit"
|
||||
>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -10,6 +10,7 @@
|
||||
.general-btn-container {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
margin-top: 1rem;
|
||||
|
||||
.save-button {
|
||||
display: block;
|
||||
|
@@ -1,55 +1,35 @@
|
||||
import { Component, Injector, Input, OnInit, Type } from '@angular/core';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { SetDefaultLanguageResponse } from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-general-settings',
|
||||
templateUrl: './general-settings.component.html',
|
||||
styleUrls: ['./general-settings.component.scss'],
|
||||
})
|
||||
export class GeneralSettingsComponent implements OnInit {
|
||||
@Input() public serviceType!: PolicyComponentServiceType;
|
||||
public service!: ManagementService | AdminService;
|
||||
|
||||
public defaultLanguage: string = '';
|
||||
public defaultLanguageOptions: string[] = [];
|
||||
|
||||
public loading: boolean = false;
|
||||
constructor(private injector: Injector, private toast: ToastService) {}
|
||||
constructor(private service: AdminService, private toast: ToastService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
this.service = this.injector.get(ManagementService as Type<ManagementService>);
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
this.service = this.injector.get(AdminService as Type<AdminService>);
|
||||
break;
|
||||
}
|
||||
this.fetchData();
|
||||
}
|
||||
|
||||
private fetchData(): void {
|
||||
if (this.serviceType === PolicyComponentServiceType.ADMIN) {
|
||||
(this.service as AdminService).getDefaultLanguage().then((langResp) => {
|
||||
this.defaultLanguage = langResp.language;
|
||||
});
|
||||
(this.service as AdminService).getSupportedLanguages().then((supportedResp) => {
|
||||
this.defaultLanguageOptions = supportedResp.languagesList;
|
||||
});
|
||||
}
|
||||
this.service.getDefaultLanguage().then((langResp) => {
|
||||
this.defaultLanguage = langResp.language;
|
||||
});
|
||||
this.service.getSupportedLanguages().then((supportedResp) => {
|
||||
this.defaultLanguageOptions = supportedResp.languagesList;
|
||||
});
|
||||
}
|
||||
|
||||
private updateData(): Promise<SetDefaultLanguageResponse.AsObject> | void {
|
||||
if (this.serviceType === PolicyComponentServiceType.ADMIN) {
|
||||
return (this.service as AdminService).setDefaultLanguage(this.defaultLanguage);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
private updateData(): Promise<SetDefaultLanguageResponse.AsObject> {
|
||||
return (this.service as AdminService).setDefaultLanguage(this.defaultLanguage);
|
||||
}
|
||||
|
||||
public savePolicy(): void {
|
||||
@@ -68,21 +48,4 @@ export class GeneralSettingsComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public removePolicy(): void {
|
||||
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
(this.service as ManagementService)
|
||||
.resetLoginPolicyToDefault()
|
||||
.then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
|
||||
this.loading = true;
|
||||
setTimeout(() => {
|
||||
this.fetchData();
|
||||
}, 2000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -15,7 +15,6 @@
|
||||
justify-content: space-between;
|
||||
|
||||
button {
|
||||
margin-left: 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
@@ -21,7 +21,6 @@
|
||||
<div class="mfa-list-btns">
|
||||
<button
|
||||
mat-stroked-button
|
||||
color="primary"
|
||||
class="new-mfa cnsl-action-button"
|
||||
[disabled]="disabled"
|
||||
(click)="!disabled ? addMfa() : null"
|
||||
|
@@ -0,0 +1,48 @@
|
||||
<h1 mat-dialog-title class="title">
|
||||
<span>{{ 'SETTING.SMS.ADDPROVIDER' | translate }}</span>
|
||||
</h1>
|
||||
<div mat-dialog-content>
|
||||
<p class="desc cnsl-secondary-text">{{ 'SETTING.SMS.ADDPROVIDERDESCRIPTION' | translate }}</p>
|
||||
|
||||
<cnsl-form-field class="form-field" label="Access Code" required="true">
|
||||
<cnsl-label>{{ 'MFA.TYPE' | translate }}</cnsl-label>
|
||||
<mat-select [(ngModel)]="provider">
|
||||
<mat-option *ngFor="let prov of availableSMSProviders" [value]="prov">
|
||||
<span *ngIf="prov === SMSProviderType.Twilio">Twilio</span>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
|
||||
<form *ngIf="provider === SMSProviderType.Twilio" (ngSubmit)="closeDialogWithRequest()" [formGroup]="twilioForm">
|
||||
<h2>Twilio</h2>
|
||||
|
||||
<cnsl-form-field class="sms-form-field" label="sid">
|
||||
<cnsl-label>{{ 'SETTING.SMS.TWILIO.SID' | translate }}</cnsl-label>
|
||||
<input cnslInput name="sid" formControlName="sid" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="sms-form-field" label="Token">
|
||||
<cnsl-label>{{ 'SETTING.SMS.TWILIO.TOKEN' | translate }}</cnsl-label>
|
||||
<input cnslInput name="token" formControlName="token" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="sms-form-field" label="Sender Number">
|
||||
<cnsl-label>{{ 'SETTING.SMS.TWILIO.SENDERNUMBER' | translate }}</cnsl-label>
|
||||
<input cnslInput name="senderNumber" formControlName="senderNumber" />
|
||||
</cnsl-form-field>
|
||||
</form>
|
||||
</div>
|
||||
<div mat-dialog-actions class="action">
|
||||
<button mat-stroked-button (click)="closeDialog()">
|
||||
<span>{{ 'ACTIONS.CLOSE' | translate }}</span>
|
||||
</button>
|
||||
<button
|
||||
[disabled]="provider === SMSProviderType.Twilio && !twilioForm.valid"
|
||||
mat-raised-button
|
||||
class="ok-button"
|
||||
color="primary"
|
||||
(click)="closeDialogWithRequest()"
|
||||
>
|
||||
<span>{{ 'ACTIONS.OK' | translate }}</span>
|
||||
</button>
|
||||
</div>
|
@@ -0,0 +1,20 @@
|
||||
.title {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
button {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { AddSMSProviderTwilioRequest } from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
|
||||
enum SMSProviderType {
|
||||
Twilio = 1,
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-dialog-add-sms-provider',
|
||||
templateUrl: './dialog-add-sms-provider.component.html',
|
||||
styleUrls: ['./dialog-add-sms-provider.component.scss'],
|
||||
})
|
||||
export class DialogAddSMSProviderComponent {
|
||||
public SMSProviderType: any = SMSProviderType;
|
||||
public availableSMSProviders: SMSProviderType[] = [SMSProviderType.Twilio];
|
||||
public provider: SMSProviderType = SMSProviderType.Twilio;
|
||||
public req: AddSMSProviderTwilioRequest = new AddSMSProviderTwilioRequest();
|
||||
|
||||
public twilioForm!: FormGroup;
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
public dialogRef: MatDialogRef<DialogAddSMSProviderComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
) {
|
||||
this.twilioForm = this.fb.group({
|
||||
sid: ['', [Validators.required]],
|
||||
token: ['', [Validators.required]],
|
||||
senderNumber: ['', [Validators.required]],
|
||||
});
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
public closeDialogWithRequest(): void {
|
||||
this.req.setSid(this.sid?.value);
|
||||
this.req.setToken(this.token?.value);
|
||||
this.req.setSenderNumber(this.senderNumber?.value);
|
||||
|
||||
this.dialogRef.close(this.req);
|
||||
}
|
||||
|
||||
public get senderNumber(): AbstractControl | null {
|
||||
return this.twilioForm.get('senderNumber');
|
||||
}
|
||||
|
||||
public get token(): AbstractControl | null {
|
||||
return this.twilioForm.get('token');
|
||||
}
|
||||
|
||||
public get sid(): AbstractControl | null {
|
||||
return this.twilioForm.get('sid');
|
||||
}
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
<div class="spinner-wr">
|
||||
<mat-spinner diameter="30" *ngIf="loading" color="primary"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<h2>{{ 'SETTING.SMTP.TITLE' | translate }}</h2>
|
||||
|
||||
<form (ngSubmit)="savePolicy()" [formGroup]="form" autocomplete="off">
|
||||
<cnsl-form-field class="smtp-form-field" label="Sender Address" required="true">
|
||||
<cnsl-label>{{ 'SETTING.SMTP.SENDERADDRESS' | translate }}</cnsl-label>
|
||||
<input cnslInput name="senderAddress" formControlName="senderAddress" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="smtp-form-field" label="Sender Name" required="true">
|
||||
<cnsl-label>{{ 'SETTING.SMTP.SENDERNAME' | translate }}</cnsl-label>
|
||||
<input cnslInput name="senderName" formControlName="senderName" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<mat-checkbox class="smtp-checkbox" formControlName="tls">
|
||||
{{ 'SETTING.SMTP.TLS' | translate }}
|
||||
</mat-checkbox>
|
||||
|
||||
<cnsl-form-field class="smtp-form-field" label="Host" required="true">
|
||||
<cnsl-label>{{ 'SETTING.SMTP.HOST' | translate }}</cnsl-label>
|
||||
<input cnslInput name="host" formControlName="host" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="smtp-form-field" label="User" required="true">
|
||||
<cnsl-label>{{ 'SETTING.SMTP.USER' | translate }}</cnsl-label>
|
||||
<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>
|
||||
|
||||
<div class="general-btn-container">
|
||||
<button class="save-button" (click)="savePolicy()" color="primary" type="submit" mat-raised-button>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<br />
|
||||
<h2>{{ 'SETTING.SMS.TITLE' | translate }}</h2>
|
||||
<h3>{{ 'SETTING.SMS.PROVIDERS' | translate }}</h3>
|
||||
<div class="sms-providers">
|
||||
<cnsl-card *ngFor="let provider of smsProviders" class="sms-card">
|
||||
<div *ngIf="provider.twilio" class="sms-provider">
|
||||
<h4 class="title">Twilio</h4>
|
||||
<span class="cnsl-secondary-text">{{ 'SETTING.SMS.PROVIDER' | translate }}</span>
|
||||
|
||||
<span
|
||||
class="state"
|
||||
[ngClass]="{
|
||||
active: provider.state === SMSProviderConfigState.SMS_PROVIDER_CONFIG_ACTIVE,
|
||||
inactive: provider.state === SMSProviderConfigState.SMS_PROVIDER_CONFIG_INACTIVE
|
||||
}"
|
||||
></span>
|
||||
</div>
|
||||
</cnsl-card>
|
||||
<button mat-stroked-button (click)="addSMSProvider()">
|
||||
<div class="sms-card add">
|
||||
<mat-icon>add</mat-icon>
|
||||
<span>{{ 'ACTIONS.ADD' | translate }}</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
@@ -0,0 +1,55 @@
|
||||
.spinner-wr {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.smtp-form-field {
|
||||
max-width: 400px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.smtp-checkbox {
|
||||
max-width: 400px;
|
||||
display: block;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.general-btn-container {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
|
||||
.save-button {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.sms-providers {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.sms-card {
|
||||
margin-right: 1rem;
|
||||
|
||||
&.add {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.sms-provider {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-bottom: -0.5rem;
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
margin: 0 1rem 0 0;
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NotificationSettingsComponent } from './notification-settings.component';
|
||||
|
||||
describe('NotificationSettingsComponent', () => {
|
||||
let component: NotificationSettingsComponent;
|
||||
let fixture: ComponentFixture<NotificationSettingsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [NotificationSettingsComponent],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(NotificationSettingsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,158 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import {
|
||||
AddSMSProviderTwilioRequest,
|
||||
UpdateSMTPConfigPasswordResponse,
|
||||
UpdateSMTPConfigRequest,
|
||||
} from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import { SMSProvider, SMSProviderConfigState } from 'src/app/proto/generated/zitadel/settings_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
import { DialogAddSMSProviderComponent } from './dialog-add-sms-provider/dialog-add-sms-provider.component';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-notification-settings',
|
||||
templateUrl: './notification-settings.component.html',
|
||||
styleUrls: ['./notification-settings.component.scss'],
|
||||
})
|
||||
export class NotificationSettingsComponent implements OnInit {
|
||||
@Input() public serviceType!: PolicyComponentServiceType;
|
||||
public smsProviders: SMSProvider.AsObject[] = [];
|
||||
|
||||
public loading: boolean = false;
|
||||
public form!: FormGroup;
|
||||
|
||||
public SMSProviderConfigState: any = SMSProviderConfigState;
|
||||
constructor(
|
||||
private service: AdminService,
|
||||
private dialog: MatDialog,
|
||||
private toast: ToastService,
|
||||
private fb: FormBuilder,
|
||||
) {
|
||||
this.form = this.fb.group({
|
||||
senderAddress: ['', [Validators.required]],
|
||||
senderName: ['', [Validators.required]],
|
||||
tls: [true, [Validators.required]],
|
||||
host: ['', [Validators.required]],
|
||||
user: ['', [Validators.required]],
|
||||
password: ['', [Validators.required]],
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.fetchData();
|
||||
}
|
||||
|
||||
private fetchData(): void {
|
||||
this.service
|
||||
.getSMTPConfig()
|
||||
.then((smtpConfig) => {
|
||||
if (smtpConfig.smtpConfig) {
|
||||
this.form.patchValue(smtpConfig.smtpConfig);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error && error.code === 5) {
|
||||
console.log(error);
|
||||
}
|
||||
});
|
||||
|
||||
this.service.listSMSProviders().then((smsProviders) => {
|
||||
if (smsProviders.resultList) {
|
||||
this.smsProviders = smsProviders.resultList;
|
||||
console.log(this.smsProviders);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private updateData(): Promise<UpdateSMTPConfigPasswordResponse.AsObject> | any {
|
||||
const req = new UpdateSMTPConfigRequest();
|
||||
req.setHost(this.host?.value ?? '');
|
||||
req.setSenderAddress(this.senderAddress?.value ?? '');
|
||||
req.setSenderName(this.senderName?.value ?? '');
|
||||
req.setTls(this.tls?.value ?? false);
|
||||
req.setUser(this.user?.value ?? '');
|
||||
|
||||
console.log(req.toObject());
|
||||
|
||||
// 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;
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
public savePolicy(): void {
|
||||
const prom = this.updateData();
|
||||
if (prom) {
|
||||
prom
|
||||
.then(() => {
|
||||
this.toast.showInfo('SETTING.SMTP.SAVED', true);
|
||||
this.loading = true;
|
||||
setTimeout(() => {
|
||||
this.fetchData();
|
||||
}, 2000);
|
||||
})
|
||||
.catch((error: unknown) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public addSMSProvider(): void {
|
||||
const dialogRef = this.dialog.open(DialogAddSMSProviderComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.DELETE',
|
||||
cancelKey: 'ACTIONS.CANCEL',
|
||||
titleKey: 'IDP.DELETE_TITLE',
|
||||
descriptionKey: 'IDP.DELETE_DESCRIPTION',
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe((req: AddSMSProviderTwilioRequest) => {
|
||||
if (req) {
|
||||
this.service
|
||||
.addSMSProviderTwilio(req)
|
||||
.then(() => {
|
||||
this.toast.showInfo('SETTING.SMS.TWILIO.ADDED', true);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public get senderAddress(): AbstractControl | null {
|
||||
return this.form.get('senderAddress');
|
||||
}
|
||||
|
||||
public get senderName(): AbstractControl | null {
|
||||
return this.form.get('senderName');
|
||||
}
|
||||
|
||||
public get tls(): AbstractControl | null {
|
||||
return this.form.get('tls');
|
||||
}
|
||||
|
||||
public get user(): AbstractControl | null {
|
||||
return this.form.get('user');
|
||||
}
|
||||
|
||||
public get host(): AbstractControl | null {
|
||||
return this.form.get('host');
|
||||
}
|
||||
|
||||
public get password(): AbstractControl | null {
|
||||
return this.form.get('password');
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } 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 { MatSelectModule } from '@angular/material/select';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { CardModule } from '../../card/card.module';
|
||||
import { FormFieldModule } from '../../form-field/form-field.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';
|
||||
|
||||
@NgModule({
|
||||
declarations: [NotificationSettingsComponent, DialogAddSMSProviderComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
CardModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MatButtonModule,
|
||||
MatCheckboxModule,
|
||||
InputModule,
|
||||
MatIconModule,
|
||||
FormFieldModule,
|
||||
MatSelectModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatSelectModule,
|
||||
TranslateModule,
|
||||
],
|
||||
exports: [NotificationSettingsComponent],
|
||||
})
|
||||
export class NotificationSettingsModule {}
|
@@ -0,0 +1,45 @@
|
||||
<div class="spinner-wr">
|
||||
<mat-spinner diameter="30" *ngIf="loading" color="primary"></mat-spinner>
|
||||
</div>
|
||||
<h2>{{ 'SETTING.OIDC.TITLE' | translate }}</h2>
|
||||
<form (ngSubmit)="savePolicy()" [formGroup]="form" autocomplete="off">
|
||||
<cnsl-form-field class="oidc-form-field" label="Sender Address" required="true">
|
||||
<cnsl-label
|
||||
>{{ 'SETTING.OIDC.ACCESSTOKENLIFETIME' | translate }} <strong
|
||||
>({{ 'SETTING.OIDC.INHOURS' | translate }})</strong
|
||||
></cnsl-label
|
||||
>
|
||||
<input cnslInput type="number" name="accessTokenLifetime" formControlName="accessTokenLifetime" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="oidc-form-field" label="Sender Name" required="true">
|
||||
<cnsl-label
|
||||
>{{ 'SETTING.OIDC.IDTOKENLIFETIME' | translate }} <strong
|
||||
>({{ 'SETTING.OIDC.INHOURS' | translate }})</strong
|
||||
></cnsl-label
|
||||
>
|
||||
<input cnslInput type="number" name="idTokenLifetime" formControlName="idTokenLifetime" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="oidc-form-field" label="Sender Address" required="true">
|
||||
<cnsl-label
|
||||
>{{ 'SETTING.OIDC.REFRESHTOKENEXPIRATION' | translate }} <strong
|
||||
>({{ 'SETTING.OIDC.INDAYS' | translate }})</strong
|
||||
></cnsl-label
|
||||
>
|
||||
<input cnslInput type="number" name="refreshTokenExpiration" formControlName="refreshTokenExpiration" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="oidc-form-field" label="Sender Name" required="true">
|
||||
<cnsl-label
|
||||
>{{ 'SETTING.OIDC.REFRESHTOKENIDLEEXPIRATION' | translate }}
|
||||
<strong>({{ 'SETTING.OIDC.INDAYS' | translate }})</strong></cnsl-label
|
||||
>
|
||||
<input cnslInput type="number" name="refreshTokenIdleExpiration" formControlName="refreshTokenIdleExpiration" />
|
||||
</cnsl-form-field>
|
||||
</form>
|
||||
<div class="oidc-btn-container">
|
||||
<button class="save-button" (click)="savePolicy()" color="primary" type="submit" mat-raised-button>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
@@ -0,0 +1,18 @@
|
||||
.spinner-wr {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.oidc-form-field {
|
||||
max-width: 400px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.oidc-btn-container {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
margin-top: 1rem;
|
||||
|
||||
.save-button {
|
||||
display: block;
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { OIDCConfigurationComponent } from './oidc-configuration.component';
|
||||
|
||||
describe('OIDCConfigurationComponent', () => {
|
||||
let component: OIDCConfigurationComponent;
|
||||
let fixture: ComponentFixture<OIDCConfigurationComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [OIDCConfigurationComponent],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(OIDCConfigurationComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,119 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { Duration } from 'google-protobuf/google/protobuf/duration_pb';
|
||||
import { SetDefaultLanguageResponse, UpdateOIDCSettingsRequest } from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import { OIDCSettings } from 'src/app/proto/generated/zitadel/settings_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-oidc-configuration',
|
||||
templateUrl: './oidc-configuration.component.html',
|
||||
styleUrls: ['./oidc-configuration.component.scss'],
|
||||
})
|
||||
export class OIDCConfigurationComponent implements OnInit {
|
||||
public oidcSettings!: OIDCSettings.AsObject;
|
||||
|
||||
public loading: boolean = false;
|
||||
public form!: FormGroup;
|
||||
constructor(private service: AdminService, private fb: FormBuilder, private toast: ToastService) {
|
||||
this.form = this.fb.group({
|
||||
accessTokenLifetime: [12, [Validators.required]],
|
||||
idTokenLifetime: [12, [Validators.required]],
|
||||
refreshTokenExpiration: [30, [Validators.required]],
|
||||
refreshTokenIdleExpiration: [90, [Validators.required]],
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.fetchData();
|
||||
}
|
||||
|
||||
private fetchData(): void {
|
||||
this.service
|
||||
.getOIDCSettings()
|
||||
.then((oidcConfiguration) => {
|
||||
if (oidcConfiguration.settings) {
|
||||
this.oidcSettings = oidcConfiguration.settings;
|
||||
|
||||
this.accessTokenLifetime?.setValue(
|
||||
oidcConfiguration.settings.accessTokenLifetime?.seconds
|
||||
? oidcConfiguration.settings.accessTokenLifetime?.seconds / 60 / 60
|
||||
: 12,
|
||||
);
|
||||
this.idTokenLifetime?.setValue(
|
||||
oidcConfiguration.settings.idTokenLifetime?.seconds
|
||||
? oidcConfiguration.settings.idTokenLifetime?.seconds / 60 / 60
|
||||
: 12,
|
||||
);
|
||||
this.refreshTokenExpiration?.setValue(
|
||||
oidcConfiguration.settings.refreshTokenExpiration?.seconds
|
||||
? oidcConfiguration.settings.refreshTokenExpiration?.seconds / 60 / 60 / 24
|
||||
: 30,
|
||||
);
|
||||
this.refreshTokenIdleExpiration?.setValue(
|
||||
oidcConfiguration.settings.refreshTokenIdleExpiration?.seconds
|
||||
? oidcConfiguration.settings.refreshTokenIdleExpiration?.seconds / 60 / 60 / 24
|
||||
: 90,
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.code === 5) {
|
||||
} else {
|
||||
this.toast.showError(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private updateData(): Promise<SetDefaultLanguageResponse.AsObject> {
|
||||
const req = new UpdateOIDCSettingsRequest();
|
||||
|
||||
const accessToken = new Duration().setSeconds((this.accessTokenLifetime?.value ?? 12) * 60 * 60);
|
||||
req.setAccessTokenLifetime(accessToken);
|
||||
|
||||
const idToken = new Duration().setSeconds((this.idTokenLifetime?.value ?? 12) * 60 * 60);
|
||||
req.setIdTokenLifetime(idToken);
|
||||
|
||||
const refreshToken = new Duration().setSeconds((this.refreshTokenExpiration?.value ?? 30) * 60 * 60 * 24);
|
||||
req.setRefreshTokenExpiration(refreshToken);
|
||||
|
||||
const refreshIdleToken = new Duration().setSeconds((this.refreshTokenIdleExpiration?.value ?? 90) * 60 * 60 * 24);
|
||||
req.setRefreshTokenIdleExpiration(refreshIdleToken);
|
||||
|
||||
return (this.service as AdminService).updateOIDCSettings(req);
|
||||
}
|
||||
|
||||
public savePolicy(): void {
|
||||
const prom = this.updateData();
|
||||
if (prom) {
|
||||
prom
|
||||
.then(() => {
|
||||
this.toast.showInfo('SETTING.SMTP.SAVED', true);
|
||||
this.loading = true;
|
||||
setTimeout(() => {
|
||||
this.fetchData();
|
||||
}, 2000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public get accessTokenLifetime(): AbstractControl | null {
|
||||
return this.form.get('accessTokenLifetime');
|
||||
}
|
||||
|
||||
public get idTokenLifetime(): AbstractControl | null {
|
||||
return this.form.get('idTokenLifetime');
|
||||
}
|
||||
|
||||
public get refreshTokenExpiration(): AbstractControl | null {
|
||||
return this.form.get('refreshTokenExpiration');
|
||||
}
|
||||
|
||||
public get refreshTokenIdleExpiration(): AbstractControl | null {
|
||||
return this.form.get('refreshTokenIdleExpiration');
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { CardModule } from '../../card/card.module';
|
||||
import { FormFieldModule } from '../../form-field/form-field.module';
|
||||
import { InputModule } from '../../input/input.module';
|
||||
import { OIDCConfigurationComponent } from './oidc-configuration.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [OIDCConfigurationComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
CardModule,
|
||||
FormsModule,
|
||||
MatButtonModule,
|
||||
FormFieldModule,
|
||||
ReactiveFormsModule,
|
||||
InputModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatSelectModule,
|
||||
TranslateModule,
|
||||
],
|
||||
exports: [OIDCConfigurationComponent],
|
||||
})
|
||||
export class OIDCConfigurationModule {}
|
@@ -42,9 +42,7 @@ export class PasswordLockoutPolicyComponent implements OnInit {
|
||||
}
|
||||
|
||||
private fetchData(): void {
|
||||
console.log(this.serviceType);
|
||||
this.getData().then((resp) => {
|
||||
console.log(resp);
|
||||
if (resp.policy) {
|
||||
this.lockoutData = resp.policy;
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@
|
||||
queryParam="id"
|
||||
>
|
||||
<ng-container *ngIf="currentSetting === 'general' && serviceType === PolicyComponentServiceType.ADMIN">
|
||||
<cnsl-general-settings [serviceType]="serviceType"></cnsl-general-settings>
|
||||
<cnsl-general-settings></cnsl-general-settings>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="currentSetting === 'complexity'">
|
||||
<cnsl-password-complexity-policy [serviceType]="serviceType"></cnsl-password-complexity-policy>
|
||||
@@ -22,6 +22,14 @@
|
||||
<ng-container *ngIf="currentSetting === 'idp'">
|
||||
<cnsl-idp-settings [serviceType]="serviceType"></cnsl-idp-settings>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="currentSetting === 'notifications' && serviceType === PolicyComponentServiceType.ADMIN">
|
||||
<cnsl-notification-settings [serviceType]="serviceType"></cnsl-notification-settings>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="currentSetting === 'oidc' && serviceType === PolicyComponentServiceType.ADMIN">
|
||||
<cnsl-oidc-configuration></cnsl-oidc-configuration>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="currentSetting === 'branding'">
|
||||
<cnsl-private-labeling-policy [serviceType]="serviceType"></cnsl-private-labeling-policy>
|
||||
</ng-container>
|
||||
|
@@ -10,6 +10,8 @@ import { IdpSettingsModule } from '../policies/idp-settings/idp-settings.module'
|
||||
import { LoginPolicyModule } from '../policies/login-policy/login-policy.module';
|
||||
import { LoginTextsPolicyModule } from '../policies/login-texts/login-texts.module';
|
||||
import { MessageTextsPolicyModule } from '../policies/message-texts/message-texts.module';
|
||||
import { 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';
|
||||
@@ -37,6 +39,8 @@ import { SettingsListComponent } from './settings-list.component';
|
||||
OrgIamPolicyModule,
|
||||
TranslateModule,
|
||||
HasRolePipeModule,
|
||||
NotificationSettingsModule,
|
||||
OIDCConfigurationModule,
|
||||
],
|
||||
exports: [SettingsListComponent],
|
||||
})
|
||||
|
@@ -5,6 +5,11 @@ export const GENERAL: SidenavSetting = {
|
||||
i18nKey: 'SETTINGS.LIST.GENERAL',
|
||||
};
|
||||
|
||||
export const OIDC: SidenavSetting = {
|
||||
id: 'oidc',
|
||||
i18nKey: 'SETTINGS.LIST.OIDC',
|
||||
};
|
||||
|
||||
export const LOGIN: SidenavSetting = {
|
||||
id: 'login',
|
||||
i18nKey: 'SETTINGS.LIST.LOGIN',
|
||||
@@ -29,12 +34,6 @@ export const COMPLEXITY: SidenavSetting = {
|
||||
|
||||
export const IDP: SidenavSetting = { id: 'idp', i18nKey: 'SETTINGS.LIST.IDP', groupI18nKey: 'SETTINGS.GROUPS.LOGIN' };
|
||||
|
||||
export const NOTIFICATIONPROVIDERS: SidenavSetting = {
|
||||
id: 'notificationproviders',
|
||||
i18nKey: 'SETTINGS.LIST.NOTIFICATIONPROVIDERS',
|
||||
groupI18nKey: 'SETTINGS.GROUPS.NOTIFICATIONS',
|
||||
};
|
||||
|
||||
export const NOTIFICATIONS: SidenavSetting = {
|
||||
id: 'notifications',
|
||||
i18nKey: 'SETTINGS.LIST.NOTIFICATIONS',
|
||||
|
@@ -14,8 +14,8 @@ import {
|
||||
LOGIN,
|
||||
LOGINTEXTS,
|
||||
MESSAGETEXTS,
|
||||
NOTIFICATIONPROVIDERS,
|
||||
NOTIFICATIONS,
|
||||
OIDC,
|
||||
PRIVACYPOLICY,
|
||||
} from '../../modules/settings-list/settings';
|
||||
|
||||
@@ -29,16 +29,20 @@ export class InstanceSettingsComponent {
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
public settingsList: SidenavSetting[] = [
|
||||
GENERAL,
|
||||
// notifications
|
||||
NOTIFICATIONS,
|
||||
// login
|
||||
LOGIN,
|
||||
COMPLEXITY,
|
||||
LOCKOUT,
|
||||
IDP,
|
||||
NOTIFICATIONS,
|
||||
NOTIFICATIONPROVIDERS,
|
||||
// appearance
|
||||
BRANDING,
|
||||
MESSAGETEXTS,
|
||||
LOGINTEXTS,
|
||||
// others
|
||||
PRIVACYPOLICY,
|
||||
OIDC,
|
||||
];
|
||||
constructor(breadcrumbService: BreadcrumbService, activatedRoute: ActivatedRoute) {
|
||||
const breadcrumbs = [
|
||||
|
@@ -13,8 +13,6 @@ import {
|
||||
LOGIN,
|
||||
LOGINTEXTS,
|
||||
MESSAGETEXTS,
|
||||
NOTIFICATIONPROVIDERS,
|
||||
NOTIFICATIONS,
|
||||
PRIVACYPOLICY,
|
||||
} from '../../modules/settings-list/settings';
|
||||
|
||||
@@ -31,8 +29,6 @@ export class OrgSettingsComponent {
|
||||
COMPLEXITY,
|
||||
LOCKOUT,
|
||||
IDP,
|
||||
NOTIFICATIONS,
|
||||
NOTIFICATIONPROVIDERS,
|
||||
BRANDING,
|
||||
MESSAGETEXTS,
|
||||
LOGINTEXTS,
|
||||
|
@@ -17,6 +17,8 @@ import {
|
||||
AddOIDCIDPResponse,
|
||||
AddSecondFactorToLoginPolicyRequest,
|
||||
AddSecondFactorToLoginPolicyResponse,
|
||||
AddSMSProviderTwilioRequest,
|
||||
AddSMSProviderTwilioResponse,
|
||||
DeactivateIDPRequest,
|
||||
DeactivateIDPResponse,
|
||||
GetCustomDomainClaimedMessageTextRequest,
|
||||
@@ -51,6 +53,8 @@ import {
|
||||
GetDefaultVerifyEmailMessageTextResponse,
|
||||
GetDefaultVerifyPhoneMessageTextRequest,
|
||||
GetDefaultVerifyPhoneMessageTextResponse,
|
||||
GetFileSystemNotificationProviderRequest,
|
||||
GetFileSystemNotificationProviderResponse,
|
||||
GetIDPByIDRequest,
|
||||
GetIDPByIDResponse,
|
||||
GetLabelPolicyRequest,
|
||||
@@ -59,6 +63,10 @@ import {
|
||||
GetLockoutPolicyResponse,
|
||||
GetLoginPolicyRequest,
|
||||
GetLoginPolicyResponse,
|
||||
GetLogNotificationProviderRequest,
|
||||
GetLogNotificationProviderResponse,
|
||||
GetOIDCSettingsRequest,
|
||||
GetOIDCSettingsResponse,
|
||||
GetOrgIAMPolicyRequest,
|
||||
GetOrgIAMPolicyResponse,
|
||||
GetPasswordAgePolicyRequest,
|
||||
@@ -69,6 +77,10 @@ import {
|
||||
GetPreviewLabelPolicyResponse,
|
||||
GetPrivacyPolicyRequest,
|
||||
GetPrivacyPolicyResponse,
|
||||
GetSMSProviderRequest,
|
||||
GetSMSProviderResponse,
|
||||
GetSMTPConfigRequest,
|
||||
GetSMTPConfigResponse,
|
||||
GetSupportedLanguagesRequest,
|
||||
GetSupportedLanguagesResponse,
|
||||
IDPQuery,
|
||||
@@ -86,6 +98,8 @@ import {
|
||||
ListLoginPolicyMultiFactorsResponse,
|
||||
ListLoginPolicySecondFactorsRequest,
|
||||
ListLoginPolicySecondFactorsResponse,
|
||||
ListSMSProvidersRequest,
|
||||
ListSMSProvidersResponse,
|
||||
ListViewsRequest,
|
||||
ListViewsResponse,
|
||||
ReactivateIDPRequest,
|
||||
@@ -150,6 +164,8 @@ import {
|
||||
UpdateLockoutPolicyResponse,
|
||||
UpdateLoginPolicyRequest,
|
||||
UpdateLoginPolicyResponse,
|
||||
UpdateOIDCSettingsRequest,
|
||||
UpdateOIDCSettingsResponse,
|
||||
UpdateOrgIAMPolicyRequest,
|
||||
UpdateOrgIAMPolicyResponse,
|
||||
UpdatePasswordAgePolicyRequest,
|
||||
@@ -158,6 +174,10 @@ import {
|
||||
UpdatePasswordComplexityPolicyResponse,
|
||||
UpdatePrivacyPolicyRequest,
|
||||
UpdatePrivacyPolicyResponse,
|
||||
UpdateSMTPConfigPasswordRequest,
|
||||
UpdateSMTPConfigPasswordResponse,
|
||||
UpdateSMTPConfigRequest,
|
||||
UpdateSMTPConfigResponse,
|
||||
} from '../proto/generated/zitadel/admin_pb';
|
||||
import { SearchQuery } from '../proto/generated/zitadel/member_pb';
|
||||
import { ListQuery } from '../proto/generated/zitadel/object_pb';
|
||||
@@ -433,6 +453,37 @@ export class AdminService {
|
||||
return this.grpcService.admin.setDefaultLanguage(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
/* notification settings */
|
||||
|
||||
public getSMTPConfig(): Promise<GetSMTPConfigResponse.AsObject> {
|
||||
const req = new GetSMTPConfigRequest();
|
||||
return this.grpcService.admin.getSMTPConfig(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public updateSMTPConfig(req: UpdateSMTPConfigRequest): Promise<UpdateSMTPConfigResponse.AsObject> {
|
||||
return this.grpcService.admin.updateSMTPConfig(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public updateSMTPConfigPassword(req: UpdateSMTPConfigPasswordRequest): Promise<UpdateSMTPConfigPasswordResponse.AsObject> {
|
||||
return this.grpcService.admin.updateSMTPConfigPassword(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
/* sms */
|
||||
|
||||
public listSMSProviders(): Promise<ListSMSProvidersResponse.AsObject> {
|
||||
const req = new ListSMSProvidersRequest();
|
||||
return this.grpcService.admin.listSMSProviders(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getSMSProvider(): Promise<GetSMSProviderResponse.AsObject> {
|
||||
const req = new GetSMSProviderRequest();
|
||||
return this.grpcService.admin.getSMSProvider(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public addSMSProviderTwilio(req: AddSMSProviderTwilioRequest): Promise<AddSMSProviderTwilioResponse.AsObject> {
|
||||
return this.grpcService.admin.addSMSProviderTwilio(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
/* lockout */
|
||||
|
||||
public getLockoutPolicy(): Promise<GetLockoutPolicyResponse.AsObject> {
|
||||
@@ -504,6 +555,30 @@ export class AdminService {
|
||||
return this.grpcService.admin.updateLoginPolicy(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
/* OIDC Configuration */
|
||||
|
||||
public getOIDCSettings(): Promise<GetOIDCSettingsResponse.AsObject> {
|
||||
const req = new GetOIDCSettingsRequest();
|
||||
return this.grpcService.admin.getOIDCSettings(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public updateOIDCSettings(req: UpdateOIDCSettingsRequest): Promise<UpdateOIDCSettingsResponse.AsObject> {
|
||||
return this.grpcService.admin.updateOIDCSettings(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
/* LOG and FILE Notifications */
|
||||
|
||||
public getLogNotificationProvider(): Promise<GetLogNotificationProviderResponse.AsObject> {
|
||||
const req = new GetLogNotificationProviderRequest();
|
||||
return this.grpcService.admin.getLogNotificationProvider(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getFileSystemNotificationProvider(
|
||||
req: GetFileSystemNotificationProviderRequest,
|
||||
): Promise<GetFileSystemNotificationProviderResponse.AsObject> {
|
||||
return this.grpcService.admin.getFileSystemNotificationProvider(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
/* org iam */
|
||||
|
||||
public getCustomOrgIAMPolicy(orgId: string): Promise<GetCustomOrgIAMPolicyResponse.AsObject> {
|
||||
|
@@ -816,12 +816,12 @@
|
||||
"LOCKOUT": "Sperrmechanismen",
|
||||
"COMPLEXITY": "Passwordkomplexität",
|
||||
"NOTIFICATIONS": "Benachrichtigungen",
|
||||
"NOTIFICATIONPROVIDERS": "Anbieter und SMTP",
|
||||
"MESSAGETEXTS": "Benachrichtigungstexte",
|
||||
"IDP": "Identity Provider",
|
||||
"LOGINTEXTS": "Login Interface Texte",
|
||||
"BRANDING": "Branding",
|
||||
"PRIVACYPOLICY": "Datenschutzrichtlinie"
|
||||
"PRIVACYPOLICY": "Datenschutzrichtlinie",
|
||||
"OIDC": "OIDC Konfiguration"
|
||||
},
|
||||
"GROUPS": {
|
||||
"NOTIFICATIONS": "Benachrichtigungen",
|
||||
@@ -837,6 +837,38 @@
|
||||
"de": "Deutsch",
|
||||
"it": "Italiano",
|
||||
"en": "English"
|
||||
},
|
||||
"SMTP": {
|
||||
"TITLE": "SMTP Einstellungen",
|
||||
"SENDERADDRESS": "Sender Email-Adresse",
|
||||
"SENDERNAME": "Sender Name",
|
||||
"HOST": "Host",
|
||||
"USER": "Benutzer",
|
||||
"PASSWORD": "Passwort",
|
||||
"TLS": "Transport Layer Security (TLS)",
|
||||
"SAVED": "Erfolgreich gespeichert."
|
||||
},
|
||||
"SMS": {
|
||||
"TITLE": "SMS Einstellungen",
|
||||
"PROVIDERS": "Anbieter",
|
||||
"PROVIDER": "SMS Anbieter",
|
||||
"ADDPROVIDER": "Anbieter hinzufügen",
|
||||
"ADDPROVIDERDESCRIPTION": "Wählen Sie einen der verfügbaren Anbieter und geben Sie die erforderlichen Daten ein.",
|
||||
"TWILIO": {
|
||||
"SID": "Sid",
|
||||
"TOKEN": "Token",
|
||||
"SENDERNUMBER": "Sender Number",
|
||||
"ADDED": "Twilio erfolgreich hinzugefügt."
|
||||
}
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "OIDC Einstellungen",
|
||||
"ACCESSTOKENLIFETIME": "Access Token Lifetime",
|
||||
"IDTOKENLIFETIME": "Id Token Lifetime",
|
||||
"REFRESHTOKENEXPIRATION": "Refresh Token Expiration",
|
||||
"REFRESHTOKENIDLEEXPIRATION": "Refresh Token Idle Expiration",
|
||||
"INHOURS": "Stunden",
|
||||
"INDAYS": "Tage"
|
||||
}
|
||||
},
|
||||
"POLICY": {
|
||||
|
@@ -815,13 +815,13 @@
|
||||
"LOGIN": "Login Behaviour and Security",
|
||||
"LOCKOUT": "Lockout",
|
||||
"COMPLEXITY": "Password complexity",
|
||||
"NOTIFICATIONS": "Notifications",
|
||||
"NOTIFICATIONPROVIDERS": "Notification providers and SMTP",
|
||||
"NOTIFICATIONS": "Notification providers and SMTP",
|
||||
"MESSAGETEXTS": "Message Texts",
|
||||
"IDP": "Identity Providers",
|
||||
"LOGINTEXTS": "Login Interface Texts",
|
||||
"BRANDING": "Branding",
|
||||
"PRIVACYPOLICY": "Privacy Policy"
|
||||
"PRIVACYPOLICY": "Privacy Policy",
|
||||
"OIDC": "OIDC Configuration"
|
||||
},
|
||||
"GROUPS": {
|
||||
"NOTIFICATIONS": "Notifications",
|
||||
@@ -837,6 +837,38 @@
|
||||
"de": "Deutsch",
|
||||
"it": "Italiano",
|
||||
"en": "English"
|
||||
},
|
||||
"SMTP": {
|
||||
"TITLE": "SMTP Settings",
|
||||
"SENDERADDRESS": "Sender Email address",
|
||||
"SENDERNAME": "Sender Name",
|
||||
"HOST": "Host",
|
||||
"USER": "User",
|
||||
"PASSWORD": "Password",
|
||||
"TLS": "Transport Layer Security (TLS)",
|
||||
"SAVED": "Saved successfully!"
|
||||
},
|
||||
"SMS": {
|
||||
"TITLE": "SMS Settings",
|
||||
"PROVIDERS": "Providers",
|
||||
"PROVIDER": "SMS Provider",
|
||||
"ADDPROVIDER": "Add SMS Provider",
|
||||
"ADDPROVIDERDESCRIPTION": "Choose one of the available providers and enter the required data.",
|
||||
"TWILIO": {
|
||||
"SID": "Sid",
|
||||
"TOKEN": "Token",
|
||||
"SENDERNUMBER": "Sender Number",
|
||||
"ADDED": "Twilio added successfully."
|
||||
}
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "OIDC Settings",
|
||||
"ACCESSTOKENLIFETIME": "Access Token Lifetime",
|
||||
"IDTOKENLIFETIME": "Id Token Lifetime",
|
||||
"REFRESHTOKENEXPIRATION": "Refresh Token Expiration",
|
||||
"REFRESHTOKENIDLEEXPIRATION": "Refresh Token Idle Expiration",
|
||||
"INHOURS": "hours",
|
||||
"INDAYS": "Days"
|
||||
}
|
||||
},
|
||||
"POLICY": {
|
||||
|
@@ -806,18 +806,22 @@
|
||||
"TITLE": "Impostazioni dell'istanza",
|
||||
"DESCRIPTION": "Queste impostazioni si applicheranno a tutte le tue organizzazioni a meno che l'impostazione non venga sovrascritta."
|
||||
},
|
||||
"ORG": {
|
||||
"TITLE": "Impostazioni dell'organizzazione",
|
||||
"DESCRIPTION": "Queste impostazioni si applicheranno alla organizzazione corrente."
|
||||
},
|
||||
"LIST": {
|
||||
"GENERAL": "Generale",
|
||||
"LOGIN": "Comportamento login e sicurezza",
|
||||
"LOCKOUT": "Meccanismi di bloccaggio",
|
||||
"COMPLEXITY": "complessità della password",
|
||||
"NOTIFICATIONS": "Notifiche",
|
||||
"NOTIFICATIONPROVIDERS": "Fornitori e SMTP",
|
||||
"MESSAGETEXTS": "Testi di notifica",
|
||||
"IDP": "Identity Providers",
|
||||
"LOGINTEXTS": "Testi dell'interfaccia login",
|
||||
"BRANDING": "Branding",
|
||||
"PRIVACYPOLICY": "Informativa sulla privacy e TOS"
|
||||
"PRIVACYPOLICY": "Informativa sulla privacy e TOS",
|
||||
"OIDC": "OIDC Configuration"
|
||||
},
|
||||
"GROUPS": {
|
||||
"NOTIFICATIONS": "Notifiche",
|
||||
@@ -833,6 +837,38 @@
|
||||
"de": "Deutsch",
|
||||
"it": "Italiano",
|
||||
"en": "English"
|
||||
},
|
||||
"SMTP": {
|
||||
"TITLE": "Impostazioni SMTP",
|
||||
"SENDERADDRESS": "Indirizzo email del mittente",
|
||||
"SENDERNAME": "Nome del mittente",
|
||||
"HOST": "Host",
|
||||
"USER": "Utente",
|
||||
"PASSWORD": "Password",
|
||||
"TLS": "Transport Layer Security (TLS)",
|
||||
"SAVED": "Salvato con successo!"
|
||||
},
|
||||
"SMS": {
|
||||
"TITLE": "Impostazioni SMS",
|
||||
"PROVIDERS": "Fornitori",
|
||||
"PROVIDER": "Fornitore SMS",
|
||||
"ADDPROVIDER": "Aggiungi fornitore SMS",
|
||||
"ADDPROVIDERDESCRIPTION": "Scegli uno dei provider disponibili e inserisci i dati richiesti.",
|
||||
"TWILIO": {
|
||||
"SID": "Sid",
|
||||
"TOKEN": "Token",
|
||||
"SENDERNUMBER": "Sender Number",
|
||||
"ADDED": "Twilio aggiunto con successo."
|
||||
}
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "OIDC Einstellungen",
|
||||
"ACCESSTOKENLIFETIME": "Access Token Lifetime",
|
||||
"IDTOKENLIFETIME": "Id Token Lifetime",
|
||||
"REFRESHTOKENEXPIRATION": "Refresh Token Expiration",
|
||||
"REFRESHTOKENIDLEEXPIRATION": "Refresh Token Idle Expiration",
|
||||
"INHOURS": "ore",
|
||||
"INDAYS": "giorni"
|
||||
}
|
||||
},
|
||||
"POLICY": {
|
||||
|
Reference in New Issue
Block a user