mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-14 09:55:12 +00:00
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:
co-authored by
Livio Amstutz
parent
00f7dbe875
commit
08ae39ae19
+4
-3
@@ -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;
|
||||
}
|
||||
|
||||
+3
-3
@@ -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 {
|
||||
|
||||
+25
-25
@@ -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>
|
||||
+4
-5
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user