feat: rehauled console (#3525)

* new console

* move npm ci to angular build

* rel path for assets

* local grpc copy

* login policy, rm clear views, features rel path

* lock

Co-authored-by: Livio Amstutz <[email protected]>
This commit is contained in:
Max Peintner
2022-04-28 10:35:02 +00:00
committed by GitHub
co-authored by Livio Amstutz
parent 00f7dbe875
commit 08ae39ae19
668 changed files with 47746 additions and 19117 deletions
@@ -9,12 +9,12 @@
button {
margin-bottom: 14px;
margin-right: -.5rem;
margin-right: -0.5rem;
}
}
.uri-list {
margin: 0 .5rem;
margin: 0 0.5rem;
width: 100%;
.uri-line {
@@ -24,6 +24,7 @@
.uri {
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
font-size: 14px;
overflow-x: auto;
}
.fill-space {
@@ -58,5 +59,5 @@
.error {
font-size: 13px;
color: var(--warn);
margin: 0 .5rem 1.5rem .5rem;
margin: 0 0.5rem 1.5rem 0.5rem;
}
@@ -1,4 +1,4 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Observable, Subscription } from 'rxjs';
@@ -12,14 +12,14 @@ export class AdditionalOriginsComponent implements OnInit, OnDestroy {
@Input() canWrite: boolean = false;
@Input() public urisList: string[] = [];
@Input() public redirectControl: FormControl = new FormControl({ value: '', disabled: true });
@Input() public changedUris: EventEmitter<string[]> = new EventEmitter();
@Output() public changedUris: EventEmitter<string[]> = new EventEmitter<string[]>();
@Input() public getValues: Observable<void> = new Observable();
public placeholder: string = '<scheme> "://" <hostname> [ ":" <port> ]';
@ViewChild('originInput') input!: any;
private sub: Subscription = new Subscription();
constructor() { }
constructor() {}
ngOnInit(): void {
if (this.canWrite) {
@@ -1,270 +1,275 @@
<div class="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>
</div>
<div class="app-create-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>
</div>
<h1>{{'APP.PAGES.CREATE_OIDC_DESC_TITLE' | translate}}</h1>
<p class="desc">{{'APP.PAGES.CREATE_OIDC_DESC_SUB' | translate}}</p>
<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-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-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-step [stepControl]="firstFormGroup" [editable]="true">
<form [formGroup]="firstFormGroup">
<ng-template matStepLabel>{{'APP.OIDC.NAMEANDTYPESECTION' | translate}}</ng-template>
<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-label>{{ 'APP.NAME' | translate }}</cnsl-label>
<input cnslInput cdkFocusInitial formControlName="name" />
<span cnslError *ngIf="name?.errors?.required">{{'PROJECT.APP.NAMEREQUIRED' | translate}}</span>
</cnsl-form-field>
<p class="step-title">{{'APP.OIDC.TITLEFIRST' | translate}}</p>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'APP.NAME' | translate }}</cnsl-label>
<input cnslInput cdkFocusInitial formControlName="name" />
<span cnslError *ngIf="name?.errors?.required">{{'PROJECT.APP.NAMEREQUIRED' | translate}}</span>
</cnsl-form-field>
<p class="step-title">{{'APP.OIDC.TYPETITLE' | translate}}</p>
<p class="step-title">{{'APP.OIDC.TYPETITLE' | translate}}</p>
<cnsl-type-radio [types]="appTypes" (selectedType)="appType?.setValue($event)"
[selected]="appType?.value">
</cnsl-type-radio>
<div class="actions">
<button mat-raised-button [disabled]="firstFormGroup.invalid" color="primary"
matStepperNext>{{'ACTIONS.CONTINUE' | translate}}</button>
</div>
</form>
</mat-step>
<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>
</div>
</form>
</mat-step>
<!-- skip for native applications -->
<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>
<!-- skip for native applications -->
<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>
<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="actions">
<button mat-stroked-button color="primary" matStepperPrevious>{{'ACTIONS.BACK' |
translate}}</button>
<button mat-raised-button color="primary" [disabled]="secondFormGroup.invalid"
matStepperNext>{{'ACTIONS.CONTINUE' | translate}}</button>
</div>
</form>
</mat-step>
<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>
</div>
</form>
</mat-step>
<!-- show redirect step only for OIDC apps -->
<mat-step *ngIf="appType?.value?.createType === AppCreateType.OIDC" [editable]="true">
<ng-template matStepLabel>{{'APP.OIDC.REDIRECTSECTION' | translate}}</ng-template>
<!-- show redirect step only for OIDC apps -->
<mat-step *ngIf="appType?.value?.createType === AppCreateType.OIDC" [editable]="true">
<ng-template matStepLabel>{{'APP.OIDC.REDIRECTSECTION' | translate}}</ng-template>
<p class="step-title">{{'APP.OIDC.REDIRECTTITLE' | translate}}</p>
<p class="step-description" *ngIf="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE">
{{'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate}}</p>
<p class="step-description" *ngIf="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_WEB">
{{'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate}}</p>
<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_WEB">
{{'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate}}</p>
<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 }}">
</cnsl-redirect-uris>
<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 }}">
</cnsl-redirect-uris>
<p class="step-title">{{'APP.OIDC.POSTREDIRECTTITLE' | translate}}</p>
<p class="step-description" *ngIf="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE">
{{'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate}}</p>
<p class="step-description"
*ngIf="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_WEB || oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_USER_AGENT">
{{'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate}}</p>
<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>
<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">
</cnsl-redirect-uris>
<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">
</cnsl-redirect-uris>
<div class="actions">
<button mat-stroked-button color="primary" matStepperPrevious>{{'ACTIONS.BACK' | translate}}</button>
<button mat-raised-button color="primary" matStepperNext>{{'ACTIONS.CONTINUE' | translate}}</button>
</div>
</mat-step>
<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>
</div>
</mat-step>
<mat-step>
<ng-template matStepLabel>{{'APP.OIDC.OVERVIEWSECTION'| translate}}</ng-template>
<p class="step-title">{{'APP.OIDC.OVERVIEWTITLE' | translate}}</p>
<div class="row">
<span class="left">
{{ 'APP.NAME' | translate }}
</span>
<span class="right">
{{oidcAppRequest.name}}
</span>
</div>
<mat-step>
<ng-template matStepLabel>{{'APP.OIDC.OVERVIEWSECTION'| translate}}</ng-template>
<p class="step-title">{{'APP.OIDC.OVERVIEWTITLE' | translate}}</p>
<div class="row cnsl-secondary-text">
<span class="left">
{{ 'APP.NAME' | translate }}
</span>
<span class="right">
{{oidcAppRequest.name}}
</span>
</div>
<ng-container *ngIf="appType?.value?.createType === AppCreateType.OIDC">
<div class="row">
<span class="left">
{{ 'APP.TYPE' | translate }}
</span>
<span class="right">
{{'APP.OIDC.APPTYPE.'+oidcAppRequest.appType | translate}}
</span>
</div>
<div class="row">
<span class="left">
{{ 'APP.GRANT' | translate }}
</span>
<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>]
</span>
</div>
<div class="row">
<span class="left">
{{ 'APP.OIDC.RESPONSETYPE' | translate }}
</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>]
</span>
</div>
<ng-container *ngIf="appType?.value?.createType === AppCreateType.OIDC">
<div class="row cnsl-secondary-text">
<span class="left">
{{ 'APP.TYPE' | translate }}
</span>
<span class="right">
{{'APP.OIDC.APPTYPE.'+oidcAppRequest.appType | translate}}
</span>
</div>
<div class="row cnsl-secondary-text">
<span class="left">
{{ 'APP.GRANT' | translate }}
</span>
<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>]
</span>
</div>
<div class="row cnsl-secondary-text">
<span class="left">
{{ 'APP.OIDC.RESPONSETYPE' | translate }}
</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>]
</span>
</div>
<div class="row">
<span class="left">
{{ 'APP.AUTHMETHOD' | translate }}
</span>
<span class="right">
<span>
{{'APP.OIDC.AUTHMETHOD.'+oidcAppRequest?.authMethodType | translate}}
</span>
</span>
</div>
<div class="row cnsl-secondary-text">
<span class="left">
{{ 'APP.AUTHMETHOD' | translate }}
</span>
<span class="right">
<span>
{{'APP.OIDC.AUTHMETHOD.'+oidcAppRequest?.authMethodType | translate}}
</span>
</span>
</div>
<div class="row">
<span class="left">
{{ 'APP.OIDC.REDIRECT' | translate }}
</span>
<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>]
</span>
</div>
<div class="row cnsl-secondary-text">
<span class="left">
{{ 'APP.OIDC.REDIRECT' | translate }}
</span>
<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>]
</span>
</div>
<div class="row">
<span class="left">
{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}
</span>
<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>]
</span>
</div>
</ng-container>
<div class="row cnsl-secondary-text">
<span class="left">
{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}
</span>
<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>]
</span>
</div>
</ng-container>
<ng-container *ngIf="appType?.value?.createType === AppCreateType.API">
<div class="row">
<span class="left">
{{ 'APP.AUTHMETHOD' | translate }}
</span>
<span class="right">
<span>
{{'APP.API.AUTHMETHOD.'+apiAppRequest?.authMethodType | translate}}
</span>
</span>
</div>
</ng-container>
<ng-container *ngIf="appType?.value?.createType === AppCreateType.API">
<div class="row cnsl-secondary-text">
<span class="left">
{{ 'APP.AUTHMETHOD' | translate }}
</span>
<span class="right">
<span>
{{'APP.API.AUTHMETHOD.'+apiAppRequest?.authMethodType | translate}}
</span>
</span>
</div>
</ng-container>
<div class="actions">
<button mat-stroked-button color="primary" matStepperPrevious>{{'ACTIONS.BACK' | translate}}</button>
<button mat-raised-button color="primary" (click)="createApp()">{{'ACTIONS.CREATE' |
translate}}</button>
</div>
</mat-step>
</mat-horizontal-stepper>
<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>
</div>
</mat-step>
</mat-horizontal-stepper>
<div *ngIf="devmode" class="dev">
<form [formGroup]="form" (ngSubmit)="createApp()">
<div class="content">
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'APP.NAME' | translate }}</cnsl-label>
<input cnslInput formControlName="name" />
</cnsl-form-field>
<div *ngIf="devmode" class="dev">
<form [formGroup]="form" (ngSubmit)="createApp()" [attr.data-e2e]="'create-app-wizzard-3'">
<div class="content">
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'APP.NAME' | translate }}</cnsl-label>
<input cnslInput formControlName="name" />
</cnsl-form-field>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'APP.TYPE' | translate }}</cnsl-label>
<mat-select formControlName="appType">
<mat-option *ngFor="let appType of appTypes" [value]="appType">
{{ appType.titleI18nKey | translate }}
</mat-option>
</mat-select>
</cnsl-form-field>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'APP.TYPE' | translate }}</cnsl-label>
<mat-select formControlName="appType">
<mat-option *ngFor="let appType of appTypes" [value]="appType">
{{ appType.titleI18nKey | translate }}
</mat-option>
</mat-select>
</cnsl-form-field>
<ng-container *ngIf="formappType?.value?.createType === AppCreateType.OIDC">
<cnsl-form-field appearance="outline" class="formfield">
<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 }}
</mat-option>
</mat-select>
</cnsl-form-field>
<ng-container *ngIf="formappType?.value?.createType === AppCreateType.OIDC">
<cnsl-form-field appearance="outline" class="formfield">
<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 }}
</mat-option>
</mat-select>
</cnsl-form-field>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'APP.OIDC.RESPONSETYPE' | translate }}</cnsl-label>
<mat-select formControlName="responseTypesList" multiple>
<mat-option *ngFor="let type of oidcResponseTypes" [value]="type.type">
{{ 'APP.OIDC.RESPONSE.'+type.type | translate }}
</mat-option>
</mat-select>
</cnsl-form-field>
</ng-container>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'APP.OIDC.RESPONSETYPE' | translate }}</cnsl-label>
<mat-select formControlName="responseTypesList" multiple>
<mat-option *ngFor="let type of oidcResponseTypes" [value]="type.type">
{{ 'APP.OIDC.RESPONSE.'+type.type | translate }}
</mat-option>
</mat-select>
</cnsl-form-field>
</ng-container>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'APP.AUTHMETHOD' | translate }}</cnsl-label>
<mat-select formControlName="authMethodType">
<mat-option *ngFor="let type of authMethodTypes" [value]="type.type">
<span *ngIf="type.oidc">{{ 'APP.OIDC.AUTHMETHOD.'+type.type | translate }}</span>
<span *ngIf="type.api">{{ 'APP.API.AUTHMETHOD.'+type.type | translate }}</span>
</mat-option>
</mat-select>
</cnsl-form-field>
</div>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'APP.AUTHMETHOD' | translate }}</cnsl-label>
<mat-select formControlName="authMethodType">
<mat-option *ngFor="let type of authMethodTypes" [value]="type.type">
<span *ngIf="type.oidc">{{ 'APP.OIDC.AUTHMETHOD.'+type.type | translate }}</span>
<span *ngIf="type.api">{{ 'APP.API.AUTHMETHOD.'+type.type | translate }}</span>
</mat-option>
</mat-select>
</cnsl-form-field>
</div>
<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>
<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>
<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">
</cnsl-redirect-uris>
</div>
</div>
<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">
</cnsl-redirect-uris>
</div>
</div>
<button color="primary" mat-raised-button class="continue-button" [disabled]="form.invalid" cdkFocusInitial
type="submit">
{{ 'ACTIONS.SAVE' | translate }}
</button>
</form>
</div>
<button color="primary" mat-raised-button class="continue-button" [disabled]="form.invalid" cdkFocusInitial
type="submit">
{{ 'ACTIONS.SAVE' | translate }}
</button>
</form>
</div>
</div>
@@ -5,7 +5,6 @@ h1 {
p.desc {
font-size: 14px;
color: var(--grey);
}
.proswitch {
@@ -13,8 +12,8 @@ p.desc {
display: block;
}
.container {
padding: 4rem 4rem 2rem 4rem;
.app-create-container {
padding-top: 2rem;
.progress-bar {
margin-bottom: 1rem;
@@ -39,7 +38,7 @@ p.desc {
}
.margin-right {
margin-right: .5rem;
margin-right: 0.5rem;
}
.full-width {
@@ -57,12 +56,11 @@ p.desc {
.step-title {
font-size: 1rem;
text-transform: uppercase;
letter-spacing: .05em;
letter-spacing: 0.05em;
}
.step-description {
font-size: .9rem;
color: var(--grey);
font-size: 0.9rem;
}
}
@@ -71,7 +69,7 @@ p.desc {
flex-direction: column;
.checkbox {
margin: .5rem 0;
margin: 0.5rem 0;
}
}
@@ -81,38 +79,31 @@ p.desc {
.left,
.right {
margin-bottom: .5rem;
margin-bottom: 0.5rem;
font-size: 14px;
color: var(--grey);
}
}
.actions {
button[mat-stroked-button] {
float: left;
margin-top: 1rem;
border-radius: .5rem;
}
.app-create-actions {
margin-top: 1rem;
display: flex;
button[mat-raised-button] {
float: right;
margin-top: 1rem;
border-radius: .5rem;
min-width: 100px;
.fill-space {
flex: 1;
}
}
.dev {
.content {
display: flex;
margin: 0 -.5rem;
margin: 0 -0.5rem;
flex-wrap: wrap;
flex-direction: row;
.formfield {
flex: 1;
box-sizing: border-box;
margin: 0 .5rem;
margin: 0 0.5rem;
&.full-width {
flex-basis: 80%;
@@ -123,7 +114,7 @@ p.desc {
.continue-button {
margin-top: 3rem;
display: block;
padding: .5rem 4rem;
padding: 0.5rem 4rem;
float: right;
}
}
@@ -21,6 +21,7 @@ import {
AddOIDCAppRequest,
AddOIDCAppResponse,
} from 'src/app/proto/generated/zitadel/management_pb';
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service';
@@ -36,7 +37,6 @@ import {
} from '../authmethods';
import { API_TYPE, AppCreateType, NATIVE_TYPE, RadioItemAppType, USER_AGENT_TYPE, WEB_TYPE } from '../authtypes';
@Component({
selector: 'cnsl-app-create',
templateUrl: './app-create.component.html',
@@ -52,7 +52,7 @@ export class AppCreateComponent implements OnInit, OnDestroy {
public oidcAppRequest: AddOIDCAppRequest.AsObject = new AddOIDCAppRequest().toObject();
public apiAppRequest: AddAPIAppRequest.AsObject = new AddAPIAppRequest().toObject();
public oidcResponseTypes: { type: OIDCResponseType, checked: boolean; disabled: boolean; }[] = [
public oidcResponseTypes: { type: OIDCResponseType; checked: boolean; disabled: boolean }[] = [
{ type: OIDCResponseType.OIDC_RESPONSE_TYPE_CODE, checked: false, disabled: false },
{ type: OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN, checked: false, disabled: false },
{ type: OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN_TOKEN, checked: false, disabled: false },
@@ -63,32 +63,22 @@ export class AppCreateComponent implements OnInit, OnDestroy {
OIDCAppType.OIDC_APP_TYPE_NATIVE,
OIDCAppType.OIDC_APP_TYPE_USER_AGENT,
];
public appTypes: any = [
WEB_TYPE,
NATIVE_TYPE,
USER_AGENT_TYPE,
API_TYPE,
];
public appTypes: any = [WEB_TYPE, NATIVE_TYPE, USER_AGENT_TYPE, API_TYPE];
public authMethods: RadioItemAuthType[] = [
PKCE_METHOD,
CODE_METHOD,
PK_JWT_METHOD,
POST_METHOD,
];
public authMethods: RadioItemAuthType[] = [PKCE_METHOD, CODE_METHOD, PK_JWT_METHOD, POST_METHOD];
// set to oidc first
public authMethodTypes: {
type: OIDCAuthMethodType | APIAuthMethodType,
checked: boolean,
type: OIDCAuthMethodType | APIAuthMethodType;
checked: boolean;
disabled: boolean;
api?: boolean;
oidc?: boolean;
}[] = [
{ type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC, checked: false, disabled: false, oidc: true },
{ type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE, checked: false, disabled: false, oidc: true },
{ type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST, checked: false, disabled: false, oidc: true },
];
{ type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC, checked: false, disabled: false, oidc: true },
{ type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE, checked: false, disabled: false, oidc: true },
{ type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST, checked: false, disabled: false, oidc: true },
];
// stepper
firstFormGroup!: FormGroup;
@@ -103,15 +93,15 @@ export class AppCreateComponent implements OnInit, OnDestroy {
public OIDCAuthMethodType: any = OIDCAuthMethodType;
public oidcGrantTypes: {
type: OIDCGrantType,
checked: boolean,
disabled: boolean,
type: OIDCGrantType;
checked: boolean;
disabled: boolean;
}[] = [
{ type: OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE, checked: true, disabled: false },
{ type: OIDCGrantType.OIDC_GRANT_TYPE_IMPLICIT, checked: false, disabled: true },
// { type: OIDCGrantType.OIDCGRANTTYPE_REFRESH_TOKEN, checked: false, disabled: true },
// TODO show when implemented
];
{ type: OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE, checked: true, disabled: false },
{ type: OIDCGrantType.OIDC_GRANT_TYPE_IMPLICIT, checked: false, disabled: true },
// { type: OIDCGrantType.OIDCGRANTTYPE_REFRESH_TOKEN, checked: false, disabled: true },
// TODO show when implemented
];
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
public requestRedirectValuesSubject$: Subject<void> = new Subject();
@@ -124,6 +114,7 @@ export class AppCreateComponent implements OnInit, OnDestroy {
private mgmtService: ManagementService,
private fb: FormBuilder,
private _location: Location,
private breadcrumbService: BreadcrumbService,
) {
this.form = this.fb.group({
name: ['', [Validators.required]],
@@ -140,7 +131,7 @@ export class AppCreateComponent implements OnInit, OnDestroy {
appType: [WEB_TYPE, [Validators.required]],
});
this.firstFormGroup.valueChanges.subscribe(value => {
this.firstFormGroup.valueChanges.subscribe((value) => {
if (this.firstFormGroup.valid) {
this.oidcAppRequest.name = this.name?.value;
this.apiAppRequest.name = this.name?.value;
@@ -153,9 +144,7 @@ export class AppCreateComponent implements OnInit, OnDestroy {
switch (this.oidcAppRequest.appType) {
case OIDCAppType.OIDC_APP_TYPE_NATIVE:
this.authMethods = [
PKCE_METHOD,
];
this.authMethods = [PKCE_METHOD];
// automatically set to PKCE and skip step
this.oidcAppRequest.responseTypesList = [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE];
@@ -165,30 +154,19 @@ export class AppCreateComponent implements OnInit, OnDestroy {
break;
case OIDCAppType.OIDC_APP_TYPE_WEB:
// PK_JWT_METHOD.recommended = false;
this.authMethods = [
PKCE_METHOD,
CODE_METHOD,
PK_JWT_METHOD,
POST_METHOD,
];
this.authMethods = [PKCE_METHOD, CODE_METHOD, PK_JWT_METHOD, POST_METHOD];
this.authMethod?.setValue(PKCE_METHOD.key);
break;
case OIDCAppType.OIDC_APP_TYPE_USER_AGENT:
this.authMethods = [
PKCE_METHOD,
IMPLICIT_METHOD,
];
this.authMethods = [PKCE_METHOD, IMPLICIT_METHOD];
this.authMethod?.setValue(PKCE_METHOD.key);
break;
}
} else if (this.isStepperAPI) {
// PK_JWT_METHOD.recommended = true;
this.authMethods = [
PK_JWT_METHOD,
BASIC_AUTH_METHOD,
];
this.authMethods = [PK_JWT_METHOD, BASIC_AUTH_METHOD];
this.authMethod?.setValue(PK_JWT_METHOD.key);
}
@@ -198,28 +176,48 @@ export class AppCreateComponent implements OnInit, OnDestroy {
this.secondFormGroup = this.fb.group({
authMethod: [this.authMethods[0].key, [Validators.required]],
});
this.secondFormGroup.valueChanges.subscribe(form => {
this.secondFormGroup.valueChanges.subscribe((form) => {
const partialConfig = getPartialConfigFromAuthMethod(form.authMethod);
if (this.isStepperOIDC && partialConfig && partialConfig.oidc) {
this.oidcAppRequest.responseTypesList = partialConfig.oidc?.responseTypesList
?? [];
this.oidcAppRequest.responseTypesList = partialConfig.oidc?.responseTypesList ?? [];
this.oidcAppRequest.grantTypesList = partialConfig.oidc?.grantTypesList
?? [];
this.oidcAppRequest.authMethodType = partialConfig.oidc?.authMethodType
?? OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE;
this.oidcAppRequest.grantTypesList = partialConfig.oidc?.grantTypesList ?? [];
this.oidcAppRequest.authMethodType =
partialConfig.oidc?.authMethodType ?? OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE;
} else if (this.isStepperAPI && partialConfig && partialConfig.api) {
this.apiAppRequest.authMethodType = partialConfig.api?.authMethodType
?? APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC;
this.apiAppRequest.authMethodType =
partialConfig.api?.authMethodType ?? APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC;
}
});
}
public ngOnInit(): void {
this.subscription = this.route.params.subscribe(params => this.getData(params));
this.subscription = this.route.params.subscribe((params) => this.getData(params));
const projectId = this.route.snapshot.paramMap.get('projectid');
if (projectId) {
const breadcrumbs = [
new Breadcrumb({
type: BreadcrumbType.IAM,
name: 'IAM',
routerLink: ['/system'],
}),
new Breadcrumb({
type: BreadcrumbType.ORG,
routerLink: ['/org'],
}),
new Breadcrumb({
type: BreadcrumbType.PROJECT,
name: '',
param: { key: 'projectid', value: projectId },
routerLink: ['/projects', projectId],
isZitadel: false,
}),
];
this.breadcrumbService.setBreadcrumb(breadcrumbs);
}
}
public ngOnDestroy(): void {
@@ -228,23 +226,21 @@ export class AppCreateComponent implements OnInit, OnDestroy {
}
public initForm(): void {
this.form.valueChanges.pipe(
takeUntil(this.destroyed$),
debounceTime(150)).subscribe(() => {
this.oidcAppRequest.name = this.formname?.value;
this.apiAppRequest.name = this.formname?.value;
this.form.valueChanges.pipe(takeUntil(this.destroyed$), debounceTime(150)).subscribe(() => {
this.oidcAppRequest.name = this.formname?.value;
this.apiAppRequest.name = this.formname?.value;
this.oidcAppRequest.responseTypesList = this.formresponseTypesList?.value;
this.oidcAppRequest.grantTypesList = this.formgrantTypesList?.value;
this.oidcAppRequest.responseTypesList = this.formresponseTypesList?.value;
this.oidcAppRequest.grantTypesList = this.formgrantTypesList?.value;
this.oidcAppRequest.authMethodType = this.formauthMethodType?.value;
this.apiAppRequest.authMethodType = this.formauthMethodType?.value;
this.oidcAppRequest.authMethodType = this.formauthMethodType?.value;
this.apiAppRequest.authMethodType = this.formauthMethodType?.value;
const oidcAppType = (this.formappType?.value as RadioItemAppType).oidcAppType;
if (oidcAppType !== undefined) {
this.oidcAppRequest.appType = oidcAppType;
}
});
const oidcAppType = (this.formappType?.value as RadioItemAppType).oidcAppType;
if (oidcAppType !== undefined) {
this.oidcAppRequest.appType = oidcAppType;
}
});
this.formappType?.valueChanges.pipe(takeUntil(this.destroyed$)).subscribe(() => {
this.setDevFormValidators();
@@ -306,18 +302,20 @@ export class AppCreateComponent implements OnInit, OnDestroy {
.addOIDCApp(this.oidcAppRequest)
.then((resp) => {
this.loading = false;
this.toast.showInfo('APP.TOAST.CREATED', true);
if (resp.clientId || resp.clientSecret) {
this.showSavedDialog(resp);
} else {
this.router.navigate(['projects', this.projectId, 'apps', resp.appId]);
}
})
.catch(error => {
.catch((error) => {
this.loading = false;
this.toast.showError(error);
});
} else if (appAPICheck) {
this.loading = true;
this.toast.showInfo('APP.TOAST.CREATED', true);
this.mgmtService
.addAPIApp(this.apiAppRequest)
.then((resp) => {
@@ -329,7 +327,7 @@ export class AppCreateComponent implements OnInit, OnDestroy {
this.router.navigate(['projects', this.projectId, 'apps', resp.appId]);
}
})
.catch(error => {
.catch((error) => {
this.loading = false;
this.toast.showError(error);
});
@@ -364,7 +362,12 @@ export class AppCreateComponent implements OnInit, OnDestroy {
return this.firstFormGroup.get('appType');
}
public grantTypeChecked(type: OIDCGrantType): boolean {
return this.oidcGrantTypes.filter(gt => gt.checked).map(gt => gt.type).findIndex(t => t === type) > -1;
return (
this.oidcGrantTypes
.filter((gt) => gt.checked)
.map((gt) => gt.type)
.findIndex((t) => t === type) > -1
);
}
get responseTypesList(): AbstractControl | null {
return this.secondFormGroup.get('responseTypesList');
@@ -410,4 +413,3 @@ export class AppCreateComponent implements OnInit, OnDestroy {
return (this.appType?.value as RadioItemAppType).createType === AppCreateType.API;
}
}
@@ -1,282 +1,271 @@
<cnsl-meta-layout>
<div class="enlarged-container">
<div class="head">
<a [routerLink]="['/projects', projectId]" mat-icon-button>
<mat-icon class="icon">arrow_back</mat-icon>
</a>
<div class="title-col">
<h1>{{app?.name}}</h1>
<span *ngIf="app?.oidcConfig">{{'APP.OIDC.APPTYPE.'+app?.oidcConfig?.appType |
translate}}</span>
<span *ngIf="app?.apiConfig">API</span>
</div>
<ng-container *ngIf="isZitadel === false">
<span class="fill-space"></span>
<ng-template cnslHasRole [hasRole]="['project.app.write:'+projectId, 'project.app.write']">
<button class="actions-trigger" mat-raised-button color="primary" [matMenuTriggerFor]="actions">
<span>{{'ACTIONS.ACTIONS' | translate}}</span>
<mat-icon class="icon">keyboard_arrow_down</mat-icon>
</button>
<mat-menu #actions="matMenu" xPosition="before">
<button mat-menu-item (click)="openNameDialog()"
aria-label="Edit project name" *ngIf="isZitadel === false">
{{'ACTIONS.RENAME' | translate}}
</button>
<button mat-menu-item
*ngIf="app?.state !== AppState.APP_STATE_INACTIVE"
(click)="changeState(AppState.APP_STATE_INACTIVE)">
{{'ACTIONS.DEACTIVATE' | translate}}
</button>
<button mat-menu-item *ngIf="app?.state === AppState.APP_STATE_INACTIVE"
(click)="changeState(AppState.APP_STATE_ACTIVE)">
{{'ACTIONS.REACTIVATE' | translate}}
</button>
<ng-template cnslHasRole [hasRole]="['project.app.delete:'+projectId, 'project.app.delete']">
<button mat-menu-item matTooltip="{{'APP.PAGES.DELETE' | translate}}"
(click)="deleteApp()">
<span [style.color]="'var(--warn)'">{{'APP.PAGES.DELETE' | translate}}</span>
</button>
</ng-template>
</mat-menu>
</ng-template>
</ng-container>
<cnsl-top-view title="{{app?.name}}"
[hasActions]="isZitadel === false && (['project.app.write:'+projectId, 'project.app.write'] | hasRole | async)"
docLink="https://docs.zitadel.ch/docs/guides/basics/projects"
[sub]="app?.oidcConfig ? ('APP.OIDC.APPTYPE.'+app?.oidcConfig?.appType | translate): 'API'"
[isActive]="app?.state === AppState.APP_STATE_ACTIVE" [isInactive]="app?.state === AppState.APP_STATE_INACTIVE"
stateTooltip="{{('APP.PAGES.DETAIL.STATE.'+app?.state) | translate}}">
<ng-template topActions cnslHasRole [hasRole]="['project.app.write:'+projectId, 'project.app.write']">
<div class="regen-secret"
*ngIf="isZitadel === false && this.app?.oidcConfig && (currentAuthMethod === 'CODE' || currentAuthMethod === 'POST')">
<button type="button" [disabled]="!canWrite" mat-menu-item
(click)="regenerateOIDCClientSecret()">{{'APP.OIDC.REGENERATESECRET' | translate}}</button>
</div>
<p class="desc">{{ 'APP.PAGES.DESCRIPTION' | translate }}</p>
<p *ngIf="isZitadel" class="zitadel-warning">{{'PROJECT.PAGES.ZITADELPROJECT' | translate}}</p>
</div>
<div class="regen-secret" *ngIf="isZitadel === false && this.app?.apiConfig && currentAuthMethod === 'BASIC'">
<button [disabled]="!canWrite" mat-menu-item (click)="regenerateAPIClientSecret()">{{'APP.API.REGENERATESECRET' |
translate}}</button>
</div>
<span *ngIf="errorMessage" class="err-container">{{errorMessage}}</span>
<button *ngIf="isZitadel === false" mat-menu-item (click)="openNameDialog()" aria-label="Edit project name">
{{'ACTIONS.RENAME' | translate}}
</button>
<button mat-menu-item *ngIf="isZitadel === false && app?.state !== AppState.APP_STATE_INACTIVE"
(click)="changeState(AppState.APP_STATE_INACTIVE)">
{{'ACTIONS.DEACTIVATE' | translate}}
</button>
<button mat-menu-item *ngIf="isZitadel === false && app?.state === AppState.APP_STATE_INACTIVE"
(click)="changeState(AppState.APP_STATE_ACTIVE)">
{{'ACTIONS.REACTIVATE' | translate}}
</button>
<ng-template cnslHasRole [hasRole]="['project.app.delete:'+projectId, 'project.app.delete']">
<button *ngIf="isZitadel === false" mat-menu-item matTooltip="{{'APP.PAGES.DELETE' | translate}}"
(click)="deleteApp()">
<span [style.color]="'var(--warn)'">{{'APP.PAGES.DELETE' | translate}}</span>
</button>
</ng-template>
</ng-template>
<span *ngIf="errorMessage" class="app-err-container">{{errorMessage}}</span>
<div class="compliance"
*ngIf="app?.oidcConfig?.complianceProblemsList && app.oidcConfig?.complianceProblemsList?.length">
<cnsl-info-section class="problem" [type]="InfoSectionType.WARN">
<ul style="margin: 0;">
<li style="margin: 0 0 .5rem 0;"
*ngFor="let problem of app.oidcConfig?.complianceProblemsList || []">
{{problem.localizedMessage}}</li>
</ul>
</cnsl-info-section>
</div>
<cnsl-info-row topContent *ngIf="app" [app]="app"></cnsl-info-row>
</cnsl-top-view>
<cnsl-info-row *ngIf="app" [app]="app"></cnsl-info-row>
<div class="max-width-container">
<cnsl-auth-method-radio *ngIf="authMethods && initialAuthMethod && (app?.oidcConfig || app?.apiConfig)"
[authMethods]="authMethods" [selected]="initialAuthMethod" [current]="currentAuthMethod"
[isOIDC]="app?.oidcConfig !== undefined" (selectedMethod)="setPartialConfigFromAuthMethod($event)">
</cnsl-auth-method-radio>
<div *ngIf="app?.oidcConfig" class="expandables">
<div class="expandable">
<p class="title">{{'APP.OIDC.REDIRECTSECTIONTITLE' | translate}}
<button mat-icon-button (click)="showRedirects = !showRedirects"
matTooltip="{{(showRedirects ? 'ACTIONS.HIDE' : 'ACTIONS.SHOW') | translate}}">
<mat-icon *ngIf="!showRedirects">expand_more</mat-icon>
<mat-icon *ngIf="showRedirects">expand_less</mat-icon>
</button>
</p>
<ng-container *ngIf="showRedirects">
<cnsl-info-section *ngIf="appType?.value === OIDCAppType.OIDC_APP_TYPE_NATIVE">
<div class="dev-col">
<span>{{'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate}}</span>
<mat-slide-toggle *ngIf="devMode" color="primary" class="devmode" [formControl]="devMode" name="devMode"
matTooltip="{{'APP.OIDC.DEVMODEDESC' | translate}}">
{{ 'APP.OIDC.DEVMODE' | translate }}
</mat-slide-toggle>
</div>
</cnsl-info-section>
<cnsl-info-section
*ngIf="appType?.value === OIDCAppType.OIDC_APP_TYPE_WEB || appType?.value === OIDCAppType.OIDC_APP_TYPE_USER_AGENT">
<div class="dev-col">
<span>{{'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate}}</span>
<mat-slide-toggle *ngIf="devMode" color="primary" class="devmode" [formControl]="devMode" name="devMode"
matTooltip="{{'APP.OIDC.DEVMODEDESC' | translate}}">
{{ 'APP.OIDC.DEVMODE' | translate }}
</mat-slide-toggle>
</div>
</cnsl-info-section>
<div class="content">
<cnsl-redirect-uris *ngIf="appType?.value !== undefined" class="redirect-section" [canWrite]="canWrite"
[devMode]="devMode?.value" [getValues]="requestRedirectValuesSubject$"
(changedUris)="redirectUrisList = $any($event)" [urisList]="redirectUrisList"
title="{{ 'APP.OIDC.REDIRECT' | translate }}"
[isNative]="appType?.value === OIDCAppType.OIDC_APP_TYPE_NATIVE">
</cnsl-redirect-uris>
<cnsl-redirect-uris *ngIf="appType?.value !== undefined" class="redirect-section" [canWrite]="canWrite"
[devMode]="devMode?.value" (changedUris)="postLogoutRedirectUrisList = $any($event)"
[urisList]="postLogoutRedirectUrisList" [getValues]="requestRedirectValuesSubject$"
title="{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}"
[isNative]="appType?.value === OIDCAppType.OIDC_APP_TYPE_NATIVE">
</cnsl-redirect-uris>
</div>
</ng-container>
</div>
<div class="app-main-content">
<cnsl-meta-layout>
<cnsl-sidenav [(ngModel)]="currentSetting" [settingsList]="settingsList">
<ng-container *ngIf="currentSetting === 'redirect-uris'">
<cnsl-card title=" {{ 'APP.OIDC.REDIRECTSECTIONTITLE' | translate }}">
<cnsl-info-section *ngIf="appType?.value === OIDCAppType.OIDC_APP_TYPE_NATIVE">
<div class="dev-col">
<span>{{'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate}}</span>
<mat-slide-toggle *ngIf="devMode" color="primary" class="devmode" [formControl]="devMode" name="devMode"
matTooltip="{{'APP.OIDC.DEVMODEDESC' | translate}}">
{{ 'APP.OIDC.DEVMODE' | translate }}
</mat-slide-toggle>
</div>
</cnsl-info-section>
<cnsl-info-section
*ngIf="appType?.value === OIDCAppType.OIDC_APP_TYPE_WEB || appType?.value === OIDCAppType.OIDC_APP_TYPE_USER_AGENT">
<div class="dev-col">
<span>{{'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate}}</span>
<mat-slide-toggle *ngIf="devMode" color="primary" class="devmode" [formControl]="devMode" name="devMode"
matTooltip="{{'APP.OIDC.DEVMODEDESC' | translate}}">
{{ 'APP.OIDC.DEVMODE' | translate }}
</mat-slide-toggle>
</div>
</cnsl-info-section>
<div class="expandable">
<p class="title">{{'APP.ADDITIONALORIGINS' | translate}}
<button mat-icon-button (click)="showAdditionalOrigins = !showAdditionalOrigins"
matTooltip="{{(showAdditionalOrigins ? 'ACTIONS.HIDE' : 'ACTIONS.SHOW') | translate}}">
<mat-icon *ngIf="!showAdditionalOrigins">expand_more</mat-icon>
<mat-icon *ngIf="showAdditionalOrigins">expand_less</mat-icon>
</button>
</p>
<ng-container *ngIf="showAdditionalOrigins">
<p class="desc">{{'APP.ADDITIONALORIGINSDESC' | translate}}</p>
<cnsl-additional-origins *ngIf="appType?.value !== undefined" class="input" [canWrite]="canWrite"
[getValues]="requestRedirectValuesSubject$" (changedUris)="additionalOriginsList = $any($event)"
[urisList]="additionalOriginsList" title="{{ 'APP.ORIGINS' | translate }}">
</cnsl-additional-origins>
</ng-container>
</div>
<cnsl-redirect-uris *ngIf="appType?.value !== undefined" class="redirect-section" [canWrite]="canWrite"
[devMode]="devMode?.value" [getValues]="requestRedirectValuesSubject$"
(changedUris)="redirectUrisList = $any($event)" [urisList]="redirectUrisList"
title="{{ 'APP.OIDC.REDIRECT' | translate }}"
[isNative]="appType?.value === OIDCAppType.OIDC_APP_TYPE_NATIVE">
</cnsl-redirect-uris>
<div style="margin: .5rem" class="divider"></div>
</div>
<cnsl-auth-method-radio *ngIf="authMethods && initialAuthMethod && (app?.oidcConfig || app?.apiConfig)"
[authMethods]="authMethods" [selected]="initialAuthMethod" [current]="currentAuthMethod"
[isOIDC]="app?.oidcConfig !== undefined" (selectedMethod)="setPartialConfigFromAuthMethod($event)">
</cnsl-auth-method-radio>
<cnsl-card *ngIf="currentAuthMethod === 'PK_JWT' && projectId && app?.id" [expanded]="false"
title="{{ 'USER.MACHINE.KEYSTITLE' | translate }}" description="{{ 'USER.MACHINE.KEYSDESC' | translate }}">
<cnsl-client-keys [projectId]="projectId" [appId]="app.id"></cnsl-client-keys>
</cnsl-card>
<div *ngIf="apiForm && app?.apiConfig" class="btn-container">
<button class="submit-button" (click)="saveAPIApp()" color="primary"
[disabled]="apiForm.invalid || !canWrite" mat-raised-button>
{{ 'ACTIONS.SAVE' | translate }}
</button>
</div>
<div *ngIf="currentAuthMethod === 'BASIC'">
<button [disabled]="!canWrite" mat-stroked-button
(click)="regenerateAPIClientSecret()">{{'APP.API.REGENERATESECRET' | translate}}</button>
</div>
<form *ngIf="oidcForm && app?.oidcConfig" [formGroup]="oidcForm" (ngSubmit)="saveOIDCApp()">
<cnsl-card title=" {{ 'APP.OIDC.TITLE' | translate }}" *ngIf="app && app.oidcConfig" [expanded]="false">
<div card-actions *ngIf="currentAuthMethod === 'CODE' || currentAuthMethod === 'POST'">
<button type="button" [disabled]="!canWrite" mat-stroked-button
(click)="regenerateOIDCClientSecret()">{{'APP.OIDC.REGENERATESECRET' | translate}}</button>
</div>
<div class="content">
<div class="grid">
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'APP.OIDC.CLIENTID' | translate }}</cnsl-label>
<input cnslInput formControlName="clientId" />
</cnsl-form-field>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'APP.TYPE' | translate }}</cnsl-label>
<mat-select formControlName="appType">
<mat-option *ngFor="let type of oidcAppTypes" [value]="type">
{{ 'APP.OIDC.APPTYPE.'+type | translate }}
</mat-option>
</mat-select>
</cnsl-form-field>
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'APP.OIDC.RESPONSETYPE' | translate }}</cnsl-label>
<mat-select formControlName="responseTypesList" multiple>
<mat-option *ngFor="let type of oidcResponseTypes" [value]="type">
{{ 'APP.OIDC.RESPONSE.'+type | translate }}
</mat-option>
</mat-select>
</cnsl-form-field>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'APP.AUTHMETHOD' | translate }}</cnsl-label>
<mat-select formControlName="authMethodType">
<mat-option *ngFor="let type of oidcAuthMethodType" [value]="type">
{{ 'APP.OIDC.AUTHMETHOD.'+type | translate }}
</mat-option>
</mat-select>
</cnsl-form-field>
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'APP.OIDC.GRANTTYPE' | translate }}</cnsl-label>
<mat-select formControlName="grantTypesList" multiple>
<mat-option *ngFor="let grant of oidcGrantTypes" [value]="grant">
{{ 'APP.OIDC.GRANT.'+grant | translate }}
</mat-option>
</mat-select>
</cnsl-form-field>
<mat-checkbox color="primary" class="rt"
(change)="toggleRefreshToken($event)" [disabled]="!this.grantTypesList?.value.includes(OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE)" [checked]="this.grantTypesList?.value.includes(OIDCGrantType.OIDC_GRANT_TYPE_REFRESH_TOKEN)">
{{ 'APP.OIDC.REFRESHTOKEN' | translate }}
</mat-checkbox>
</div>
<div class="divider"></div>
<p class="full-width section-title">{{'APP.OIDC.TOKENSECTIONTITLE' | translate}}</p>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'APP.OIDC.TOKENTYPE' | translate }}</cnsl-label>
<mat-select formControlName="accessTokenType">
<mat-option *ngFor="let type of oidcTokenTypes" [value]="type">
{{ 'APP.OIDC.TOKENTYPE'+type | translate }}
</mat-option>
</mat-select>
</cnsl-form-field>
<mat-checkbox *ngIf="accessTokenType?.value === OIDCTokenType.OIDC_TOKEN_TYPE_JWT" class="full-width"
formControlName="accessTokenRoleAssertion" color="primary">
{{'APP.OIDC.ACCESSTOKENROLEASSERTION' | translate}}</mat-checkbox>
<cnsl-info-section class="full-width desc">
<span>{{'APP.OIDC.ACCESSTOKENROLEASSERTION_DESCRIPTION' | translate}}</span>
</cnsl-info-section>
<mat-checkbox class="full-width" style="margin-bottom: .5rem" formControlName="idTokenRoleAssertion"
color="primary">
{{'APP.OIDC.IDTOKENROLEASSERTION' | translate}}</mat-checkbox>
<cnsl-info-section class="full-width desc">
<span>{{'APP.OIDC.IDTOKENROLEASSERTION_DESCRIPTION' | translate}}</span>
</cnsl-info-section>
<mat-checkbox class="full-width" style="margin-bottom: .5rem"
formControlName="idTokenUserinfoAssertion" color="primary">
{{'APP.OIDC.IDTOKENUSERINFOASSERTION' | translate}}</mat-checkbox>
<cnsl-info-section class="full-width desc">
<span>{{'APP.OIDC.IDTOKENUSERINFOASSERTION_DESCRIPTION' | translate}}</span>
</cnsl-info-section>
<p class="clockskew-title">ClockSkew</p>
<mat-slider color="primary" formControlName="clockSkewSeconds" class="clockskew-slider" thumbLabel
[displayWith]="formatClockSkewLabel" tickInterval=".1" min="0" [step]="1" max="5">
</mat-slider>
<cnsl-info-section class="full-width desc">
<span>{{'APP.OIDC.CLOCKSKEW' | translate}}</span>
</cnsl-info-section>
</div>
</cnsl-card>
<cnsl-redirect-uris *ngIf="appType?.value !== undefined" class="redirect-section" [canWrite]="canWrite"
[devMode]="devMode?.value" (changedUris)="postLogoutRedirectUrisList = $any($event)"
[urisList]="postLogoutRedirectUrisList" [getValues]="requestRedirectValuesSubject$"
title="{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}"
[isNative]="appType?.value === OIDCAppType.OIDC_APP_TYPE_NATIVE">
</cnsl-redirect-uris>
<div class="btn-container">
<button class="submit-button" color="primary" (click)="saveOIDCApp()" [disabled]="!canWrite"
mat-raised-button>
{{ 'ACTIONS.SAVE' | translate }}
</button>
</div>
</cnsl-card>
</ng-container>
<ng-container *ngIf="currentSetting === 'additional-origins'">
<cnsl-card title=" {{ 'APP.ADDITIONALORIGINS' | translate }}">
<p class="app-desc cnsl-secondary-text">{{'APP.ADDITIONALORIGINSDESC' | translate}}</p>
<cnsl-additional-origins *ngIf="additionalOriginsList && appType && appType?.value !== undefined"
class="app-input" [canWrite]="canWrite" [getValues]="requestRedirectValuesSubject$"
(changedUris)="additionalOriginsListChanged($event)" [urisList]="additionalOriginsList"
title="{{ 'APP.ORIGINS' | translate }}">
</cnsl-additional-origins>
<div class="btn-container">
<button class="submit-button" (click)="saveOIDCApp()" color="primary" [disabled]="!canWrite"
mat-raised-button>
{{ 'ACTIONS.SAVE' | translate }}
</button>
</div>
</cnsl-card>
</ng-container>
<ng-container *ngIf="currentSetting === 'urls'">
<cnsl-card title=" {{ 'APP.URLS' | translate }}">
<div class="app-info-row">
<div class="app-info-wrapper" *ngFor="let environmentV of (environmentMap | keyvalue)">
<p class="app-info-row-title cnsl-secondary-text">{{environmentV.key}}</p>
<div class="app-copy-row">
<div *ngIf="environmentV.value" class="environment">
<button [disabled]="copied === environmentV.value"
[matTooltip]="(copied !== environmentV.value ? 'ACTIONS.COPY' : 'ACTIONS.COPIED' ) | translate"
cnslCopyToClipboard [valueToCopy]="environmentV.value" (copiedValue)="copied = environmentV.key">
{{environmentV.value}}
</button>
</div>
</div>
</div>
</div>
</cnsl-card>
</ng-container>
<ng-container *ngIf="currentSetting === 'configuration'">
<cnsl-card *ngIf="oidcForm && app?.oidcConfig" title=" {{ 'APP.OIDC.TITLE' | translate }}">
<form [formGroup]="oidcForm" (ngSubmit)="saveOIDCApp()">
<div class="compliance"
*ngIf="app?.oidcConfig?.complianceProblemsList && app.oidcConfig?.complianceProblemsList?.length">
<h2 class="compliance-title">{{'APP.COMPLIANCE' | translate}}</h2>
<cnsl-info-section class="problem" [type]="InfoSectionType.WARN">
<ul style="margin: 0;">
<li style="margin: 0 0 .5rem 0;"
*ngFor="let problem of app.oidcConfig?.complianceProblemsList || []">
{{problem.localizedMessage}}</li>
</ul>
</cnsl-info-section>
</div>
<div class="app-oidc-content">
<div class="app-oidc-grid">
<cnsl-form-field class="app-formfield" appearance="outline">
<cnsl-label>{{ 'APP.OIDC.CLIENTID' | translate }}</cnsl-label>
<input cnslInput formControlName="clientId" />
</cnsl-form-field>
<cnsl-form-field appearance="outline" class="app-formfield">
<cnsl-label>{{ 'APP.TYPE' | translate }}</cnsl-label>
<mat-select formControlName="appType">
<mat-option *ngFor="let type of oidcAppTypes" [value]="type">
{{ 'APP.OIDC.APPTYPE.'+type | translate }}
</mat-option>
</mat-select>
</cnsl-form-field>
<cnsl-form-field class="app-formfield" appearance="outline">
<cnsl-label>{{ 'APP.OIDC.RESPONSETYPE' | translate }}</cnsl-label>
<mat-select formControlName="responseTypesList" multiple>
<mat-option *ngFor="let type of oidcResponseTypes" [value]="type">
{{ 'APP.OIDC.RESPONSE.'+type | translate }}
</mat-option>
</mat-select>
</cnsl-form-field>
<cnsl-form-field appearance="outline" class="app-formfield">
<cnsl-label>{{ 'APP.AUTHMETHOD' | translate }}</cnsl-label>
<mat-select formControlName="authMethodType">
<mat-option *ngFor="let type of oidcAuthMethodType" [value]="type">
{{ 'APP.OIDC.AUTHMETHOD.'+type | translate }}
</mat-option>
</mat-select>
</cnsl-form-field>
<cnsl-form-field class="app-formfield" appearance="outline">
<cnsl-label>{{ 'APP.OIDC.GRANTTYPE' | translate }}</cnsl-label>
<mat-select formControlName="grantTypesList" multiple>
<mat-option *ngFor="let grant of oidcGrantTypes" [value]="grant">
{{ 'APP.OIDC.GRANT.'+grant | translate }}
</mat-option>
</mat-select>
</cnsl-form-field>
<mat-checkbox color="primary" class="rt" (change)="toggleRefreshToken($event)"
[disabled]="!this.grantTypesList?.value.includes(OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE)"
[checked]="this.grantTypesList?.value.includes(OIDCGrantType.OIDC_GRANT_TYPE_REFRESH_TOKEN)">
{{ 'APP.OIDC.REFRESHTOKEN' | translate }}
</mat-checkbox>
</div>
<p class="app-full-width app-section-title">{{'APP.OIDC.TOKENSECTIONTITLE' | translate}}</p>
<cnsl-form-field appearance="outline" class="app-formfield">
<cnsl-label>{{ 'APP.OIDC.TOKENTYPE' | translate }}</cnsl-label>
<mat-select formControlName="accessTokenType">
<mat-option *ngFor="let type of oidcTokenTypes" [value]="type">
{{ 'APP.OIDC.TOKENTYPE'+type | translate }}
</mat-option>
</mat-select>
</cnsl-form-field>
<mat-checkbox *ngIf="accessTokenType?.value === OIDCTokenType.OIDC_TOKEN_TYPE_JWT" class="full-width"
formControlName="accessTokenRoleAssertion" color="primary">
{{'APP.OIDC.ACCESSTOKENROLEASSERTION' | translate}}</mat-checkbox>
<cnsl-info-section class="app-full-width app-desc">
<span>{{'APP.OIDC.ACCESSTOKENROLEASSERTION_DESCRIPTION' | translate}}</span>
</cnsl-info-section>
<mat-checkbox class="app-full-width" style="margin-top: 1.5rem" formControlName="idTokenRoleAssertion"
color="primary">
{{'APP.OIDC.IDTOKENROLEASSERTION' | translate}}</mat-checkbox>
<cnsl-info-section class="full-width app-desc">
<span>{{'APP.OIDC.IDTOKENROLEASSERTION_DESCRIPTION' | translate}}</span>
</cnsl-info-section>
<mat-checkbox class="app-full-width" style="margin-top: 1.5rem"
formControlName="idTokenUserinfoAssertion" color="primary">
{{'APP.OIDC.IDTOKENUSERINFOASSERTION' | translate}}</mat-checkbox>
<cnsl-info-section class="app-full-width app-desc">
<span>{{'APP.OIDC.IDTOKENUSERINFOASSERTION_DESCRIPTION' | translate}}</span>
</cnsl-info-section>
<p class="clockskew-title cnsl-secondary-text">ClockSkew</p>
<mat-slider color="primary" formControlName="clockSkewSeconds" class="clockskew-slider" thumbLabel
[displayWith]="formatClockSkewLabel" tickInterval=".1" min="0" [step]="1" max="5">
</mat-slider>
<cnsl-info-section class="app-full-width app-desc">
<span>{{'APP.OIDC.CLOCKSKEW' | translate}}</span>
</cnsl-info-section>
</div>
<div class="btn-container">
<button class="submit-button" type="submit" color="primary" [disabled]="oidcForm.invalid || !canWrite"
mat-raised-button>
{{ 'ACTIONS.SAVE' | translate }}
mat-raised-button>
{{ 'ACTIONS.SAVE' | translate }}
</button>
</div>
</form>
</cnsl-card>
</ng-container>
<ng-container *ngIf="currentSetting === 'configuration'">
<cnsl-card *ngIf="currentAuthMethod === 'PK_JWT' && projectId && app?.id"
title="{{ 'USER.MACHINE.KEYSTITLE' | translate }}" description="{{ 'USER.MACHINE.KEYSDESC' | translate }}">
<cnsl-client-keys [projectId]="projectId" [appId]="app.id"></cnsl-client-keys>
<div *ngIf="apiForm && app?.apiConfig" class="btn-container">
<button class="submit-button" (click)="saveAPIApp()" color="primary"
[disabled]="apiForm.invalid || !canWrite" mat-raised-button>
{{ 'ACTIONS.SAVE' | translate }}
</button>
</div>
</form>
</cnsl-card>
</ng-container>
</cnsl-sidenav>
<cnsl-links [links]="nextLinks"></cnsl-links>
</div>
<div class="side" metainfo>
<div class="meta-details">
<div class="meta-row">
<span class="first">{{'RESOURCEID' | translate}}:</span>
<span *ngIf="app?.id" class="second">{{ app.id }}</span>
</div>
<div class="meta-row">
<span class="first">{{'PROJECT.STATE.TITLE' | translate}}:</span>
<span *ngIf="app && app.state !== undefined"
[ngClass]="{'active': app.state === AppState.APP_STATE_ACTIVE, 'inactive': app.state === AppState.APP_STATE_INACTIVE}"
class="state">{{'APP.PAGES.DETAIL.STATE.'+app.state | translate}}</span>
</div>
</div>
<!-- <cnsl-links [links]="nextLinks"></cnsl-links> -->
<div metainfo>
<cnsl-changes *ngIf="app" [changeType]="ChangeType.APP" [id]="app.id" [secId]="projectId"></cnsl-changes>
</div>
</cnsl-meta-layout>
</div>
</cnsl-meta-layout>
</div>
</div>
@@ -1,68 +1,131 @@
.head {
display: flex;
align-items: center;
border-bottom: 1px solid #ffffff20;
flex-wrap: wrap;
@use '@angular/material' as mat;
a {
display: block;
@mixin app-detail-theme($theme) {
$foreground: map-get($theme, foreground);
$background: map-get($theme, background);
$is-dark-theme: map-get($theme, is-dark);
$warn: map-get($theme, warn);
$warn-color: map-get($warn, 500);
$button-text-color: map-get($foreground, text);
$button-disabled-text-color: map-get($foreground, disabled-button);
$divider-color: map-get($foreground, dividers);
.app-zitadel-warning {
font-size: 14px;
color: $warn-color;
}
.title-col {
margin-left: 2rem;
margin-right: 1rem;
.app-err-container {
color: $warn-color;
font-size: 14px;
}
h1 {
font-size: 2rem;
margin: 0;
font-weight: normal;
}
.dev-col {
display: flex;
flex-direction: column;
align-items: flex-start;
span {
font-size: 14px;
margin: .5rem 0;
color: var(--grey);
.devmode {
margin: 1rem 0 0.5rem 0;
}
}
.fill-space {
flex: 1;
.app-desc {
font-size: 14px;
margin: 0;
}
.desc {
.app-input {
width: 100%;
display: block;
font-size: .9rem;
color: var(--grey);
}
.app-info-row {
display: flex;
flex-direction: column;
margin: 0 -0.5rem;
@media only screen and (min-width: 500px) {
flex-direction: row;
flex-wrap: wrap;
}
.app-info-wrapper {
display: flex;
flex-direction: column;
margin: 0 0.5rem;
flex: 1;
align-items: flex-start;
box-sizing: border-box;
&:not(.width) {
min-width: 100px;
}
.app-info-row-title {
font-size: 12px;
margin: 0;
}
.app-info-row-desc {
margin: 0;
font-size: 14px;
padding: 2px 0;
}
.app-copy-row {
display: flex;
flex-direction: column;
width: 100%;
align-items: stretch;
button {
transition: opacity 0.15s ease-in-out;
background-color: #8795a110;
border: 1px solid #8795a160;
border-radius: 4px;
padding: 0.25rem 1rem;
margin: 0.25rem 0;
color: $button-text-color;
text-overflow: ellipsis;
overflow: hidden;
cursor: copy;
&[disabled] {
color: $button-disabled-text-color;
}
}
.environment {
display: flex;
flex-direction: column;
width: 100%;
margin: 0.25rem 0;
.key {
font-size: 14px;
}
}
}
}
}
.app-main-content {
border-top: 1px solid $divider-color;
margin-top: 1.5rem;
}
}
.zitadel-warning {
.compliance {
.compliance-title {
margin: 1rem 0 1rem 0;
font-size: 16px;
}
.problem {
font-size: 14px;
color: rgb(201, 51, 71);
margin: 0 0 0.5rem 0;
display: block;
}
.actions-trigger {
margin-top: .25rem;
display: flex;
align-items: center;
.icon {
margin-left: .5rem;
margin-right: -.5rem;
}
}
}
.err-container {
color: rgb(201, 51, 71);
font-size: 14px;
}
.compliance .problem {
font-size: 14px;
margin-bottom: .5rem;
display: block;
}
.name-content {
@@ -75,7 +138,7 @@
outline: none;
margin-top: 1rem;
margin-right: 1rem;
border-radius: .5rem;
border-radius: 0.5rem;
* {
outline: none;
@@ -93,11 +156,11 @@
.docs-line {
flex-basis: 100%;
margin: .5rem 0;
margin: 0.5rem 0;
p {
margin: 0;
margin-bottom: .5rem;
margin-bottom: 0.5rem;
}
p:last-child {
@@ -105,99 +168,51 @@
}
}
.grid {
.app-oidc-grid {
width: 100%;
display: grid;
grid-gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
.rt {
margin-top: 2.3rem;
margin-left: .5rem;
margin-left: 0.5rem;
}
}
.expandables {
padding-top: 1rem;
.expandable {
display: block;
width: 100%;
margin: 0 .5rem;
.title {
margin: 0;
text-transform: uppercase;
letter-spacing: .05em;
font-size: 14px;
}
.dev-col {
display: flex;
flex-direction: column;
align-items: flex-start;
.devmode {
margin: 1rem 0 .5rem 0;
}
}
.content {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: 0 -.5rem;
padding-bottom: 2rem;
.redirect-section {
flex: 1;
margin: 0 .5rem;
}
}
.desc {
color: var(--grey);
font-size: 14px;
margin: 0;
}
.input {
width: 100%;
}
}
.main-app-content {
display: flex;
flex-direction: column;
}
.formfield {
.app-formfield {
flex: 1 1 30%;
margin: 0 .5rem;
&.full-width {
flex-basis: 100%;
}
}
.section-title {
.app-section-title {
margin: 1.5rem 0 0 0;
}
.full-width {
.app-full-width {
flex-basis: 100%;
margin-left: .5rem;
margin-right: .5rem;
margin-right: 0.5rem;
}
.clockskew-title {
font-size: 14px;
color: var(--grey);
margin: 1rem .5rem 0 .5rem;
margin: 1rem 0.5rem 0 0.5rem;
}
.clockskew-slider {
width: 100%;
margin: 0 .5rem;
margin: 0 0.5rem;
}
.desc {
color: var(--grey);
font-size: 14px;
margin-bottom: 1.5rem;
}
@@ -205,7 +220,7 @@
.error {
font-size: 13px;
color: var(--warn);
margin: 0 .5rem 1.5rem .5rem;
margin: 0 0.5rem 1.5rem 0.5rem;
}
.btn-container {
@@ -213,10 +228,3 @@
justify-content: flex-end;
margin-top: 1rem;
}
.divider {
flex-basis: 100%;
margin: 1.5rem .5rem;
height: 1px;
background-color: rgba(#8795a1, .2);
}
@@ -1,11 +1,12 @@
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
import { Location } from '@angular/common';
import { HttpClient } from '@angular/common/http';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { AbstractControl, FormBuilder, FormControl, FormGroup } from '@angular/forms';
import { MatCheckboxChange } from '@angular/material/checkbox';
import { MatDialog } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { Duration } from 'google-protobuf/google/protobuf/duration_pb';
import { Subject, Subscription } from 'rxjs';
@@ -15,6 +16,7 @@ import { ChangeType } from 'src/app/modules/changes/changes.component';
import { InfoSectionType } from 'src/app/modules/info-section/info-section.component';
import { CnslLinks } from 'src/app/modules/links/links.component';
import { NameDialogComponent } from 'src/app/modules/name-dialog/name-dialog.component';
import { SidenavSetting } from 'src/app/modules/sidenav/sidenav.component';
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
import {
APIAuthMethodType,
@@ -33,6 +35,7 @@ import {
UpdateAPIAppConfigRequest,
UpdateOIDCAppConfigRequest,
} from 'src/app/proto/generated/zitadel/management_pb';
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service';
@@ -64,15 +67,15 @@ export class AppDetailComponent implements OnInit, OnDestroy {
public removable: boolean = true;
public addOnBlur: boolean = true;
public showAdditionalOrigins: boolean = false;
public showRedirects: boolean = false;
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
public authMethods: RadioItemAuthType[] = [];
private subscription?: Subscription;
public projectId: string = '';
public app!: App.AsObject;
public environmentMap: { [key: string]: string } = {};
public oidcResponseTypes: OIDCResponseType[] = [
OIDCResponseType.OIDC_RESPONSE_TYPE_CODE,
OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN,
@@ -96,10 +99,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
];
public oidcTokenTypes: OIDCTokenType[] = [
OIDCTokenType.OIDC_TOKEN_TYPE_BEARER,
OIDCTokenType.OIDC_TOKEN_TYPE_JWT,
];
public oidcTokenTypes: OIDCTokenType[] = [OIDCTokenType.OIDC_TOKEN_TYPE_BEARER, OIDCTokenType.OIDC_TOKEN_TYPE_JWT];
public AppState: any = AppState;
public oidcForm!: FormGroup;
@@ -124,6 +124,10 @@ export class AppDetailComponent implements OnInit, OnDestroy {
public copiedKey: any = '';
public nextLinks: Array<CnslLinks> = [];
public InfoSectionType: any = InfoSectionType;
public copied: string = '';
public settingsList: SidenavSetting[] = [{ id: 'configuration', i18nKey: 'APP.CONFIGURATION', featureRequired: false }];
public currentSetting: string | undefined = this.settingsList[0].id;
constructor(
public translate: TranslateService,
@@ -136,6 +140,8 @@ export class AppDetailComponent implements OnInit, OnDestroy {
private authService: GrpcAuthService,
private router: Router,
private snackbar: MatSnackBar,
private breadcrumbService: BreadcrumbService,
private http: HttpClient,
) {
this.oidcForm = this.fb.group({
devMode: [{ value: false, disabled: true }, []],
@@ -154,12 +160,25 @@ export class AppDetailComponent implements OnInit, OnDestroy {
this.apiForm = this.fb.group({
authMethodType: [{ value: '', disabled: true }],
});
this.http.get('./assets/environment.json').subscribe((env: any) => {
this.environmentMap = {
issuer: env.issuer,
adminServiceUrl: env.adminServiceUrl,
mgmtServiceUrl: env.mgmtServiceUrl,
authServiceUrl: env.adminServiceUrl,
};
});
}
public formatClockSkewLabel(seconds: number): string {
return seconds + 's';
}
public additionalOriginsListChanged(origins: string[]): void {
this.additionalOriginsList = origins;
}
public openNameDialog(): void {
const dialogRef = this.dialog.open(NameDialogComponent, {
data: {
@@ -171,7 +190,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
width: '400px',
});
dialogRef.afterClosed().subscribe(name => {
dialogRef.afterClosed().subscribe((name) => {
if (name) {
this.app.name = name;
this.saveApp();
@@ -180,7 +199,13 @@ export class AppDetailComponent implements OnInit, OnDestroy {
}
public ngOnInit(): void {
this.subscription = this.route.params.subscribe(params => this.getData(params));
const projectId = this.route.snapshot.paramMap.get('projectid');
const appId = this.route.snapshot.paramMap.get('appid');
if (projectId && appId) {
this.projectId = projectId;
this.getData(projectId, appId);
}
}
public ngOnDestroy(): void {
@@ -192,7 +217,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
{
i18nTitle: 'APP.PAGES.NEXTSTEPS.0.TITLE',
i18nDesc: 'APP.PAGES.NEXTSTEPS.0.DESC',
routerLink: ['/projects', this.projectId],
routerLink: ['/projects', this.projectId, 'roles'],
iconClasses: 'las la-user-tag',
},
{
@@ -200,7 +225,8 @@ export class AppDetailComponent implements OnInit, OnDestroy {
i18nDesc: 'APP.PAGES.NEXTSTEPS.1.DESC',
routerLink: ['/users', 'create'],
iconClasses: 'las la-user-plus',
}, {
},
{
i18nTitle: 'APP.PAGES.NEXTSTEPS.2.TITLE',
i18nDesc: 'APP.PAGES.NEXTSTEPS.2.DESC',
href: 'https://docs.zitadel.ch',
@@ -209,107 +235,151 @@ export class AppDetailComponent implements OnInit, OnDestroy {
];
}
private async getData({ projectid, id }: Params): Promise<void> {
this.projectId = projectid;
private async getData(projectId: string, appId: string): Promise<void> {
this.initLinks();
this.mgmtService.getIAM().then(iam => {
this.mgmtService.getIAM().then((iam) => {
this.isZitadel = iam.iamProjectId === this.projectId;
});
this.authService.isAllowed(['project.app.write$', 'project.app.write:' + projectid])
this.authService
.isAllowed(['project.app.write$', 'project.app.write:' + projectId])
.pipe(take(1))
.subscribe((allowed) => {
this.canWrite = allowed;
this.mgmtService.getAppByID(projectid, id).then(app => {
if (app.app) {
this.app = app.app;
if (this.app.oidcConfig) {
this.getAuthMethodOptions('OIDC');
this.mgmtService
.getAppByID(projectId, appId)
.then((app) => {
if (app.app) {
this.app = app.app;
this.initialAuthMethod = this.authMethodFromPartialConfig({ oidc: this.app.oidcConfig });
this.currentAuthMethod = this.initialAuthMethod;
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
if (!this.authMethods.includes(CUSTOM_METHOD)) {
this.authMethods.push(CUSTOM_METHOD);
const breadcrumbs = [
new Breadcrumb({
type: BreadcrumbType.IAM,
name: 'IAM',
routerLink: ['/system'],
}),
new Breadcrumb({
type: BreadcrumbType.ORG,
routerLink: ['/org'],
}),
new Breadcrumb({
type: BreadcrumbType.PROJECT,
name: '',
param: { key: 'projectid', value: projectId },
routerLink: ['/projects', projectId],
}),
new Breadcrumb({
type: BreadcrumbType.APP,
name: app.app.name,
param: { key: 'appid', value: appId },
routerLink: ['/projects', projectId, 'apps', appId],
}),
];
this.breadcrumbService.setBreadcrumb(breadcrumbs);
if (this.app.oidcConfig) {
this.getAuthMethodOptions('OIDC');
this.settingsList = [
{ id: 'configuration', i18nKey: 'APP.CONFIGURATION', featureRequired: false },
{ id: 'redirect-uris', i18nKey: 'APP.OIDC.REDIRECTSECTIONTITLE', featureRequired: false },
{ id: 'additional-origins', i18nKey: 'APP.ADDITIONALORIGINS', featureRequired: false },
{ id: 'urls', i18nKey: 'APP.URLS', featureRequired: false },
];
this.initialAuthMethod = this.authMethodFromPartialConfig({ oidc: this.app.oidcConfig });
this.currentAuthMethod = this.initialAuthMethod;
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
if (!this.authMethods.includes(CUSTOM_METHOD)) {
this.authMethods.push(CUSTOM_METHOD);
}
} else {
this.authMethods = this.authMethods.filter((element) => element !== CUSTOM_METHOD);
}
} else {
this.authMethods = this.authMethods.filter(element => element !== CUSTOM_METHOD);
}
} else if (this.app.apiConfig) {
this.getAuthMethodOptions('API');
} else if (this.app.apiConfig) {
this.getAuthMethodOptions('API');
this.initialAuthMethod = this.authMethodFromPartialConfig({ api: this.app.apiConfig });
this.currentAuthMethod = this.initialAuthMethod;
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
if (!this.authMethods.includes(CUSTOM_METHOD)) {
this.authMethods.push(CUSTOM_METHOD);
this.initialAuthMethod = this.authMethodFromPartialConfig({ api: this.app.apiConfig });
if (this.initialAuthMethod === 'BASIC') {
this.settingsList = [{ id: 'urls', i18nKey: 'APP.URLS', featureRequired: false }];
this.currentSetting = 'urls';
} else {
this.settingsList = [
{ id: 'configuration', i18nKey: 'APP.CONFIGURATION', featureRequired: false },
{ id: 'urls', i18nKey: 'APP.URLS', featureRequired: false },
];
}
} else {
this.authMethods = this.authMethods.filter(element => element !== CUSTOM_METHOD);
}
}
if (allowed) {
this.oidcForm.enable();
this.apiForm.enable();
}
if (this.app.oidcConfig?.redirectUrisList) {
this.redirectUrisList = this.app.oidcConfig.redirectUrisList;
}
if (this.app.oidcConfig?.postLogoutRedirectUrisList) {
this.postLogoutRedirectUrisList = this.app.oidcConfig.postLogoutRedirectUrisList;
}
if (this.app.oidcConfig?.additionalOriginsList) {
this.additionalOriginsList = this.app.oidcConfig.additionalOriginsList;
}
if (this.app.oidcConfig?.clockSkew) {
const inSecs = this.app.oidcConfig?.clockSkew.seconds +
this.app.oidcConfig?.clockSkew.nanos / 100000;
this.oidcForm.controls['clockSkewSeconds'].setValue(inSecs);
}
if (this.app.oidcConfig) {
this.oidcForm.patchValue(this.app.oidcConfig);
}
if (this.app.apiConfig) {
this.apiForm.patchValue(this.app.apiConfig);
}
this.oidcForm.valueChanges.subscribe((oidcConfig) => {
this.initialAuthMethod = this.authMethodFromPartialConfig({ oidc: oidcConfig });
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
if (!this.authMethods.includes(CUSTOM_METHOD)) {
this.authMethods.push(CUSTOM_METHOD);
this.currentAuthMethod = this.initialAuthMethod;
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
if (!this.authMethods.includes(CUSTOM_METHOD)) {
this.authMethods.push(CUSTOM_METHOD);
}
} else {
this.authMethods = this.authMethods.filter((element) => element !== CUSTOM_METHOD);
}
} else {
this.authMethods = this.authMethods.filter(element => element !== CUSTOM_METHOD);
}
this.showSaveSnack();
});
this.apiForm.valueChanges.subscribe((apiConfig) => {
this.initialAuthMethod = this.authMethodFromPartialConfig({ api: apiConfig });
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
if (!this.authMethods.includes(CUSTOM_METHOD)) {
this.authMethods.push(CUSTOM_METHOD);
}
} else {
this.authMethods = this.authMethods.filter(element => element !== CUSTOM_METHOD);
if (allowed) {
this.oidcForm.enable();
this.apiForm.enable();
}
this.showSaveSnack();
});
}
}).catch(error => {
console.error(error);
this.toast.showError(error);
this.errorMessage = error.message;
});
if (this.app.oidcConfig?.redirectUrisList) {
this.redirectUrisList = this.app.oidcConfig.redirectUrisList;
}
if (this.app.oidcConfig?.postLogoutRedirectUrisList) {
this.postLogoutRedirectUrisList = this.app.oidcConfig.postLogoutRedirectUrisList;
}
if (this.app.oidcConfig?.additionalOriginsList) {
this.additionalOriginsList = this.app.oidcConfig.additionalOriginsList;
}
if (this.app.oidcConfig?.clockSkew) {
const inSecs = this.app.oidcConfig?.clockSkew.seconds + this.app.oidcConfig?.clockSkew.nanos / 100000;
this.oidcForm.controls['clockSkewSeconds'].setValue(inSecs);
}
if (this.app.oidcConfig) {
this.oidcForm.patchValue(this.app.oidcConfig);
}
if (this.app.apiConfig) {
this.apiForm.patchValue(this.app.apiConfig);
}
this.oidcForm.valueChanges.subscribe((oidcConfig) => {
this.initialAuthMethod = this.authMethodFromPartialConfig({ oidc: oidcConfig });
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
if (!this.authMethods.includes(CUSTOM_METHOD)) {
this.authMethods.push(CUSTOM_METHOD);
}
} else {
this.authMethods = this.authMethods.filter((element) => element !== CUSTOM_METHOD);
}
this.showSaveSnack();
});
this.apiForm.valueChanges.subscribe((apiConfig) => {
this.initialAuthMethod = this.authMethodFromPartialConfig({ api: apiConfig });
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
if (!this.authMethods.includes(CUSTOM_METHOD)) {
this.authMethods.push(CUSTOM_METHOD);
}
} else {
this.authMethods = this.authMethods.filter((element) => element !== CUSTOM_METHOD);
}
this.showSaveSnack();
});
}
})
.catch((error) => {
console.error(error);
this.toast.showError(error);
this.errorMessage = error.message;
});
});
this.docs = (await this.mgmtService.getOIDCInformation());
this.docs = await this.mgmtService.getOIDCInformation();
}
private async showSaveSnack(): Promise<void> {
@@ -330,36 +400,22 @@ export class AppDetailComponent implements OnInit, OnDestroy {
if (type === 'OIDC') {
switch (this.app.oidcConfig?.appType) {
case OIDCAppType.OIDC_APP_TYPE_NATIVE:
this.authMethods = [
PKCE_METHOD,
CUSTOM_METHOD,
];
this.authMethods = [PKCE_METHOD, CUSTOM_METHOD];
break;
case OIDCAppType.OIDC_APP_TYPE_WEB:
this.authMethods = [
PKCE_METHOD,
CODE_METHOD,
PK_JWT_METHOD,
POST_METHOD,
];
this.authMethods = [PKCE_METHOD, CODE_METHOD, PK_JWT_METHOD, POST_METHOD];
break;
case OIDCAppType.OIDC_APP_TYPE_USER_AGENT:
this.authMethods = [
PKCE_METHOD,
IMPLICIT_METHOD,
];
this.authMethods = [PKCE_METHOD, IMPLICIT_METHOD];
break;
}
}
if (type === 'API') {
this.authMethods = [
PK_JWT_METHOD,
BASIC_AUTH_METHOD,
];
this.authMethods = [PK_JWT_METHOD, BASIC_AUTH_METHOD];
}
}
public authMethodFromPartialConfig(config: { oidc?: OIDCConfig.AsObject, api?: APIConfig.AsObject; }): string {
public authMethodFromPartialConfig(config: { oidc?: OIDCConfig.AsObject; api?: APIConfig.AsObject }): string {
const key = getAuthMethodFromPartialConfig(config);
return key;
}
@@ -367,23 +423,17 @@ export class AppDetailComponent implements OnInit, OnDestroy {
public setPartialConfigFromAuthMethod(authMethod: string): void {
const partialConfig = getPartialConfigFromAuthMethod(authMethod);
if (partialConfig && partialConfig.oidc && this.app.oidcConfig) {
this.app.oidcConfig.responseTypesList =
(partialConfig.oidc as Partial<OIDCConfig.AsObject>).responseTypesList
?? [];
this.app.oidcConfig.responseTypesList = (partialConfig.oidc as Partial<OIDCConfig.AsObject>).responseTypesList ?? [];
this.app.oidcConfig.grantTypesList =
(partialConfig.oidc as Partial<OIDCConfig.AsObject>).grantTypesList
?? [];
this.app.oidcConfig.grantTypesList = (partialConfig.oidc as Partial<OIDCConfig.AsObject>).grantTypesList ?? [];
this.app.oidcConfig.authMethodType =
(partialConfig.oidc as Partial<OIDCConfig.AsObject>).authMethodType
?? OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE;
(partialConfig.oidc as Partial<OIDCConfig.AsObject>).authMethodType ?? OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE;
this.oidcForm.patchValue(this.app.oidcConfig);
} else if (partialConfig && partialConfig.api && this.app.apiConfig) {
this.app.apiConfig.authMethodType =
(partialConfig.api as Partial<APIConfig.AsObject>).authMethodType
?? APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC;
(partialConfig.api as Partial<APIConfig.AsObject>).authMethodType ?? APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC;
this.apiForm.patchValue(this.app.apiConfig);
}
@@ -399,34 +449,43 @@ export class AppDetailComponent implements OnInit, OnDestroy {
},
width: '400px',
});
dialogRef.afterClosed().subscribe(resp => {
dialogRef.afterClosed().subscribe((resp) => {
if (resp && this.projectId && this.app.id) {
this.mgmtService.removeApp(this.projectId, this.app.id).then(() => {
this.toast.showInfo('APP.TOAST.DELETED', true);
this.mgmtService
.removeApp(this.projectId, this.app.id)
.then(() => {
this.toast.showInfo('APP.TOAST.DELETED', true);
this.router.navigate(['/projects', this.projectId]);
}).catch(error => {
this.toast.showError(error);
});
this.router.navigate(['/projects', this.projectId]);
})
.catch((error) => {
this.toast.showError(error);
});
}
});
}
public changeState(state: AppState): void {
if (state === AppState.APP_STATE_ACTIVE) {
this.mgmtService.reactivateApp(this.projectId, this.app.id).then(() => {
this.app.state = state;
this.toast.showInfo('APP.TOAST.REACTIVATED', true);
}).catch((error: any) => {
this.toast.showError(error);
});
this.mgmtService
.reactivateApp(this.projectId, this.app.id)
.then(() => {
this.app.state = state;
this.toast.showInfo('APP.TOAST.REACTIVATED', true);
})
.catch((error: any) => {
this.toast.showError(error);
});
} else if (state === AppState.APP_STATE_INACTIVE) {
this.mgmtService.deactivateApp(this.projectId, this.app.id).then(() => {
this.app.state = state;
this.toast.showInfo('APP.TOAST.DEACTIVATED', true);
}).catch((error: any) => {
this.toast.showError(error);
});
this.mgmtService
.deactivateApp(this.projectId, this.app.id)
.then(() => {
this.app.state = state;
this.toast.showInfo('APP.TOAST.DEACTIVATED', true);
})
.catch((error: any) => {
this.toast.showError(error);
});
}
}
@@ -437,7 +496,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
this.toast.showInfo('APP.TOAST.UPDATED', true);
this.editState = false;
})
.catch(error => {
.catch((error) => {
this.toast.showError(error);
});
}
@@ -450,8 +509,9 @@ export class AppDetailComponent implements OnInit, OnDestroy {
this.grantTypesList?.setValue([OIDCGrantType.OIDC_GRANT_TYPE_REFRESH_TOKEN, ...c]);
}
} else {
const index = (this.grantTypesList?.value as OIDCGrantType[])
.findIndex(gt => gt === OIDCGrantType.OIDC_GRANT_TYPE_REFRESH_TOKEN);
const index = (this.grantTypesList?.value as OIDCGrantType[]).findIndex(
(gt) => gt === OIDCGrantType.OIDC_GRANT_TYPE_REFRESH_TOKEN,
);
if (index > -1) {
const copy = Object.assign([], this.grantTypesList?.value);
copy.splice(index, 1);
@@ -460,7 +520,6 @@ export class AppDetailComponent implements OnInit, OnDestroy {
}
}
public saveOIDCApp(): void {
this.requestRedirectValuesSubject$.next();
if (this.oidcForm.valid) {
@@ -496,7 +555,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
if (this.clockSkewSeconds?.value) {
const dur = new Duration();
dur.setSeconds(Math.floor(this.clockSkewSeconds?.value));
dur.setNanos((Math.floor(this.clockSkewSeconds?.value % 1) * 10000));
dur.setNanos(Math.floor(this.clockSkewSeconds?.value % 1) * 10000);
req.setClockSkew(dur);
}
this.mgmtService
@@ -508,7 +567,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
}
this.toast.showInfo('APP.TOAST.OIDCUPDATED', true);
})
.catch(error => {
.catch((error) => {
this.toast.showError(error);
});
}
@@ -530,44 +589,59 @@ export class AppDetailComponent implements OnInit, OnDestroy {
if (this.app.apiConfig) {
const config = { api: this.app.apiConfig };
this.currentAuthMethod = this.authMethodFromPartialConfig(config);
if (this.currentAuthMethod === 'BASIC') {
this.settingsList = [{ id: 'urls', i18nKey: 'APP.URLS', featureRequired: false }];
this.currentSetting = 'urls';
} else {
this.settingsList = [
{ id: 'configuration', i18nKey: 'APP.CONFIGURATION', featureRequired: false },
{ id: 'urls', i18nKey: 'APP.URLS', featureRequired: false },
];
this.currentSetting = 'configuration';
}
}
this.toast.showInfo('APP.TOAST.APIUPDATED', true);
})
.catch(error => {
.catch((error) => {
this.toast.showError(error);
});
}
}
public regenerateOIDCClientSecret(): void {
this.mgmtService.regenerateOIDCClientSecret(this.app.id, this.projectId).then(resp => {
this.toast.showInfo('APP.TOAST.CLIENTSECRETREGENERATED', true);
this.dialog.open(AppSecretDialogComponent, {
data: {
// clientId: data.toObject() as ClientSecret.AsObject.clientId,
clientSecret: resp.clientSecret,
},
width: '400px',
this.mgmtService
.regenerateOIDCClientSecret(this.app.id, this.projectId)
.then((resp) => {
this.toast.showInfo('APP.TOAST.CLIENTSECRETREGENERATED', true);
this.dialog.open(AppSecretDialogComponent, {
data: {
// clientId: data.toObject() as ClientSecret.AsObject.clientId,
clientSecret: resp.clientSecret,
},
width: '400px',
});
})
.catch((error) => {
this.toast.showError(error);
});
}).catch(error => {
this.toast.showError(error);
});
}
public regenerateAPIClientSecret(): void {
this.mgmtService.regenerateAPIClientSecret(this.app.id, this.projectId).then(resp => {
this.toast.showInfo('APP.TOAST.CLIENTSECRETREGENERATED', true);
this.dialog.open(AppSecretDialogComponent, {
data: {
clientSecret: resp.clientSecret,
},
width: '400px',
this.mgmtService
.regenerateAPIClientSecret(this.app.id, this.projectId)
.then((resp) => {
this.toast.showInfo('APP.TOAST.CLIENTSECRETREGENERATED', true);
this.dialog.open(AppSecretDialogComponent, {
data: {
clientSecret: resp.clientSecret,
},
width: '400px',
});
})
.catch((error) => {
this.toast.showError(error);
});
}).catch(error => {
this.toast.showError(error);
});
}
public navigateBack(): void {
@@ -1,33 +1,33 @@
<h1 mat-dialog-title>
<span class="title">{{'APP.OIDC.CLIENTSECRET' | translate}}</span>
<span class="title">{{'APP.OIDC.CLIENTSECRET' | translate}}</span>
</h1>
<p class="desc">{{'APP.OIDC.CLIENTSECRET_DESCRIPTION' | translate}}</p>
<p class="desc cnsl-secondary-text">{{'APP.OIDC.CLIENTSECRET_DESCRIPTION' | translate}}</p>
<div mat-dialog-content>
<div class="flex" *ngIf="data.clientId">
<span class="overflow-auto"><span class="desc">ClientId:</span> {{data.clientId}}</span>
<button color="primary" [disabled]="copied === data.clientId" matTooltip="copy to clipboard" cnslCopyToClipboard
[valueToCopy]="data.clientId" (copiedValue)="this.copied = $event" mat-icon-button>
<i *ngIf="copied !== data.clientId" class="las la-clipboard"></i>
<i *ngIf="copied === data.clientId" class="las la-clipboard-check"></i>
</button>
</div>
<div class="flex" *ngIf="data.clientId">
<span class="overflow-auto"><span class="desc">ClientId:</span> {{data.clientId}}</span>
<button color="primary" [disabled]="copied === data.clientId" matTooltip="copy to clipboard" cnslCopyToClipboard
[valueToCopy]="data.clientId" (copiedValue)="this.copied = $event" mat-icon-button>
<i *ngIf="copied !== data.clientId" class="las la-clipboard"></i>
<i *ngIf="copied === data.clientId" class="las la-clipboard-check"></i>
</button>
</div>
<div *ngIf="data.clientSecret; else showNoSecretInfo" class="flex">
<span class="overflow-auto"><span class="desc">ClientSecret:</span> {{data.clientSecret}}</span>
<button color="primary" [disabled]="copied === data.clientSecret" matTooltip="copy to clipboard"
cnslCopyToClipboard [valueToCopy]="data.clientSecret" (copiedValue)="this.copied = $event" mat-icon-button>
<i *ngIf="copied !== data.clientSecret" class="las la-clipboard"></i>
<i *ngIf="copied === data.clientSecret" class="las la-clipboard-check"></i>
</button>
</div>
<div *ngIf="data.clientSecret; else showNoSecretInfo" class="flex">
<span class="overflow-auto"><span class="desc cnsl-secondary-text">ClientSecret:</span> {{data.clientSecret}}</span>
<button color="primary" [disabled]="copied === data.clientSecret" matTooltip="copy to clipboard" cnslCopyToClipboard
[valueToCopy]="data.clientSecret" (copiedValue)="this.copied = $event" mat-icon-button>
<i *ngIf="copied !== data.clientSecret" class="las la-clipboard"></i>
<i *ngIf="copied === data.clientSecret" class="las la-clipboard-check"></i>
</button>
</div>
<ng-template #showNoSecretInfo>
<cnsl-info-section>{{'APP.OIDC.CLIENTSECRET_NOSECRET' | translate}}</cnsl-info-section>
</ng-template>
<ng-template #showNoSecretInfo>
<cnsl-info-section>{{'APP.OIDC.CLIENTSECRET_NOSECRET' | translate}}</cnsl-info-section>
</ng-template>
</div>
<div mat-dialog-actions class="action">
<button cdkFocusInitial color="primary" mat-raised-button class="ok-button" (click)="closeDialog()">
{{'ACTIONS.CLOSE' | translate}}
</button>
<button cdkFocusInitial color="primary" mat-raised-button class="ok-button" (click)="closeDialog()"
[attr.data-e2e]="'close-dialog'">
{{'ACTIONS.CLOSE' | translate}}
</button>
</div>
@@ -3,8 +3,7 @@
}
.desc {
color: var(--grey);
font-size: .9rem;
font-size: 0.9rem;
}
.full-width {
@@ -16,7 +15,7 @@
justify-content: flex-end;
.ok-button {
margin-left: .5rem;
margin-left: 0.5rem;
}
}
@@ -24,7 +23,8 @@
display: flex;
align-items: center;
border: 1px solid #ffffff20;
border-radius: .5rem;
border-radius: 0.5rem;
padding-left: 0.5rem;
justify-content: space-between;
.overflow-auto {
@@ -32,7 +32,6 @@
.desc {
font-size: 14px;
color: var(--grey);
}
}
}
@@ -5,20 +5,20 @@ import { AppCreateComponent } from '../apps/app-create/app-create.component';
import { AppDetailComponent } from '../apps/app-detail/app-detail.component';
const routes: Routes = [
{
path: 'create',
component: AppCreateComponent,
data: { animation: 'AddPage' },
},
{
path: ':id',
component: AppDetailComponent,
data: { animation: 'HomePage' },
},
{
path: 'create',
component: AppCreateComponent,
data: { animation: 'AddPage' },
},
{
path: ':appid',
component: AppDetailComponent,
data: { animation: 'HomePage' },
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class AppsRoutingModule { }
export class AppsRoutingModule {}
@@ -3,7 +3,6 @@ import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatChipsModule } from '@angular/material/chips';
import { MatDialogModule } from '@angular/material/dialog';
@@ -30,6 +29,9 @@ import { InputModule } from 'src/app/modules/input/input.module';
import { LinksModule } from 'src/app/modules/links/links.module';
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
import { NameDialogModule } from 'src/app/modules/name-dialog/name-dialog.module';
import { SidenavModule } from 'src/app/modules/sidenav/sidenav.module';
import { TopViewModule } from 'src/app/modules/top-view/top-view.module';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { OriginPipeModule } from 'src/app/pipes/origin-pipe/origin-pipe.module';
import { RedirectPipeModule } from 'src/app/pipes/redirect-pipe/redirect-pipe.module';
@@ -60,21 +62,22 @@ import { RedirectUrisComponent } from './redirect-uris/redirect-uris.component';
InfoRowModule,
TranslateModule,
OriginPipeModule,
MatMenuModule,
ReactiveFormsModule,
HasRoleModule,
MatMenuModule,
SidenavModule,
MatChipsModule,
ClientKeysModule,
HasRolePipeModule,
MatIconModule,
MatSelectModule,
MatButtonToggleModule,
MatButtonModule,
MatProgressSpinnerModule,
MatProgressBarModule,
MatDialogModule,
MatCheckboxModule,
CardModule,
TopViewModule,
MatMenuModule,
MatTooltipModule,
TranslateModule,
MatStepperModule,
@@ -89,4 +92,4 @@ import { RedirectUrisComponent } from './redirect-uris/redirect-uris.component';
],
exports: [TranslateModule],
})
export class AppsModule { }
export class AppsModule {}
@@ -14,7 +14,7 @@ export const CODE_METHOD: RadioItemAuthType = {
descI18nKey: 'APP.AUTHMETHODS.CODE.DESCRIPTION',
disabled: false,
prefix: 'CODE',
background: 'rgb(89 115 128)',
background: 'linear-gradient(40deg, rgb(25 105 143) 30%, rgb(23 95 129))',
responseType: OIDCResponseType.OIDC_RESPONSE_TYPE_CODE,
grantType: OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE,
authMethod: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC,
@@ -26,7 +26,7 @@ export const PKCE_METHOD: RadioItemAuthType = {
descI18nKey: 'APP.AUTHMETHODS.PKCE.DESCRIPTION',
disabled: false,
prefix: 'PKCE',
background: 'rgb(80 110 92)',
background: 'linear-gradient(40deg, #059669 30%, #047857)',
responseType: OIDCResponseType.OIDC_RESPONSE_TYPE_CODE,
grantType: OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE,
authMethod: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
@@ -38,7 +38,7 @@ export const POST_METHOD: RadioItemAuthType = {
descI18nKey: 'APP.AUTHMETHODS.POST.DESCRIPTION',
disabled: false,
prefix: 'POST',
background: 'rgb(144 75 75)',
background: 'linear-gradient(40deg, #c53b3b 30%, rgb(169 51 51))',
responseType: OIDCResponseType.OIDC_RESPONSE_TYPE_CODE,
grantType: OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE,
authMethod: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST,
@@ -50,7 +50,7 @@ export const PK_JWT_METHOD: RadioItemAuthType = {
descI18nKey: 'APP.AUTHMETHODS.PK_JWT.DESCRIPTION',
disabled: false,
prefix: 'JWT',
background: 'rgb(89, 93, 128)',
background: 'linear-gradient(40deg, rgb(70 77 145) 30%, rgb(58 65 124))',
responseType: OIDCResponseType.OIDC_RESPONSE_TYPE_CODE,
grantType: OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE,
authMethod: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
@@ -63,7 +63,7 @@ export const BASIC_AUTH_METHOD: RadioItemAuthType = {
descI18nKey: 'APP.AUTHMETHODS.BASIC.DESCRIPTION',
disabled: false,
prefix: 'BASIC',
background: 'rgb(144 75 75)',
background: 'linear-gradient(40deg, #c53b3b 30%, rgb(169 51 51))',
responseType: OIDCResponseType.OIDC_RESPONSE_TYPE_CODE,
grantType: OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE,
authMethod: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST,
@@ -76,7 +76,7 @@ export const IMPLICIT_METHOD: RadioItemAuthType = {
descI18nKey: 'APP.AUTHMETHODS.IMPLICIT.DESCRIPTION',
disabled: false,
prefix: 'IMP',
background: 'rgb(144 75 75)',
background: 'linear-gradient(40deg, #c53b3b 30%, rgb(169 51 51))',
responseType: OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN,
grantType: OIDCGrantType.OIDC_GRANT_TYPE_IMPLICIT,
authMethod: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
@@ -89,16 +89,18 @@ export const CUSTOM_METHOD: RadioItemAuthType = {
descI18nKey: 'APP.AUTHMETHODS.CUSTOM.DESCRIPTION',
disabled: false,
prefix: 'CUSTOM',
background: '#333',
background: 'linear-gradient(40deg, #1f2937, #111827)',
};
export function getPartialConfigFromAuthMethod(authMethod: string): {
oidc?: Partial<OIDCConfig.AsObject>;
api?: Partial<APIConfig.AsObject>;
} | undefined {
export function getPartialConfigFromAuthMethod(authMethod: string):
| {
oidc?: Partial<OIDCConfig.AsObject>;
api?: Partial<APIConfig.AsObject>;
}
| undefined {
let config: {
oidc?: Partial<OIDCConfig.AsObject>,
api?: Partial<APIConfig.AsObject>,
oidc?: Partial<OIDCConfig.AsObject>;
api?: Partial<APIConfig.AsObject>;
};
switch (authMethod) {
case CODE_METHOD.key:
@@ -167,107 +169,98 @@ export function getPartialConfigFromAuthMethod(authMethod: string): {
}
}
export function getAuthMethodFromPartialConfig(
config: {
oidc?: Partial<OIDCConfig.AsObject>,
api?: Partial<APIConfig.AsObject>,
},
): string {
export function getAuthMethodFromPartialConfig(config: {
oidc?: Partial<OIDCConfig.AsObject>;
api?: Partial<APIConfig.AsObject>;
}): string {
if (config?.oidc) {
const toCheck = [config.oidc.responseTypesList, config.oidc.grantTypesList?.sort(), config.oidc.authMethodType];
const code = JSON.stringify(
[
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC,
],
);
const code = JSON.stringify([
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC,
]);
const codeWithRefresh = JSON.stringify(
[
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE, OIDCGrantType.OIDC_GRANT_TYPE_REFRESH_TOKEN].sort(),
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC,
],
);
const codeWithRefresh = JSON.stringify([
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE, OIDCGrantType.OIDC_GRANT_TYPE_REFRESH_TOKEN].sort(),
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC,
]);
const pkce = JSON.stringify(
[
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
],
);
const pkce = JSON.stringify([
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
]);
const pkceWithRefresh = JSON.stringify(
[
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE, OIDCGrantType.OIDC_GRANT_TYPE_REFRESH_TOKEN].sort(),
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
],
);
const pkceWithRefresh = JSON.stringify([
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE, OIDCGrantType.OIDC_GRANT_TYPE_REFRESH_TOKEN].sort(),
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
]);
const post = JSON.stringify(
[
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST,
],
);
const post = JSON.stringify([
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST,
]);
const postWithRefresh = JSON.stringify(
[
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE, OIDCGrantType.OIDC_GRANT_TYPE_REFRESH_TOKEN].sort(),
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST,
],
);
const postWithRefresh = JSON.stringify([
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE, OIDCGrantType.OIDC_GRANT_TYPE_REFRESH_TOKEN].sort(),
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST,
]);
const pkjwt = JSON.stringify(
[
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
],
);
const pkjwt = JSON.stringify([
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
]);
const pkjwtWithRefresh = JSON.stringify(
[
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE, OIDCGrantType.OIDC_GRANT_TYPE_REFRESH_TOKEN].sort(),
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
],
);
const pkjwtWithRefresh = JSON.stringify([
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE, OIDCGrantType.OIDC_GRANT_TYPE_REFRESH_TOKEN].sort(),
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
]);
const implicit = JSON.stringify(
[
[OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN_TOKEN],
[OIDCGrantType.OIDC_GRANT_TYPE_IMPLICIT],
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
],
);
const implicit = JSON.stringify([
[OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN_TOKEN],
[OIDCGrantType.OIDC_GRANT_TYPE_IMPLICIT],
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
]);
switch (JSON.stringify(toCheck)) {
case code: return CODE_METHOD.key;
case codeWithRefresh: return CODE_METHOD.key;
case code:
return CODE_METHOD.key;
case codeWithRefresh:
return CODE_METHOD.key;
case pkce: return PKCE_METHOD.key;
case pkceWithRefresh: return PKCE_METHOD.key;
case pkce:
return PKCE_METHOD.key;
case pkceWithRefresh:
return PKCE_METHOD.key;
case post: return POST_METHOD.key;
case postWithRefresh: return POST_METHOD.key;
case post:
return POST_METHOD.key;
case postWithRefresh:
return POST_METHOD.key;
case pkjwt: return PK_JWT_METHOD.key;
case pkjwtWithRefresh: return PK_JWT_METHOD.key;
case pkjwt:
return PK_JWT_METHOD.key;
case pkjwtWithRefresh:
return PK_JWT_METHOD.key;
case implicit: return IMPLICIT_METHOD.key;
case implicit:
return IMPLICIT_METHOD.key;
default:
return CUSTOM_METHOD.key;
}
} else if (config.api && config.api.authMethodType !== undefined) {
switch (config.api.authMethodType.toString()) {
case APIAuthMethodType.API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT.toString(): return PK_JWT_METHOD.key;
case APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC.toString(): return BASIC_AUTH_METHOD.key;
case APIAuthMethodType.API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT.toString():
return PK_JWT_METHOD.key;
case APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC.toString():
return BASIC_AUTH_METHOD.key;
default:
return CUSTOM_METHOD.key;
}
@@ -8,55 +8,55 @@ import { OIDCAppType } from 'src/app/proto/generated/zitadel/app_pb';
// }
export enum AppCreateType {
API = 'API',
OIDC = 'OIDC',
API = 'API',
OIDC = 'OIDC',
}
export interface RadioItemAppType {
// key: string;
createType: AppCreateType;
oidcAppType?: OIDCAppType;
titleI18nKey: string;
descI18nKey: string;
prefix: string;
background: string;
// key: string;
createType: AppCreateType;
oidcAppType?: OIDCAppType;
titleI18nKey: string;
descI18nKey: string;
prefix: string;
background: string;
}
export const WEB_TYPE = {
// key: AppType.WEB,
titleI18nKey: 'APP.OIDC.SELECTION.APPTYPE.WEB.TITLE',
descI18nKey: 'APP.OIDC.SELECTION.APPTYPE.WEB.DESCRIPTION',
createType: AppCreateType.OIDC,
oidcAppType: OIDCAppType.OIDC_APP_TYPE_WEB,
prefix: 'WEB',
background: 'rgb(80, 110, 110)',
export const WEB_TYPE: RadioItemAppType = {
// key: AppType.WEB,
titleI18nKey: 'APP.OIDC.SELECTION.APPTYPE.WEB.TITLE',
descI18nKey: 'APP.OIDC.SELECTION.APPTYPE.WEB.DESCRIPTION',
createType: AppCreateType.OIDC,
oidcAppType: OIDCAppType.OIDC_APP_TYPE_WEB,
prefix: 'WEB',
background: 'linear-gradient(40deg, #059669 30%, #047857)',
};
export const USER_AGENT_TYPE = {
// key: AppType.USER_AGENT,
titleI18nKey: 'APP.OIDC.SELECTION.APPTYPE.USERAGENT.TITLE',
descI18nKey: 'APP.OIDC.SELECTION.APPTYPE.USERAGENT.DESCRIPTION',
createType: AppCreateType.OIDC,
oidcAppType: OIDCAppType.OIDC_APP_TYPE_USER_AGENT,
prefix: 'UA',
background: '#6a506e',
export const USER_AGENT_TYPE: RadioItemAppType = {
// key: AppType.USER_AGENT,
titleI18nKey: 'APP.OIDC.SELECTION.APPTYPE.USERAGENT.TITLE',
descI18nKey: 'APP.OIDC.SELECTION.APPTYPE.USERAGENT.DESCRIPTION',
createType: AppCreateType.OIDC,
oidcAppType: OIDCAppType.OIDC_APP_TYPE_USER_AGENT,
prefix: 'UA',
background: 'linear-gradient(40deg, #dc2626 30%, #db2777)',
};
export const NATIVE_TYPE = {
// key: AppType.NATIVE,
titleI18nKey: 'APP.OIDC.SELECTION.APPTYPE.NATIVE.TITLE',
descI18nKey: 'APP.OIDC.SELECTION.APPTYPE.NATIVE.DESCRIPTION',
createType: AppCreateType.OIDC,
oidcAppType: OIDCAppType.OIDC_APP_TYPE_NATIVE,
prefix: 'N',
background: '#595d80',
export const NATIVE_TYPE: RadioItemAppType = {
// key: AppType.NATIVE,
titleI18nKey: 'APP.OIDC.SELECTION.APPTYPE.NATIVE.TITLE',
descI18nKey: 'APP.OIDC.SELECTION.APPTYPE.NATIVE.DESCRIPTION',
createType: AppCreateType.OIDC,
oidcAppType: OIDCAppType.OIDC_APP_TYPE_NATIVE,
prefix: 'N',
background: 'linear-gradient(40deg, #306ccc 30%, #4f46e5)',
};
export const API_TYPE = {
// key: AppType.API,
titleI18nKey: 'APP.API.SELECTION.TITLE',
descI18nKey: 'APP.API.SELECTION.DESCRIPTION',
createType: AppCreateType.API,
prefix: 'API',
background: 'rgb(73,73,73)',
export const API_TYPE: RadioItemAppType = {
// key: AppType.API,
titleI18nKey: 'APP.API.SELECTION.TITLE',
descI18nKey: 'APP.API.SELECTION.DESCRIPTION',
createType: AppCreateType.API,
prefix: 'API',
background: 'linear-gradient(40deg, #1f2937, #111827)',
};
@@ -1,4 +1,4 @@
<form class="form" (ngSubmit)="add(redInput)">
<form class="form" (ngSubmit)="add(redInput)" [attr.data-e2e]="'redirect-uris'">
<cnsl-form-field class="formfield">
<cnsl-label>{{ title }}</cnsl-label>
@@ -9,12 +9,12 @@
button {
margin-bottom: 14px;
margin-right: -.5rem;
margin-right: -0.5rem;
}
}
.uri-list {
margin: 0 .5rem;
margin: 0 0.5rem;
width: 100%;
.uri-line {
@@ -24,6 +24,7 @@
.uri {
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
font-size: 14px;
overflow-x: auto;
}
.fill-space {
@@ -58,5 +59,5 @@
.error {
font-size: 13px;
color: var(--warn);
margin: 0 .5rem 1.5rem .5rem;
margin: 0 0.5rem 1.5rem 0.5rem;
}
@@ -1,56 +1,56 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Observable, Subscription } from 'rxjs';
@Component({
selector: 'cnsl-redirect-uris',
templateUrl: './redirect-uris.component.html',
styleUrls: ['./redirect-uris.component.scss'],
selector: 'cnsl-redirect-uris',
templateUrl: './redirect-uris.component.html',
styleUrls: ['./redirect-uris.component.scss'],
})
export class RedirectUrisComponent implements OnInit, OnDestroy {
@Input() title: string = '';
@Input() devMode: boolean = false;
@Input() canWrite: boolean = false;
@Input() isNative!: boolean;
@Input() public urisList: string[] = [];
@Input() public redirectControl: FormControl = new FormControl({ value: '', disabled: true });
@Input() public changedUris: EventEmitter<string[]> = new EventEmitter();
@Input() public getValues: Observable<void> = new Observable();
@Input() title: string = '';
@Input() devMode: boolean = false;
@Input() canWrite: boolean = false;
@Input() isNative!: boolean;
@Input() public urisList: string[] = [];
@Input() public redirectControl: FormControl = new FormControl({ value: '', disabled: true });
@Output() public changedUris: EventEmitter<string[]> = new EventEmitter();
@Input() public getValues: Observable<void> = new Observable();
@ViewChild('redInput') input!: any;
private sub: Subscription = new Subscription();
constructor() { }
@ViewChild('redInput') input!: any;
private sub: Subscription = new Subscription();
constructor() {}
ngOnInit(): void {
if (this.canWrite) {
this.redirectControl.enable();
}
this.sub = this.getValues.subscribe(() => {
this.add(this.input.nativeElement);
});
ngOnInit(): void {
if (this.canWrite) {
this.redirectControl.enable();
}
ngOnDestroy(): void {
this.sub.unsubscribe();
}
this.sub = this.getValues.subscribe(() => {
this.add(this.input.nativeElement);
});
}
public add(input: any): void {
if (this.redirectControl.valid) {
if (input.value !== '' && input.value !== ' ' && input.value !== '/') {
this.urisList.push(input.value);
}
if (input) {
input.value = '';
}
}
}
ngOnDestroy(): void {
this.sub.unsubscribe();
}
public remove(redirect: any): void {
const index = this.urisList.indexOf(redirect);
if (index >= 0) {
this.urisList.splice(index, 1);
}
public add(input: any): void {
if (this.redirectControl.valid) {
if (input.value !== '' && input.value !== ' ' && input.value !== '/') {
this.urisList.push(input.value);
}
if (input) {
input.value = '';
}
}
}
public remove(redirect: any): void {
const index = this.urisList.indexOf(redirect);
if (index >= 0) {
this.urisList.splice(index, 1);
}
}
}