mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-03 12:52:16 +00:00
* fix: remove access interceptor for console * feat: show dialog on exhausted requests * fix exhausted cookie handling * fix quota exhausted screen * read instance mgmt from environment.json * fix interceptors * don't check cookie on environment.json * fix environment.json path * exclude environment.json from cookie check * remove cookie before loading env * use UTC to delete the cookie * delete cookie before fetching env * simplify cookie handling * lint * review cleanup * use exhausted property from env json * fix bootstrapping * lint * always open mgmt link if present * chore: fetch env json before ng serve * wait for cookie to be removed * fix typo * don't wait for cookie to be set
478 lines
20 KiB
HTML
478 lines
20 KiB
HTML
<cnsl-top-view
|
|
title="{{ app?.name }}"
|
|
[hasActions]="isZitadel === false && (['project.app.write:' + projectId, 'project.app.write'] | hasRole | async)"
|
|
docLink="https://zitadel.com/docs/guides/manage/console/projects"
|
|
[sub]="app?.oidcConfig ? ('APP.OIDC.APPTYPE.' + app?.oidcConfig?.appType | translate) : app?.apiConfig ? 'API' : 'SAML'"
|
|
[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>
|
|
|
|
<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>
|
|
|
|
<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>
|
|
|
|
<cnsl-info-row topContent *ngIf="app" [app]="app"></cnsl-info-row>
|
|
</cnsl-top-view>
|
|
|
|
<div class="max-width-container">
|
|
<div
|
|
class="compliance"
|
|
*ngIf="app && app.oidcConfig && app.oidcConfig.complianceProblemsList && app.oidcConfig.complianceProblemsList?.length"
|
|
>
|
|
<h2 class="compliance-title">{{ 'APP.COMPLIANCE' | translate }}</h2>
|
|
<cnsl-info-section class="problem" [type]="InfoSectionType.ALERT">
|
|
<ul style="margin: 0">
|
|
<li style="margin: 0 0 0.5rem 0" *ngFor="let problem of app.oidcConfig?.complianceProblemsList || []">
|
|
{{ problem.localizedMessage }}
|
|
</li>
|
|
</ul>
|
|
</cnsl-info-section>
|
|
</div>
|
|
|
|
<div class="app-main-content">
|
|
<cnsl-meta-layout>
|
|
<cnsl-sidenav [(ngModel)]="currentSetting" [settingsList]="settingsList">
|
|
<ng-container *ngIf="currentSetting === 'configuration'">
|
|
<cnsl-card *ngIf="oidcForm && app?.oidcConfig" title=" {{ 'APP.OIDC.TITLE' | translate }}">
|
|
<div
|
|
class="current-auth-method"
|
|
*ngIf="currentRadioItemAuthType"
|
|
[ngStyle]="{ background: currentRadioItemAuthType.background }"
|
|
>
|
|
<h3>{{ currentRadioItemAuthType.titleI18nKey | translate }}</h3>
|
|
<p>{{ currentRadioItemAuthType.descI18nKey | translate }}</p>
|
|
<span class="fill-space"></span>
|
|
<button mat-icon-button (click)="changeAuthMethod()"><i class="las la-pen"></i></button>
|
|
</div>
|
|
|
|
<form [formGroup]="oidcForm" (ngSubmit)="saveOIDCApp()">
|
|
<div class="app-oidc-content">
|
|
<div class="app-oidc-grid">
|
|
<cnsl-form-field class="app-formfield">
|
|
<cnsl-label>{{ 'APP.OIDC.CLIENTID' | translate }}</cnsl-label>
|
|
<input cnslInput formControlName="clientId" />
|
|
</cnsl-form-field>
|
|
|
|
<cnsl-form-field 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">
|
|
<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 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">
|
|
<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>
|
|
|
|
<div class="btn-container">
|
|
<button
|
|
class="submit-button"
|
|
type="submit"
|
|
color="primary"
|
|
[disabled]="oidcForm.invalid || !canWrite"
|
|
mat-raised-button
|
|
>
|
|
{{ 'ACTIONS.SAVE' | translate }}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</cnsl-card>
|
|
|
|
<cnsl-card *ngIf="samlForm && app?.samlConfig">
|
|
<form [formGroup]="samlForm">
|
|
<cnsl-form-field class="formfield">
|
|
<cnsl-label>{{ 'APP.SAML.URL' | translate }}</cnsl-label>
|
|
<input cnslInput formControlName="metadataUrl" placeholder="https://" />
|
|
</cnsl-form-field>
|
|
</form>
|
|
|
|
<div class="cnsl-saml-config-line">
|
|
<span class="cnsl-app-or cnsl-secondary-text">{{ 'APP.SAML.OR' | translate }}</span>
|
|
|
|
<input
|
|
#xmlFileInput
|
|
style="display: none"
|
|
class="file-input"
|
|
type="file"
|
|
accept="text/xml,application/xml"
|
|
(change)="onDropXML($any($event.target).files)"
|
|
/>
|
|
<button mat-stroked-button (click)="$event.preventDefault(); xmlFileInput.click()">
|
|
{{ 'APP.SAML.XML' | translate }}
|
|
</button>
|
|
</div>
|
|
|
|
<div
|
|
class="saml-xml"
|
|
[ngClass]="{ disabled: !!metadataUrl?.value }"
|
|
*ngIf="decodedBase64 && app && app.samlConfig && !app.samlConfig.metadataUrl"
|
|
>
|
|
<ngx-codemirror
|
|
[(ngModel)]="decodedBase64"
|
|
[disabled]="!!metadataUrl?.value"
|
|
[options]="{
|
|
lineNumbers: true,
|
|
theme: 'material',
|
|
mode: 'application/xml'
|
|
}"
|
|
></ngx-codemirror>
|
|
</div>
|
|
|
|
<div class="btn-container">
|
|
<button
|
|
class="submit-button"
|
|
type="submit"
|
|
color="primary"
|
|
(click)="saveSAMLApp()"
|
|
[disabled]="samlForm.invalid || !canWrite"
|
|
mat-raised-button
|
|
>
|
|
{{ 'ACTIONS.SAVE' | translate }}
|
|
</button>
|
|
</div>
|
|
</cnsl-card>
|
|
</ng-container>
|
|
|
|
<ng-container *ngIf="currentSetting === 'token'">
|
|
<cnsl-card *ngIf="oidcTokenForm && app?.oidcConfig" title=" {{ 'APP.OIDC.TOKENSECTIONTITLE' | translate }}">
|
|
<form [formGroup]="oidcTokenForm" (ngSubmit)="saveOIDCApp()">
|
|
<cnsl-form-field 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"
|
|
style="margin-top: 1.5rem"
|
|
formControlName="accessTokenRoleAssertion"
|
|
color="primary"
|
|
>
|
|
{{ 'APP.OIDC.ACCESSTOKENROLEASSERTION' | translate }}</mat-checkbox
|
|
>
|
|
|
|
<cnsl-info-section class="full-width app-desc">
|
|
<span>{{ 'APP.OIDC.ACCESSTOKENROLEASSERTION_DESCRIPTION' | translate }}</span>
|
|
</cnsl-info-section>
|
|
|
|
<mat-checkbox
|
|
class="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="full-width"
|
|
style="margin-top: 1.5rem"
|
|
formControlName="idTokenUserinfoAssertion"
|
|
color="primary"
|
|
>
|
|
{{ 'APP.OIDC.IDTOKENUSERINFOASSERTION' | translate }}</mat-checkbox
|
|
>
|
|
<cnsl-info-section class="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="full-width app-desc">
|
|
<span>{{ 'APP.OIDC.CLOCKSKEW' | translate }}</span>
|
|
</cnsl-info-section>
|
|
|
|
<div class="btn-container">
|
|
<button
|
|
class="submit-button"
|
|
type="submit"
|
|
color="primary"
|
|
[disabled]="oidcTokenForm.invalid || !canWrite"
|
|
mat-raised-button
|
|
>
|
|
{{ 'ACTIONS.SAVE' | translate }}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</cnsl-card>
|
|
</ng-container>
|
|
|
|
<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>
|
|
|
|
<mat-checkbox
|
|
*ngIf="skipNativeAppSuccessPage && appType?.value === OIDCAppType.OIDC_APP_TYPE_NATIVE"
|
|
class="full-width"
|
|
style="margin-top: 1.5rem"
|
|
[formControl]="skipNativeAppSuccessPage"
|
|
color="primary"
|
|
>
|
|
{{ 'APP.OIDC.SKIPNATIVEAPPSUCCESSPAGE' | translate }}</mat-checkbox
|
|
>
|
|
<cnsl-info-section *ngIf="appType?.value === OIDCAppType.OIDC_APP_TYPE_NATIVE" class="full-width app-desc">
|
|
<span>{{ 'APP.OIDC.SKIPNATIVEAPPSUCCESSPAGE_DESCRIPTION' | translate }}</span>
|
|
</cnsl-info-section>
|
|
|
|
<cnsl-redirect-uris
|
|
*ngIf="appType?.value !== undefined"
|
|
class="redirect-section"
|
|
[disabled]="!canWrite"
|
|
[devMode]="!!devMode?.value"
|
|
[(ngModel)]="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"
|
|
[disabled]="!canWrite"
|
|
[devMode]="!!devMode?.value"
|
|
[(ngModel)]="postLogoutRedirectUrisList"
|
|
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 }}" *ngIf="environmentMap$ | async as environmentMap">
|
|
<cnsl-info-section *ngIf="environmentMap.issuer">
|
|
<div
|
|
[innerHtml]="
|
|
'APP.OIDC.WELLKNOWN' | translate : { url: environmentMap['issuer'] + '/.well-known/openid-configuration' }
|
|
"
|
|
></div>
|
|
</cnsl-info-section>
|
|
|
|
<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>
|
|
|
|
<div class="app-info-row">
|
|
<div class="app-info-wrapper" *ngFor="let wellKnownV of wellKnownMap$ | async | keyvalue">
|
|
<p class="app-info-row-title cnsl-secondary-text">{{ wellKnownV.key }}</p>
|
|
<div class="app-copy-row">
|
|
<div *ngIf="wellKnownV.value" class="environment">
|
|
<button
|
|
[disabled]="copied === wellKnownV.value"
|
|
[matTooltip]="(copied !== wellKnownV.value ? 'ACTIONS.COPY' : 'ACTIONS.COPIED') | translate"
|
|
cnslCopyToClipboard
|
|
[valueToCopy]="wellKnownV.value"
|
|
(copiedValue)="copied = wellKnownV.key"
|
|
>
|
|
{{ wellKnownV.value }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</cnsl-card>
|
|
</ng-container>
|
|
|
|
<ng-container *ngIf="currentSetting === 'configuration'">
|
|
<cnsl-card
|
|
*ngIf="initialAuthMethod === 'PK_JWT' && projectId && app && 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>
|
|
</cnsl-card>
|
|
</ng-container>
|
|
</cnsl-sidenav>
|
|
|
|
<div metainfo>
|
|
<cnsl-changes *ngIf="app" [changeType]="ChangeType.APP" [id]="app.id" [secId]="projectId"></cnsl-changes>
|
|
</div>
|
|
</cnsl-meta-layout>
|
|
</div>
|
|
</div>
|