mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-18 05:52:51 +00:00
fix(console): support Zitadel Support IDP (#12619)
# Which Problems Are Solved
The backend and the new login already support the dedicated ZITADEL IdP
template
(`AddZitadelProvider`/`UpdateZitadelProvider`, `PROVIDER_TYPE_ZITADEL`,
`InstanceRolesInfo`),
but the Console does not expose it:
- A ZITADEL provider cannot be created or edited from the Console,
neither at instance nor at
organization level — it is missing from the "add provider" list in the
IdP settings.
- Providers created through the API are listed in the IdP table as
`coming soon` (the type
`@switch` has no `PROVIDER_TYPE_ZITADEL` case) and the row has no detail
route, so an existing
provider cannot be opened.
- `InstanceRolesInfo` — the organization ID/domain pairs that drive
instance-role synchronization
for instance-level providers — is not configurable outside the API.
# How the Problems Are Solved
- Added `ProviderZitadelComponent`
(`console/src/app/modules/providers/provider-zitadel/`),
modeled on the generic OIDC provider component:
- Form for `name`, `issuer`, `clientId`, `clientSecret` and `scopesList`
(chip input, defaulting
to `openid`, `profile`, `email`), plus the shared
`cnsl-provider-options` block.
- On edit the client secret is optional and is only sent when the
"update client secret"
checkbox is set.
- Reuses `cnsl-provider-next` for the callback URL, activation and "what
now" flow.
- Added the instance role mapping section, rendered only for
instance-level providers
(`serviceType === ADMIN`), since the API rejects `instance_roles_info`
on organization-scoped
ZITADEL providers. Entries are added as organization ID + organization
domain pairs and are
submitted as `InstanceRolesInfo`; both values are required and are
matched together on login.
- Added `addZitadelProvider`/`updateZitadelProvider` to `AdminService`
and `ManagementService`
wrapping the existing gRPC calls.
- Declared the component in `providers.module.ts` and mapped
`PROVIDER_TYPE_ZITADEL` to the
`zitadel` path in `providers-routing.module.ts`, so
`/instance/provider/zitadel/...` and
`/org/provider/zitadel/...` resolve for both create and edit.
- Added a ZITADEL tile to the "add provider" list in the IdP settings
and a
`PROVIDER_TYPE_ZITADEL` case to the IdP table;
`IdpTableComponent.routerLinkForRow` now returns
the detail route for ZITADEL rows.
# Additional Changes
- Added the new i18n keys `IDP.CREATE.ZITADEL.TITLE`/`DESCRIPTION`,
`IDP.INSTANCEROLESINFO`,
`IDP.INSTANCEROLESINFO_DESC`, `IDP.ORGANIZATIONID` and
`IDP.ORGANIZATIONDOMAIN` to all 22
Console locale files with actual translations instead of English
fallbacks.
- The provider tile, table entry and detail header use the colored
ZITADEL logo assets
(`zitadel-logo-solo-light.svg` in the dark theme,
`zitadel-logo-solo-dark.svg` in the light
theme), consistent with the header, rather than the monochrome
`*-solo-*-icon.svg` variants.
- Additional sync roles trigger for the support IDP to behave correctly
on first login after a disable/enable cycle — where auto-linking jumps
in — but not required for the link to be established.
# Additional Context
- Closes #11825
- Part of the "Zitadel as an Identity Provider" epic #5127
- Console counterpart to the API work in #12018, #12056 (add), #12371,
#12378, #12384 (update),
#12394 (get/list), #12396 (delete), #12422 (active IdPs) and to the
login support in #12469,
#12530 and #12568
---------
Co-authored-by: Livio Spring <[email protected]>
This commit is contained in:
co-authored by
Livio Spring
parent
a1a4572303
commit
324cc54d9a
@@ -556,6 +556,12 @@ async function handleAutoLinking(ctx: IDPHandlerContext): Promise<IDPHandlerResu
|
||||
});
|
||||
logger.info("User auto-linked successfully, creating session");
|
||||
|
||||
// Synchronize instance member roles for ZITADEL IdPs with instanceRolesInfo
|
||||
// configured (e.g. support access). Merge-only and never blocks the login.
|
||||
// Required here as well: a user that is auto-linked on this login would
|
||||
// otherwise only receive its roles on the next one (via the existing-user path).
|
||||
await syncInstanceRolesFromIdpIntent({ serviceConfig, intent, userId: foundUser.userId });
|
||||
|
||||
// Create session after auto-linking
|
||||
const sessionResult = await createNewSessionFromIdpIntent({
|
||||
userId: foundUser.userId,
|
||||
|
||||
@@ -59,6 +59,13 @@
|
||||
Generic OIDC
|
||||
</div>
|
||||
}
|
||||
@case (ProviderType.PROVIDER_TYPE_ZITADEL) {
|
||||
<div class="idp-table-provider-type">
|
||||
<img class="idp-logo dark" src="./assets/images/zitadel-logo-solo-light.svg" alt="zitadel" />
|
||||
<img class="idp-logo light" src="./assets/images/zitadel-logo-solo-dark.svg" alt="zitadel" />
|
||||
ZITADEL
|
||||
</div>
|
||||
}
|
||||
@case (ProviderType.PROVIDER_TYPE_OAUTH) {
|
||||
<div class="idp-table-provider-type">
|
||||
<img class="idp-logo" src="./assets/images/idp/oauth.svg" alt="oauth" />
|
||||
|
||||
@@ -266,6 +266,8 @@ export class IdpTableComponent implements OnInit, OnDestroy {
|
||||
return [row.owner === IDPOwnerType.IDP_OWNER_TYPE_SYSTEM ? '/instance' : '/org', 'provider', 'apple', row.id];
|
||||
case ProviderType.PROVIDER_TYPE_SAML:
|
||||
return [row.owner === IDPOwnerType.IDP_OWNER_TYPE_SYSTEM ? '/instance' : '/org', 'provider', 'saml', row.id];
|
||||
case ProviderType.PROVIDER_TYPE_ZITADEL:
|
||||
return [row.owner === IDPOwnerType.IDP_OWNER_TYPE_SYSTEM ? '/instance' : '/org', 'provider', 'zitadel', row.id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,23 @@
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a
|
||||
class="item card"
|
||||
[routerLink]="
|
||||
serviceType === PolicyComponentServiceType.ADMIN
|
||||
? ['/instance', 'provider', 'zitadel', 'create']
|
||||
: serviceType === PolicyComponentServiceType.MGMT
|
||||
? ['/org', 'provider', 'zitadel', 'create']
|
||||
: []
|
||||
"
|
||||
>
|
||||
<img class="idp-logo dark" src="./assets/images/zitadel-logo-solo-light.svg" alt="ZITADEL" />
|
||||
<img class="idp-logo light" src="./assets/images/zitadel-logo-solo-dark.svg" alt="ZITADEL" />
|
||||
<div class="text-container">
|
||||
<span class="title">ZITADEL</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a
|
||||
class="item card"
|
||||
[routerLink]="
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
<cnsl-create-layout
|
||||
title="{{ id ? ('IDP.DETAIL.TITLE' | translate) : ('IDP.CREATE.TITLE' | translate) }}"
|
||||
(closed)="close()"
|
||||
>
|
||||
<div class="identity-provider-create-content">
|
||||
<div class="identity-provider-title-row">
|
||||
<img class="idp-logo dark" src="./assets/images/zitadel-logo-solo-light.svg" alt="zitadel" />
|
||||
<img class="idp-logo light" src="./assets/images/zitadel-logo-solo-dark.svg" alt="zitadel" />
|
||||
<h1>{{ 'IDP.CREATE.ZITADEL.TITLE' | translate }}</h1>
|
||||
@if (exists$ | async) {
|
||||
@if ({ isNotActive: (activateLink$ | async) }; as idp) {
|
||||
<div
|
||||
class="cnsl-state-dot"
|
||||
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
|
||||
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
|
||||
></div>
|
||||
}
|
||||
}
|
||||
@if (loading) {
|
||||
<mat-spinner diameter="25" color="primary"></mat-spinner>
|
||||
}
|
||||
</div>
|
||||
|
||||
<cnsl-provider-next
|
||||
[configureProvider]="(justCreated$ | async) === ''"
|
||||
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.TITLE' | translate: { provider: 'ZITADEL' }"
|
||||
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.DESCRIPTION' | translate: { provider: 'ZITADEL' }"
|
||||
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers"
|
||||
[autofillLink]="autofillLink$ | async"
|
||||
[activateLink]="activateLink$ | async"
|
||||
[copyUrls]="copyUrls$ | async"
|
||||
[expanded]="!!(expandWhatNow$ | async)"
|
||||
(activate)="activate()"
|
||||
></cnsl-provider-next>
|
||||
|
||||
<p class="identity-provider-desc cnsl-secondary-text">{{ 'IDP.CREATE.ZITADEL.DESCRIPTION' | translate }}</p>
|
||||
|
||||
<form [formGroup]="form" (ngSubmit)="submitForm()">
|
||||
<div class="identity-provider-content">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'IDP.NAME' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="name" />
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'IDP.ISSUER' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="issuer" />
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
|
||||
<div class="identity-provider-content">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'IDP.CLIENTID' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="clientId" />
|
||||
</cnsl-form-field>
|
||||
|
||||
@if (provider) {
|
||||
<mat-checkbox [(ngModel)]="updateClientSecret" [ngModelOptions]="{ standalone: true }">{{
|
||||
'IDP.UPDATECLIENTSECRET' | translate
|
||||
}}</mat-checkbox>
|
||||
}
|
||||
@if (!provider || (provider && updateClientSecret)) {
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'IDP.CLIENTSECRET' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="clientSecret" />
|
||||
</cnsl-form-field>
|
||||
}
|
||||
|
||||
@if (isAdmin) {
|
||||
<div class="instance-roles-info">
|
||||
<h2>{{ 'IDP.INSTANCEROLESINFO' | translate }}</h2>
|
||||
<p class="checkbox-desc cnsl-secondary-text">{{ 'IDP.INSTANCEROLESINFO_DESC' | translate }}</p>
|
||||
@for (info of instanceRolesInfoList; track $index) {
|
||||
<div class="flex-line">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'IDP.ORGANIZATIONID' | translate }}</cnsl-label>
|
||||
<input cnslInput [value]="info.organizationId" disabled />
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'IDP.ORGANIZATIONDOMAIN' | translate }}</cnsl-label>
|
||||
<input cnslInput [value]="info.organizationDomain" disabled />
|
||||
</cnsl-form-field>
|
||||
<button type="button" mat-icon-button (click)="removeRolesInfo($index)">
|
||||
<mat-icon>remove_circle_outline</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
<div class="flex-line">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'IDP.ORGANIZATIONID' | translate }}</cnsl-label>
|
||||
<input cnslInput [formControl]="rolesInfoOrgId" />
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'IDP.ORGANIZATIONDOMAIN' | translate }}</cnsl-label>
|
||||
<input cnslInput [formControl]="rolesInfoOrgDomain" />
|
||||
</cnsl-form-field>
|
||||
<button
|
||||
type="button"
|
||||
mat-icon-button
|
||||
[disabled]="!rolesInfoOrgId.value || !rolesInfoOrgDomain.value"
|
||||
(click)="addRolesInfo()"
|
||||
>
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="identity-provider-optional-h-wrapper">
|
||||
<h2>{{ 'IDP.OPTIONAL' | translate }}</h2>
|
||||
<button (click)="showOptional = !showOptional" type="button" mat-icon-button>
|
||||
@if (showOptional) {
|
||||
<mat-icon>keyboard_arrow_up</mat-icon>
|
||||
}
|
||||
@if (!showOptional) {
|
||||
<mat-icon>keyboard_arrow_down</mat-icon>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
@if (showOptional) {
|
||||
<div>
|
||||
<div class="idp-scopes">
|
||||
<div class="flex-line">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'IDP.SCOPESLIST' | translate }}</cnsl-label>
|
||||
<input
|
||||
cnslInput
|
||||
#scopeInput
|
||||
[matChipInputFor]="chipScopesList"
|
||||
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||
[matChipInputAddOnBlur]="true"
|
||||
(matChipInputTokenEnd)="addScope($event)"
|
||||
/>
|
||||
</cnsl-form-field>
|
||||
<button class="scope-add-button" type="button" (click)="addScopeFromInput(scopeInput)" mat-icon-button>
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<cnsl-form-field class="formfield">
|
||||
<mat-chip-grid #chipScopesList aria-label="scope selection">
|
||||
@for (scope of scopesList?.value; track scope) {
|
||||
<mat-chip-row class="chip" removable (removed)="removeScope(scope)">
|
||||
{{ scope }} <mat-icon matChipRemove>cancel</mat-icon>
|
||||
</mat-chip-row>
|
||||
}
|
||||
</mat-chip-grid>
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
<cnsl-provider-options
|
||||
[initialOptions]="provider?.config?.options"
|
||||
(optionsChanged)="options = $event"
|
||||
></cnsl-provider-options>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="identity-provider-create-actions">
|
||||
<button
|
||||
color="primary"
|
||||
mat-raised-button
|
||||
class="continue-button"
|
||||
[disabled]="form.invalid || form.disabled"
|
||||
type="submit"
|
||||
>
|
||||
@if ({ exists: (exists$ | async) }; as idp) {
|
||||
<span>{{ (!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate }}</span>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</cnsl-create-layout>
|
||||
@@ -0,0 +1,18 @@
|
||||
.instance-roles-info {
|
||||
max-width: 500px;
|
||||
|
||||
.checkbox-desc {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.flex-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
|
||||
.formfield {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
|
||||
import { Location } from '@angular/common';
|
||||
import { Component, Injector, Type } from '@angular/core';
|
||||
import { AbstractControl, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
||||
import { MatChipInputEvent } from '@angular/material/chips';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { BehaviorSubject, take } from 'rxjs';
|
||||
import {
|
||||
AddZitadelProviderRequest as AdminAddZitadelProviderRequest,
|
||||
GetProviderByIDRequest as AdminGetProviderByIDRequest,
|
||||
UpdateZitadelProviderRequest as AdminUpdateZitadelProviderRequest,
|
||||
} from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import { AutoLinkingOption, InstanceRolesInfo, Options, Provider } from 'src/app/proto/generated/zitadel/idp_pb';
|
||||
import {
|
||||
AddZitadelProviderRequest as MgmtAddZitadelProviderRequest,
|
||||
GetProviderByIDRequest as MgmtGetProviderByIDRequest,
|
||||
UpdateZitadelProviderRequest as MgmtUpdateZitadelProviderRequest,
|
||||
} from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
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';
|
||||
import { requiredValidator } from '../../form-field/validators/validators';
|
||||
|
||||
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
|
||||
import { ProviderNextService } from '../provider-next/provider-next.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-provider-zitadel',
|
||||
templateUrl: './provider-zitadel.component.html',
|
||||
styleUrls: ['./provider-zitadel.component.scss'],
|
||||
standalone: false,
|
||||
})
|
||||
export class ProviderZitadelComponent {
|
||||
public showOptional: boolean = false;
|
||||
public options: Options = new Options()
|
||||
.setIsCreationAllowed(true)
|
||||
.setIsLinkingAllowed(true)
|
||||
.setAutoLinking(AutoLinkingOption.AUTO_LINKING_OPTION_UNSPECIFIED);
|
||||
|
||||
// DEPRECATED: use id$ instead
|
||||
public id: string | null = '';
|
||||
public updateClientSecret: boolean = false;
|
||||
// DEPRECATED: assert service$ instead
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
// DEPRECATED: use service$ instead
|
||||
private service!: ManagementService | AdminService;
|
||||
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
|
||||
public form!: UntypedFormGroup;
|
||||
|
||||
public loading: boolean = false;
|
||||
|
||||
public provider?: Provider.AsObject;
|
||||
|
||||
// Instance role mapping (instance-level providers only): organizations of the
|
||||
// remote ZITADEL instance whose project-role grants with an IAM_ prefix are
|
||||
// applied as instance memberships after login.
|
||||
public instanceRolesInfoList: InstanceRolesInfo.AsObject[] = [];
|
||||
public rolesInfoOrgId: UntypedFormControl = new UntypedFormControl('');
|
||||
public rolesInfoOrgDomain: UntypedFormControl = new UntypedFormControl('');
|
||||
|
||||
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
|
||||
public justActivated$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
private service$ = this.nextSvc.service(this.route.data);
|
||||
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
|
||||
public exists$ = this.nextSvc.exists(this.id$);
|
||||
public autofillLink$ = this.nextSvc.autofillLink(
|
||||
this.id$,
|
||||
`https://zitadel.com/docs/guides/integrate/identity-providers/additional-information`,
|
||||
);
|
||||
public activateLink$ = this.nextSvc.activateLink(
|
||||
this.id$,
|
||||
this.justActivated$,
|
||||
'https://zitadel.com/docs/guides/integrate/identity-providers',
|
||||
this.service$,
|
||||
);
|
||||
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
|
||||
public copyUrls$ = this.nextSvc.callbackUrls();
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private toast: ToastService,
|
||||
private injector: Injector,
|
||||
private _location: Location,
|
||||
breadcrumbService: BreadcrumbService,
|
||||
private nextSvc: ProviderNextService,
|
||||
) {
|
||||
this.form = new UntypedFormGroup({
|
||||
name: new UntypedFormControl('', [requiredValidator]),
|
||||
clientId: new UntypedFormControl('', [requiredValidator]),
|
||||
clientSecret: new UntypedFormControl('', [requiredValidator]),
|
||||
issuer: new UntypedFormControl('', [requiredValidator]),
|
||||
scopesList: new UntypedFormControl(['openid', 'profile', 'email'], []),
|
||||
});
|
||||
|
||||
this.route.data.pipe(take(1)).subscribe((data) => {
|
||||
this.serviceType = data['serviceType'];
|
||||
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
this.service = this.injector.get(ManagementService as Type<ManagementService>);
|
||||
|
||||
const bread: Breadcrumb = {
|
||||
type: BreadcrumbType.ORG,
|
||||
routerLink: ['/org'],
|
||||
};
|
||||
|
||||
breadcrumbService.setBreadcrumb([bread]);
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
this.service = this.injector.get(AdminService as Type<AdminService>);
|
||||
|
||||
const iamBread = new Breadcrumb({
|
||||
type: BreadcrumbType.ORG,
|
||||
name: 'Instance',
|
||||
routerLink: ['/instance'],
|
||||
});
|
||||
breadcrumbService.setBreadcrumb([iamBread]);
|
||||
break;
|
||||
}
|
||||
|
||||
this.id = this.route.snapshot.paramMap.get('id');
|
||||
if (this.id) {
|
||||
this.clientSecret?.setValidators([]);
|
||||
this.getData(this.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// The role mapping is only available on instance-level providers: the API
|
||||
// rejects organization-scoped ZITADEL providers carrying instanceRolesInfo.
|
||||
public get isAdmin(): boolean {
|
||||
return this.serviceType === PolicyComponentServiceType.ADMIN;
|
||||
}
|
||||
|
||||
public activate() {
|
||||
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
|
||||
}
|
||||
|
||||
private getData(id: string): void {
|
||||
this.loading = true;
|
||||
const req =
|
||||
this.serviceType === PolicyComponentServiceType.ADMIN
|
||||
? new AdminGetProviderByIDRequest()
|
||||
: new MgmtGetProviderByIDRequest();
|
||||
req.setId(id);
|
||||
this.service
|
||||
.getProviderByID(req)
|
||||
.then((resp) => {
|
||||
this.provider = resp.idp;
|
||||
this.loading = false;
|
||||
if (this.provider?.config?.zitadel) {
|
||||
this.form.patchValue(this.provider.config.zitadel);
|
||||
this.name?.setValue(this.provider.name);
|
||||
this.instanceRolesInfoList = this.provider.config.zitadel.instanceRolesInfoList ?? [];
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
public submitForm(): void {
|
||||
this.provider || this.justCreated$.value ? this.updateZitadelProvider() : this.addZitadelProvider();
|
||||
}
|
||||
|
||||
public addZitadelProvider(): void {
|
||||
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
const req = new MgmtAddZitadelProviderRequest();
|
||||
req.setName(this.name?.value);
|
||||
req.setClientId(this.clientId?.value);
|
||||
req.setClientSecret(this.clientSecret?.value);
|
||||
req.setIssuer(this.issuer?.value);
|
||||
req.setScopesList(this.scopesList?.value);
|
||||
req.setProviderOptions(this.options);
|
||||
|
||||
this.loading = true;
|
||||
(this.service as ManagementService)
|
||||
.addZitadelProvider(req)
|
||||
.then((addedIDP) => {
|
||||
this.justCreated$.next(addedIDP.id);
|
||||
this.loading = false;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
this.loading = false;
|
||||
});
|
||||
} else {
|
||||
const req = new AdminAddZitadelProviderRequest();
|
||||
req.setName(this.name?.value);
|
||||
req.setClientId(this.clientId?.value);
|
||||
req.setClientSecret(this.clientSecret?.value);
|
||||
req.setIssuer(this.issuer?.value);
|
||||
req.setScopesList(this.scopesList?.value);
|
||||
req.setProviderOptions(this.options);
|
||||
req.setInstanceRolesInfoList(this.instanceRolesInfoToProto());
|
||||
|
||||
this.loading = true;
|
||||
(this.service as AdminService)
|
||||
.addZitadelProvider(req)
|
||||
.then((addedIDP) => {
|
||||
this.justCreated$.next(addedIDP.id);
|
||||
this.loading = false;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public updateZitadelProvider(): void {
|
||||
if (this.provider || this.justCreated$.value) {
|
||||
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
const req = new MgmtUpdateZitadelProviderRequest();
|
||||
req.setId(this.provider?.id || this.justCreated$.value);
|
||||
req.setName(this.name?.value);
|
||||
req.setClientId(this.clientId?.value);
|
||||
req.setClientSecret(this.clientSecretForUpdate);
|
||||
req.setIssuer(this.issuer?.value);
|
||||
req.setScopesList(this.scopesList?.value);
|
||||
req.setProviderOptions(this.options);
|
||||
|
||||
this.loading = true;
|
||||
(this.service as ManagementService)
|
||||
.updateZitadelProvider(req)
|
||||
.then(() => {
|
||||
setTimeout(() => {
|
||||
this.loading = false;
|
||||
this.close();
|
||||
}, 2000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
this.loading = false;
|
||||
});
|
||||
} else {
|
||||
const req = new AdminUpdateZitadelProviderRequest();
|
||||
req.setId(this.provider?.id || this.justCreated$.value);
|
||||
req.setName(this.name?.value);
|
||||
req.setClientId(this.clientId?.value);
|
||||
req.setClientSecret(this.clientSecretForUpdate);
|
||||
req.setIssuer(this.issuer?.value);
|
||||
req.setScopesList(this.scopesList?.value);
|
||||
req.setProviderOptions(this.options);
|
||||
req.setInstanceRolesInfoList(this.instanceRolesInfoToProto());
|
||||
|
||||
this.loading = true;
|
||||
(this.service as AdminService)
|
||||
.updateZitadelProvider(req)
|
||||
.then(() => {
|
||||
setTimeout(() => {
|
||||
this.loading = false;
|
||||
this.close();
|
||||
}, 2000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The client secret to send on update. An existing provider only receives a
|
||||
* new secret when the user explicitly opted in: the form control keeps its
|
||||
* value when the "update client secret" checkbox is unticked again, and
|
||||
* sending it would silently rotate the secret. An empty value is treated as
|
||||
* "unchanged" by the API.
|
||||
*/
|
||||
private get clientSecretForUpdate(): string {
|
||||
return !this.provider || this.updateClientSecret ? (this.clientSecret?.value ?? '') : '';
|
||||
}
|
||||
|
||||
private instanceRolesInfoToProto(): InstanceRolesInfo[] {
|
||||
return this.instanceRolesInfoList.map((info) =>
|
||||
new InstanceRolesInfo().setOrganizationId(info.organizationId).setOrganizationDomain(info.organizationDomain),
|
||||
);
|
||||
}
|
||||
|
||||
public addRolesInfo(): void {
|
||||
const organizationId = (this.rolesInfoOrgId.value ?? '').trim();
|
||||
const organizationDomain = (this.rolesInfoOrgDomain.value ?? '').trim();
|
||||
// Both fields are required by the API and matched together on login.
|
||||
if (!organizationId || !organizationDomain) {
|
||||
return;
|
||||
}
|
||||
this.instanceRolesInfoList = [...this.instanceRolesInfoList, { organizationId, organizationDomain }];
|
||||
this.rolesInfoOrgId.setValue('');
|
||||
this.rolesInfoOrgDomain.setValue('');
|
||||
}
|
||||
|
||||
public removeRolesInfo(index: number): void {
|
||||
this.instanceRolesInfoList = this.instanceRolesInfoList.filter((_, i) => i !== index);
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
this._location.back();
|
||||
}
|
||||
|
||||
public addScope(event: MatChipInputEvent): void {
|
||||
this.addScopeFromInput(event.chipInput?.inputElement, event.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the scope currently typed into the chip input. Takes the value from
|
||||
* the input element because the add button emits a MouseEvent, which carries
|
||||
* no value of its own.
|
||||
*/
|
||||
public addScopeFromInput(input?: HTMLInputElement, value?: string): void {
|
||||
const scope = (value ?? input?.value ?? '').trim();
|
||||
|
||||
if (scope !== '' && this.scopesList?.value) {
|
||||
this.scopesList.value.push(scope);
|
||||
if (input) {
|
||||
input.value = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public removeScope(uri: string): void {
|
||||
if (this.scopesList?.value) {
|
||||
const index = this.scopesList.value.indexOf(uri);
|
||||
|
||||
if (index !== undefined && index >= 0) {
|
||||
this.scopesList.value.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public get name(): AbstractControl | null {
|
||||
return this.form.get('name');
|
||||
}
|
||||
|
||||
public get clientId(): AbstractControl | null {
|
||||
return this.form.get('clientId');
|
||||
}
|
||||
|
||||
public get clientSecret(): AbstractControl | null {
|
||||
return this.form.get('clientSecret');
|
||||
}
|
||||
|
||||
public get issuer(): AbstractControl | null {
|
||||
return this.form.get('issuer');
|
||||
}
|
||||
|
||||
public get scopesList(): AbstractControl | null {
|
||||
return this.form.get('scopesList');
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import { ProviderLDAPComponent } from './provider-ldap/provider-ldap.component';
|
||||
import { ProviderOAuthComponent } from './provider-oauth/provider-oauth.component';
|
||||
import { ProviderOIDCComponent } from './provider-oidc/provider-oidc.component';
|
||||
import { ProviderSamlSpComponent } from './provider-saml-sp/provider-saml-sp.component';
|
||||
import { ProviderZitadelComponent } from './provider-zitadel/provider-zitadel.component';
|
||||
|
||||
const typeMap = {
|
||||
[ProviderType.PROVIDER_TYPE_AZURE_AD]: { path: 'azure-ad', component: ProviderAzureADComponent },
|
||||
@@ -31,6 +32,7 @@ const typeMap = {
|
||||
[ProviderType.PROVIDER_TYPE_LDAP]: { path: 'ldap', component: ProviderLDAPComponent },
|
||||
[ProviderType.PROVIDER_TYPE_APPLE]: { path: 'apple', component: ProviderAppleComponent },
|
||||
[ProviderType.PROVIDER_TYPE_SAML]: { path: 'saml', component: ProviderSamlSpComponent },
|
||||
[ProviderType.PROVIDER_TYPE_ZITADEL]: { path: 'zitadel', component: ProviderZitadelComponent },
|
||||
};
|
||||
|
||||
const routes: Routes = Object.values(typeMap).map(({ path, component }) => {
|
||||
|
||||
@@ -30,6 +30,7 @@ import { ProviderOAuthComponent } from './provider-oauth/provider-oauth.componen
|
||||
import { ProviderOIDCComponent } from './provider-oidc/provider-oidc.component';
|
||||
import { ProvidersRoutingModule } from './providers-routing.module';
|
||||
import { ProviderSamlSpComponent } from './provider-saml-sp/provider-saml-sp.component';
|
||||
import { ProviderZitadelComponent } from './provider-zitadel/provider-zitadel.component';
|
||||
import { CopyRowComponent } from '../../components/copy-row/copy-row.component';
|
||||
import { ProviderNextComponent } from './provider-next/provider-next.component';
|
||||
import { ProviderNextService } from './provider-next/provider-next.service';
|
||||
@@ -50,6 +51,7 @@ import { ProviderNextService } from './provider-next/provider-next.service';
|
||||
ProviderLDAPComponent,
|
||||
ProviderAppleComponent,
|
||||
ProviderSamlSpComponent,
|
||||
ProviderZitadelComponent,
|
||||
ProviderNextComponent,
|
||||
],
|
||||
imports: [
|
||||
|
||||
@@ -50,6 +50,8 @@ import {
|
||||
AddSMSProviderTwilioResponse,
|
||||
AddSMTPConfigRequest,
|
||||
AddSMTPConfigResponse,
|
||||
AddZitadelProviderRequest,
|
||||
AddZitadelProviderResponse,
|
||||
DeactivateIDPRequest,
|
||||
DeactivateIDPResponse,
|
||||
DeactivateSMSProviderRequest,
|
||||
@@ -304,6 +306,8 @@ import {
|
||||
UpdateSMTPConfigPasswordResponse,
|
||||
UpdateSMTPConfigRequest,
|
||||
UpdateSMTPConfigResponse,
|
||||
UpdateZitadelProviderRequest,
|
||||
UpdateZitadelProviderResponse,
|
||||
} from '../proto/generated/zitadel/admin_pb';
|
||||
import {
|
||||
ResetCustomDomainClaimedMessageTextToDefaultRequest,
|
||||
@@ -1288,6 +1292,14 @@ export class AdminService {
|
||||
return this.grpcService.admin.updateGenericOIDCProvider(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public addZitadelProvider(req: AddZitadelProviderRequest): Promise<AddZitadelProviderResponse.AsObject> {
|
||||
return this.grpcService.admin.addZitadelProvider(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public updateZitadelProvider(req: UpdateZitadelProviderRequest): Promise<UpdateZitadelProviderResponse.AsObject> {
|
||||
return this.grpcService.admin.updateZitadelProvider(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public addGenericOAuthProvider(req: AddGenericOAuthProviderRequest): Promise<AddGenericOAuthProviderResponse.AsObject> {
|
||||
return this.grpcService.admin.addGenericOAuthProvider(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
@@ -89,6 +89,8 @@ import {
|
||||
AddSecondFactorToLoginPolicyResponse,
|
||||
AddUserGrantRequest,
|
||||
AddUserGrantResponse,
|
||||
AddZitadelProviderRequest,
|
||||
AddZitadelProviderResponse,
|
||||
BulkAddProjectRolesRequest,
|
||||
BulkAddProjectRolesResponse,
|
||||
BulkRemoveUserGrantRequest,
|
||||
@@ -526,6 +528,8 @@ import {
|
||||
UpdateUserGrantResponse,
|
||||
UpdateUserNameRequest,
|
||||
UpdateUserNameResponse,
|
||||
UpdateZitadelProviderRequest,
|
||||
UpdateZitadelProviderResponse,
|
||||
ValidateOrgDomainRequest,
|
||||
ValidateOrgDomainResponse,
|
||||
} from '../proto/generated/zitadel/management_pb';
|
||||
@@ -1051,6 +1055,14 @@ export class ManagementService {
|
||||
return this.grpcService.mgmt.updateGenericOIDCProvider(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public addZitadelProvider(req: AddZitadelProviderRequest): Promise<AddZitadelProviderResponse.AsObject> {
|
||||
return this.grpcService.mgmt.addZitadelProvider(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public updateZitadelProvider(req: UpdateZitadelProviderRequest): Promise<UpdateZitadelProviderResponse.AsObject> {
|
||||
return this.grpcService.mgmt.updateZitadelProvider(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public addGenericOAuthProvider(req: AddGenericOAuthProviderRequest): Promise<AddGenericOAuthProviderResponse.AsObject> {
|
||||
return this.grpcService.mgmt.addGenericOAuthProvider(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
@@ -2195,6 +2195,10 @@
|
||||
"TITLE": "إضافة مزود",
|
||||
"DESCRIPTION": "حدد واحداً أو أكثر من المزودين التاليين.",
|
||||
"STEPPERTITLE": "إنشاء مزود",
|
||||
"ZITADEL": {
|
||||
"TITLE": "ZITADEL",
|
||||
"DESCRIPTION": "أدخل البيانات المطلوبة للسماح لمستخدمي نسخة ZITADEL أخرى بالمصادقة."
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "مزود OIDC",
|
||||
"DESCRIPTION": "أدخل البيانات المطلوبة لمزود OIDC الخاص بك."
|
||||
@@ -2307,6 +2311,10 @@
|
||||
"STATE": "الحالة",
|
||||
"ISSUER": "المصدر",
|
||||
"SCOPESLIST": "قائمة النطاقات",
|
||||
"INSTANCEROLESINFO": "تعيين أدوار المثيل",
|
||||
"INSTANCEROLESINFO_DESC": "يُمنح المستخدمون الذين يصادقون عبر هذا المزود أدوار عضوية المثيل (ببادئة IAM_) التي يملكونها في المؤسسات المدرجة في نسخة ZITADEL البعيدة. يجب أن يتطابق كل من معرّف المؤسسة والنطاق الأساسي.",
|
||||
"ORGANIZATIONID": "معرّف المؤسسة",
|
||||
"ORGANIZATIONDOMAIN": "نطاق المؤسسة",
|
||||
"CLIENTID": "معرف العميل",
|
||||
"CLIENTSECRET": "سر العميل",
|
||||
"LDAPCONNECTION": "الاتصال",
|
||||
|
||||
@@ -2207,6 +2207,10 @@
|
||||
"TITLE": "Добавяне на доставчик",
|
||||
"DESCRIPTION": "Изберете един или повече от следните доставчици.",
|
||||
"STEPPERTITLE": "Създаване на доставчик",
|
||||
"ZITADEL": {
|
||||
"TITLE": "ZITADEL",
|
||||
"DESCRIPTION": "Въведете необходимите данни, за да позволите на потребители от друга ZITADEL инстанция да се удостоверяват."
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "Доставчик на OIDC",
|
||||
"DESCRIPTION": "Въведете необходимите данни за вашия OIDC доставчик."
|
||||
@@ -2319,6 +2323,10 @@
|
||||
"STATE": "Статус",
|
||||
"ISSUER": "Издател",
|
||||
"SCOPESLIST": "Списък с обхвати",
|
||||
"INSTANCEROLESINFO": "Съпоставяне на роли на инстанцията",
|
||||
"INSTANCEROLESINFO_DESC": "Потребителите, които се удостоверяват чрез този доставчик, получават ролите на членове на инстанцията (с префикс IAM_), които притежават в изброените организации на отдалечената ZITADEL инстанция. ID на организацията и основният домейн трябва да съвпадат.",
|
||||
"ORGANIZATIONID": "ID на организацията",
|
||||
"ORGANIZATIONDOMAIN": "Домейн на организацията",
|
||||
"CLIENTID": "ID на клиента",
|
||||
"CLIENTSECRET": "Тайна на клиента",
|
||||
"LDAPCONNECTION": "Връзка",
|
||||
|
||||
@@ -2203,6 +2203,10 @@
|
||||
"TITLE": "Přidat poskytovatele",
|
||||
"DESCRIPTION": "Vyberte jednoho nebo více z následujících poskytovatelů.",
|
||||
"STEPPERTITLE": "Vytvořit poskytovatele",
|
||||
"ZITADEL": {
|
||||
"TITLE": "ZITADEL",
|
||||
"DESCRIPTION": "Zadej požadované údaje, aby se mohli přihlašovat uživatelé jiné instance ZITADEL."
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "OIDC",
|
||||
"DESCRIPTION": "Zadejte požadovaná data pro vašeho OIDC poskytovatele."
|
||||
@@ -2311,6 +2315,10 @@
|
||||
"STATE": "Stav",
|
||||
"ISSUER": "Vystavitel",
|
||||
"SCOPESLIST": "Seznam oprávnění",
|
||||
"INSTANCEROLESINFO": "Mapování rolí instance",
|
||||
"INSTANCEROLESINFO_DESC": "Uživatelé, kteří se přihlásí přes tohoto poskytovatele, získají role členů instance (s prefixem IAM_), které mají v uvedených organizacích vzdálené instance ZITADEL. ID organizace i primární doména se musí shodovat.",
|
||||
"ORGANIZATIONID": "ID organizace",
|
||||
"ORGANIZATIONDOMAIN": "Doména organizace",
|
||||
"CLIENTID": "ID klienta",
|
||||
"CLIENTSECRET": "Tajemství klienta",
|
||||
"LDAPCONNECTION": "Spojení",
|
||||
|
||||
@@ -2206,6 +2206,10 @@
|
||||
"TITLE": "OIDC Provider",
|
||||
"DESCRIPTION": "Geben Sie die erforderlichen Daten für Ihren OIDC-Provider ein."
|
||||
},
|
||||
"ZITADEL": {
|
||||
"TITLE": "ZITADEL",
|
||||
"DESCRIPTION": "Geben Sie die erforderlichen Daten ein, damit sich Benutzer einer anderen ZITADEL-Instanz anmelden können."
|
||||
},
|
||||
"JWT": {
|
||||
"TITLE": "JWT Provider",
|
||||
"DESCRIPTION": "Geben Sie die erforderlichen Daten für Ihren JWT-Provider ein."
|
||||
@@ -2307,6 +2311,10 @@
|
||||
"STATE": "Status",
|
||||
"ISSUER": "Issuer",
|
||||
"SCOPESLIST": "Scopes List",
|
||||
"INSTANCEROLESINFO": "Instanzrollen-Zuordnung",
|
||||
"INSTANCEROLESINFO_DESC": "Benutzer, die sich über diesen Provider anmelden, erhalten die Instanz-Mitgliedsrollen (IAM_-Präfix), die sie in den aufgeführten Organisationen der entfernten ZITADEL-Instanz besitzen. Organisations-ID und primäre Domain müssen beide übereinstimmen.",
|
||||
"ORGANIZATIONID": "Organisations-ID",
|
||||
"ORGANIZATIONDOMAIN": "Organisations-Domain",
|
||||
"CLIENTID": "Client ID",
|
||||
"CLIENTSECRET": "Client Secret",
|
||||
"LDAPCONNECTION": "Verbindung",
|
||||
|
||||
@@ -2254,6 +2254,10 @@
|
||||
"SAML": {
|
||||
"TITLE": "Sign in with SAML",
|
||||
"DESCRIPTION": "Enter the credentials for your SAML Provider"
|
||||
},
|
||||
"ZITADEL": {
|
||||
"TITLE": "ZITADEL",
|
||||
"DESCRIPTION": "Enter the required data to let users of another ZITADEL instance authenticate."
|
||||
}
|
||||
},
|
||||
"DETAIL": {
|
||||
@@ -2319,6 +2323,10 @@
|
||||
"STATE": "Status",
|
||||
"ISSUER": "Issuer",
|
||||
"SCOPESLIST": "Scopes List",
|
||||
"INSTANCEROLESINFO": "Instance role mapping",
|
||||
"INSTANCEROLESINFO_DESC": "Users authenticating with this provider are granted the instance member roles (IAM_ prefix) they hold in the listed organizations of the remote ZITADEL instance. Organization ID and primary domain must both match.",
|
||||
"ORGANIZATIONID": "Organization ID",
|
||||
"ORGANIZATIONDOMAIN": "Organization Domain",
|
||||
"CLIENTID": "Client ID",
|
||||
"CLIENTSECRET": "Client Secret",
|
||||
"LDAPCONNECTION": "Connection",
|
||||
|
||||
@@ -2203,6 +2203,10 @@
|
||||
"TITLE": "Añadir proveedor",
|
||||
"DESCRIPTION": "Selecciona uno o más de los siguientes proveedores.",
|
||||
"STEPPERTITLE": "Crear proveedor",
|
||||
"ZITADEL": {
|
||||
"TITLE": "ZITADEL",
|
||||
"DESCRIPTION": "Introduce los datos necesarios para permitir que los usuarios de otra instancia de ZITADEL se autentiquen."
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "Proveedor OIDC",
|
||||
"DESCRIPTION": "Introduce los datos requeridos por tu proveedor OIDC."
|
||||
@@ -2307,6 +2311,10 @@
|
||||
"STATE": "Estado",
|
||||
"ISSUER": "Emisor",
|
||||
"SCOPESLIST": "Lista de scopes",
|
||||
"INSTANCEROLESINFO": "Asignación de roles de instancia",
|
||||
"INSTANCEROLESINFO_DESC": "Los usuarios que se autentican con este proveedor reciben los roles de miembro de instancia (prefijo IAM_) que poseen en las organizaciones indicadas de la instancia ZITADEL remota. El ID de la organización y el dominio principal deben coincidir.",
|
||||
"ORGANIZATIONID": "ID de la organización",
|
||||
"ORGANIZATIONDOMAIN": "Dominio de la organización",
|
||||
"CLIENTID": "ID del cliente",
|
||||
"CLIENTSECRET": "Secreto del cliente",
|
||||
"LDAPCONNECTION": "Conexión",
|
||||
|
||||
@@ -2202,6 +2202,10 @@
|
||||
"TITLE": "Ajouter un fournisseur",
|
||||
"DESCRIPTION": "Sélectionnez un ou plusieurs des fournisseurs suivants.",
|
||||
"STEPPERTITLE": "Créer un fournisseur",
|
||||
"ZITADEL": {
|
||||
"TITLE": "ZITADEL",
|
||||
"DESCRIPTION": "Saisissez les données requises pour permettre aux utilisateurs d'une autre instance ZITADEL de s'authentifier."
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "Fournisseur OIDC",
|
||||
"DESCRIPTION": "Entrez les données requises pour votre fournisseur OIDC."
|
||||
@@ -2311,6 +2315,10 @@
|
||||
"STATE": "Statut",
|
||||
"ISSUER": "Émetteur",
|
||||
"SCOPESLIST": "Liste des champs d'application",
|
||||
"INSTANCEROLESINFO": "Mappage des rôles d'instance",
|
||||
"INSTANCEROLESINFO_DESC": "Les utilisateurs qui s'authentifient avec ce fournisseur reçoivent les rôles de membre d'instance (préfixe IAM_) qu'ils possèdent dans les organisations listées de l'instance ZITADEL distante. L'ID de l'organisation et le domaine principal doivent tous deux correspondre.",
|
||||
"ORGANIZATIONID": "ID de l'organisation",
|
||||
"ORGANIZATIONDOMAIN": "Domaine de l'organisation",
|
||||
"CLIENTID": "ID du client",
|
||||
"CLIENTSECRET": "Secret du client",
|
||||
"LDAPCONNECTION": "Connexion",
|
||||
|
||||
@@ -2205,6 +2205,10 @@
|
||||
"TITLE": "Szolgáltató hozzáadása",
|
||||
"DESCRIPTION": "Válassz egyet vagy többet az alábbi szolgáltatók közül.",
|
||||
"STEPPERTITLE": "Szolgáltató létrehozása",
|
||||
"ZITADEL": {
|
||||
"TITLE": "ZITADEL",
|
||||
"DESCRIPTION": "Add meg a szükséges adatokat, hogy egy másik ZITADEL példány felhasználói is bejelentkezhessenek."
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "OIDC szolgáltató",
|
||||
"DESCRIPTION": "Add meg a szükséges adatokat az OIDC szolgáltatódhoz."
|
||||
@@ -2317,6 +2321,10 @@
|
||||
"STATE": "Állapot",
|
||||
"ISSUER": "Kibocsátó",
|
||||
"SCOPESLIST": "Hatókörök listája",
|
||||
"INSTANCEROLESINFO": "Példányszerepkörök leképezése",
|
||||
"INSTANCEROLESINFO_DESC": "Az ezzel a szolgáltatóval bejelentkező felhasználók megkapják azokat a példánytagsági szerepköröket (IAM_ előtaggal), amelyekkel a távoli ZITADEL példány felsorolt szervezeteiben rendelkeznek. A szervezet azonosítójának és az elsődleges domainnek is egyeznie kell.",
|
||||
"ORGANIZATIONID": "Szervezet azonosítója",
|
||||
"ORGANIZATIONDOMAIN": "Szervezet domainje",
|
||||
"CLIENTID": "Client ID",
|
||||
"CLIENTSECRET": "Client Secret",
|
||||
"LDAPCONNECTION": "Kapcsolat",
|
||||
|
||||
@@ -2199,6 +2199,10 @@
|
||||
"TITLE": "Tambahkan penyedia",
|
||||
"DESCRIPTION": "Pilih satu atau lebih penyedia berikut.",
|
||||
"STEPPERTITLE": "Buat Penyedia",
|
||||
"ZITADEL": {
|
||||
"TITLE": "ZITADEL",
|
||||
"DESCRIPTION": "Masukkan data yang diperlukan agar pengguna dari instans ZITADEL lain dapat melakukan autentikasi."
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "Penyedia OIDC",
|
||||
"DESCRIPTION": "Masukkan data yang diperlukan untuk penyedia OIDC Anda."
|
||||
@@ -2311,6 +2315,10 @@
|
||||
"STATE": "Status",
|
||||
"ISSUER": "Penerbit",
|
||||
"SCOPESLIST": "Daftar Cakupan",
|
||||
"INSTANCEROLESINFO": "Pemetaan peran instans",
|
||||
"INSTANCEROLESINFO_DESC": "Pengguna yang melakukan autentikasi dengan penyedia ini akan mendapatkan peran anggota instans (awalan IAM_) yang mereka miliki di organisasi yang terdaftar pada instans ZITADEL jarak jauh. ID organisasi dan domain utama keduanya harus cocok.",
|
||||
"ORGANIZATIONID": "ID Organisasi",
|
||||
"ORGANIZATIONDOMAIN": "Domain Organisasi",
|
||||
"CLIENTID": "ID Klien",
|
||||
"CLIENTSECRET": "Rahasia Klien",
|
||||
"LDAPCONNECTION": "Koneksi",
|
||||
|
||||
@@ -2202,6 +2202,10 @@
|
||||
"TITLE": "Aggiungi provider",
|
||||
"DESCRIPTION": "Seleziona uno dei seguenti provider.",
|
||||
"STEPPERTITLE": "Create Provider",
|
||||
"ZITADEL": {
|
||||
"TITLE": "ZITADEL",
|
||||
"DESCRIPTION": "Inserisci i dati richiesti per consentire agli utenti di un'altra istanza ZITADEL di autenticarsi."
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "OIDC Provider",
|
||||
"DESCRIPTION": "Inserisci i dati necessari per il tuo provider OIDC."
|
||||
@@ -2311,6 +2315,10 @@
|
||||
"STATE": "Stato",
|
||||
"ISSUER": "Issuer",
|
||||
"SCOPESLIST": "Scope list",
|
||||
"INSTANCEROLESINFO": "Mappatura dei ruoli di istanza",
|
||||
"INSTANCEROLESINFO_DESC": "Gli utenti che si autenticano con questo provider ricevono i ruoli di membro dell'istanza (prefisso IAM_) che possiedono nelle organizzazioni elencate dell'istanza ZITADEL remota. L'ID dell'organizzazione e il dominio primario devono corrispondere entrambi.",
|
||||
"ORGANIZATIONID": "ID organizzazione",
|
||||
"ORGANIZATIONDOMAIN": "Dominio organizzazione",
|
||||
"CLIENTID": "Client ID",
|
||||
"CLIENTSECRET": "Client Secret",
|
||||
"LDAPCONNECTION": "Connessione",
|
||||
|
||||
@@ -2202,6 +2202,10 @@
|
||||
"TITLE": "プロバイダーの追加",
|
||||
"DESCRIPTION": "以下のプロバイダーから1つ以上選択してください。",
|
||||
"STEPPERTITLE": "プロバイダーの作成",
|
||||
"ZITADEL": {
|
||||
"TITLE": "ZITADEL",
|
||||
"DESCRIPTION": "別の ZITADEL インスタンスのユーザーが認証できるように、必要な情報を入力してください。"
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "OIDCプロバイダー",
|
||||
"DESCRIPTION": "OIDCプロバイダーに必要なデータを入力してください。"
|
||||
@@ -2314,6 +2318,10 @@
|
||||
"STATE": "ステータス",
|
||||
"ISSUER": "発行者",
|
||||
"SCOPESLIST": "スコープリスト",
|
||||
"INSTANCEROLESINFO": "インスタンスロールのマッピング",
|
||||
"INSTANCEROLESINFO_DESC": "このプロバイダーで認証したユーザーには、リモートの ZITADEL インスタンスの指定した組織で保持しているインスタンスメンバーロール(IAM_ プレフィックス)が付与されます。組織 ID とプライマリドメインの両方が一致する必要があります。",
|
||||
"ORGANIZATIONID": "組織 ID",
|
||||
"ORGANIZATIONDOMAIN": "組織ドメイン",
|
||||
"CLIENTID": "クライアントID",
|
||||
"CLIENTSECRET": "クライアントシークレット",
|
||||
"LDAPCONNECTION": "繋がり",
|
||||
|
||||
@@ -2201,6 +2201,10 @@
|
||||
"TITLE": "제공자 추가",
|
||||
"DESCRIPTION": "다음 제공자 중 하나 이상을 선택하세요.",
|
||||
"STEPPERTITLE": "제공자 생성",
|
||||
"ZITADEL": {
|
||||
"TITLE": "ZITADEL",
|
||||
"DESCRIPTION": "다른 ZITADEL 인스턴스의 사용자가 인증할 수 있도록 필요한 데이터를 입력하세요."
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "OIDC 제공자",
|
||||
"DESCRIPTION": "OIDC 제공자에 필요한 데이터를 입력하세요."
|
||||
@@ -2313,6 +2317,10 @@
|
||||
"STATE": "상태",
|
||||
"ISSUER": "발급자",
|
||||
"SCOPESLIST": "스코프 목록",
|
||||
"INSTANCEROLESINFO": "인스턴스 역할 매핑",
|
||||
"INSTANCEROLESINFO_DESC": "이 제공자로 인증하는 사용자는 원격 ZITADEL 인스턴스의 나열된 조직에서 보유한 인스턴스 멤버 역할(IAM_ 접두사)을 부여받습니다. 조직 ID와 기본 도메인이 모두 일치해야 합니다.",
|
||||
"ORGANIZATIONID": "조직 ID",
|
||||
"ORGANIZATIONDOMAIN": "조직 도메인",
|
||||
"CLIENTID": "클라이언트 ID",
|
||||
"CLIENTSECRET": "클라이언트 시크릿",
|
||||
"LDAPCONNECTION": "연결",
|
||||
|
||||
@@ -2205,6 +2205,10 @@
|
||||
"TITLE": "Додади провајдер",
|
||||
"DESCRIPTION": "Изберете еден или повеќе од следниве провајдери.",
|
||||
"STEPPERTITLE": "Креирај провајдер",
|
||||
"ZITADEL": {
|
||||
"TITLE": "ZITADEL",
|
||||
"DESCRIPTION": "Внеси ги потребните податоци за да им овозможиш на корисниците од друга ZITADEL инстанца да се автентицираат."
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "OIDC провајдер",
|
||||
"DESCRIPTION": "Внесете ги потребните податоци за вашиот OIDC провајдер."
|
||||
@@ -2309,6 +2313,10 @@
|
||||
"STATE": "Статус",
|
||||
"ISSUER": "Issuer",
|
||||
"SCOPESLIST": "Листа на опсези",
|
||||
"INSTANCEROLESINFO": "Мапирање на улоги на инстанцата",
|
||||
"INSTANCEROLESINFO_DESC": "Корисниците што се автентицираат преку овој провајдер ги добиваат улогите на членови на инстанцата (со префикс IAM_) што ги имаат во наведените организации на оддалечената ZITADEL инстанца. ID-то на организацијата и примарниот домен мора да се совпаѓаат.",
|
||||
"ORGANIZATIONID": "ID на организацијата",
|
||||
"ORGANIZATIONDOMAIN": "Домен на организацијата",
|
||||
"CLIENTID": "Client Id",
|
||||
"CLIENTSECRET": "Клиентска Тајна",
|
||||
"LDAPCONNECTION": "Конекција",
|
||||
|
||||
@@ -2202,6 +2202,10 @@
|
||||
"TITLE": "Voeg provider toe",
|
||||
"DESCRIPTION": "Selecteer een of meer van de volgende providers.",
|
||||
"STEPPERTITLE": "Creëer Provider",
|
||||
"ZITADEL": {
|
||||
"TITLE": "ZITADEL",
|
||||
"DESCRIPTION": "Voer de vereiste gegevens in zodat gebruikers van een andere ZITADEL-instantie kunnen inloggen."
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "OIDC Provider",
|
||||
"DESCRIPTION": "Voer de vereiste gegevens in voor uw OIDC provider."
|
||||
@@ -2314,6 +2318,10 @@
|
||||
"STATE": "Status",
|
||||
"ISSUER": "Uitgever",
|
||||
"SCOPESLIST": "Scopes Lijst",
|
||||
"INSTANCEROLESINFO": "Toewijzing van instantierollen",
|
||||
"INSTANCEROLESINFO_DESC": "Gebruikers die zich via deze provider authenticeren, krijgen de instantielidrollen (IAM_-prefix) die ze hebben in de vermelde organisaties van de externe ZITADEL-instantie. Het organisatie-ID en het primaire domein moeten beide overeenkomen.",
|
||||
"ORGANIZATIONID": "Organisatie-ID",
|
||||
"ORGANIZATIONDOMAIN": "Organisatiedomein",
|
||||
"CLIENTID": "Client ID",
|
||||
"CLIENTSECRET": "Client Geheim",
|
||||
"LDAPCONNECTION": "Verbinding",
|
||||
|
||||
@@ -2201,6 +2201,10 @@
|
||||
"TITLE": "Dodaj dostawcę",
|
||||
"DESCRIPTION": "Wybierz jednego lub więcej z następujących dostawców.",
|
||||
"STEPPERTITLE": "Utwórz dostawcę",
|
||||
"ZITADEL": {
|
||||
"TITLE": "ZITADEL",
|
||||
"DESCRIPTION": "Wprowadź wymagane dane, aby umożliwić uwierzytelnianie użytkownikom innej instancji ZITADEL."
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "OIDC Provider",
|
||||
"DESCRIPTION": "Wprowadź wymagane dane dla swojego dostawcy OIDC."
|
||||
@@ -2310,6 +2314,10 @@
|
||||
"STATE": "Stan",
|
||||
"ISSUER": "Emitent",
|
||||
"SCOPESLIST": "Lista zakresów",
|
||||
"INSTANCEROLESINFO": "Mapowanie ról instancji",
|
||||
"INSTANCEROLESINFO_DESC": "Użytkownicy uwierzytelniający się przez tego dostawcę otrzymują role członków instancji (prefiks IAM_), które posiadają w wymienionych organizacjach zdalnej instancji ZITADEL. Identyfikator organizacji i domena główna muszą być zgodne.",
|
||||
"ORGANIZATIONID": "Identyfikator organizacji",
|
||||
"ORGANIZATIONDOMAIN": "Domena organizacji",
|
||||
"CLIENTID": "Identyfikator klienta",
|
||||
"CLIENTSECRET": "Tajne klienta",
|
||||
"LDAPCONNECTION": "Połączenie",
|
||||
|
||||
@@ -2204,6 +2204,10 @@
|
||||
"TITLE": "Adicionar provedor",
|
||||
"DESCRIPTION": "Selecione um ou mais dos seguintes provedores.",
|
||||
"STEPPERTITLE": "Criar Provedor",
|
||||
"ZITADEL": {
|
||||
"TITLE": "ZITADEL",
|
||||
"DESCRIPTION": "Insira os dados necessários para permitir que usuários de outra instância do ZITADEL se autentiquem."
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "Provedor OIDC",
|
||||
"DESCRIPTION": "Insira os dados necessários para o seu provedor OIDC."
|
||||
@@ -2308,6 +2312,10 @@
|
||||
"STATE": "Status",
|
||||
"ISSUER": "Eu emissor",
|
||||
"SCOPESLIST": "Lista de Escopos",
|
||||
"INSTANCEROLESINFO": "Mapeamento de funções da instância",
|
||||
"INSTANCEROLESINFO_DESC": "Os usuários que se autenticam com este provedor recebem as funções de membro da instância (prefixo IAM_) que possuem nas organizações listadas da instância ZITADEL remota. O ID da organização e o domínio principal devem corresponder.",
|
||||
"ORGANIZATIONID": "ID da organização",
|
||||
"ORGANIZATIONDOMAIN": "Domínio da organização",
|
||||
"CLIENTID": "ID do Cliente",
|
||||
"CLIENTSECRET": "Segredo do Cliente",
|
||||
"LDAPCONNECTION": "Conexão",
|
||||
|
||||
@@ -2199,6 +2199,10 @@
|
||||
"TITLE": "Adăugați furnizor",
|
||||
"DESCRIPTION": "Selectați unul sau mai mulți dintre următorii furnizori.",
|
||||
"STEPPERTITLE": "Creați furnizorul",
|
||||
"ZITADEL": {
|
||||
"TITLE": "ZITADEL",
|
||||
"DESCRIPTION": "Introduceți datele necesare pentru a permite utilizatorilor unei alte instanțe ZITADEL să se autentifice."
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "Furnizor OIDC",
|
||||
"DESCRIPTION": "Introduceți datele necesare pentru furnizorul dvs. OIDC."
|
||||
@@ -2311,6 +2315,10 @@
|
||||
"STATE": "Stare",
|
||||
"ISSUER": "Emitent",
|
||||
"SCOPESLIST": "Listă de domenii",
|
||||
"INSTANCEROLESINFO": "Maparea rolurilor de instanță",
|
||||
"INSTANCEROLESINFO_DESC": "Utilizatorii care se autentifică cu acest furnizor primesc rolurile de membru al instanței (prefix IAM_) pe care le dețin în organizațiile listate din instanța ZITADEL la distanță. ID-ul organizației și domeniul principal trebuie să corespundă ambele.",
|
||||
"ORGANIZATIONID": "ID-ul organizației",
|
||||
"ORGANIZATIONDOMAIN": "Domeniul organizației",
|
||||
"CLIENTID": "ID client",
|
||||
"CLIENTSECRET": "Secret client",
|
||||
"LDAPCONNECTION": "Conexiune",
|
||||
|
||||
@@ -2293,6 +2293,10 @@
|
||||
"TITLE": "Новый поставщик идентификационных данных",
|
||||
"DESCRIPTION": "Выберите один из следующих типов поставщиков идентификационных данных.",
|
||||
"STEPPERTITLE": "Создать поставщика",
|
||||
"ZITADEL": {
|
||||
"TITLE": "ZITADEL",
|
||||
"DESCRIPTION": "Введите необходимые данные, чтобы пользователи другого экземпляра ZITADEL могли проходить аутентификацию."
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "Поставщик OIDC",
|
||||
"DESCRIPTION": "Введите необходимые данные для вашего провайдера OIDC."
|
||||
@@ -2417,6 +2421,10 @@
|
||||
"STATE": "Статус",
|
||||
"ISSUER": "Поставщик",
|
||||
"SCOPESLIST": "Список областей",
|
||||
"INSTANCEROLESINFO": "Сопоставление ролей экземпляра",
|
||||
"INSTANCEROLESINFO_DESC": "Пользователи, проходящие аутентификацию через этого провайдера, получают роли участника экземпляра (префикс IAM_), которыми они обладают в перечисленных организациях удалённого экземпляра ZITADEL. ID организации и основной домен должны совпадать.",
|
||||
"ORGANIZATIONID": "ID организации",
|
||||
"ORGANIZATIONDOMAIN": "Домен организации",
|
||||
"CLIENTID": "ID Клиента",
|
||||
"CLIENTSECRET": "Клиентский ключ",
|
||||
"LDAPCONNECTION": "Связь",
|
||||
|
||||
@@ -2205,6 +2205,10 @@
|
||||
"TITLE": "Lägg till leverantör",
|
||||
"DESCRIPTION": "Välj en eller flera av följande leverantörer.",
|
||||
"STEPPERTITLE": "Skapa leverantör",
|
||||
"ZITADEL": {
|
||||
"TITLE": "ZITADEL",
|
||||
"DESCRIPTION": "Ange de uppgifter som krävs för att användare från en annan ZITADEL-instans ska kunna autentisera sig."
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "OIDC-leverantör",
|
||||
"DESCRIPTION": "Ange de nödvändiga uppgifterna för din OIDC-leverantör."
|
||||
@@ -2317,6 +2321,10 @@
|
||||
"STATE": "Status",
|
||||
"ISSUER": "Utfärdare",
|
||||
"SCOPESLIST": "Scopes-lista",
|
||||
"INSTANCEROLESINFO": "Mappning av instansroller",
|
||||
"INSTANCEROLESINFO_DESC": "Användare som autentiserar sig med den här leverantören tilldelas de instansmedlemsroller (IAM_-prefix) som de har i de angivna organisationerna i den externa ZITADEL-instansen. Både organisations-ID och primär domän måste stämma.",
|
||||
"ORGANIZATIONID": "Organisations-ID",
|
||||
"ORGANIZATIONDOMAIN": "Organisationsdomän",
|
||||
"CLIENTID": "Klient-ID",
|
||||
"CLIENTSECRET": "Klienthemlighet",
|
||||
"LDAPCONNECTION": "Anslutning",
|
||||
|
||||
@@ -2205,6 +2205,10 @@
|
||||
"TITLE": "Sağlayıcı ekle",
|
||||
"DESCRIPTION": "Aşağıdaki sağlayıcılardan birini veya daha fazlasını seçin.",
|
||||
"STEPPERTITLE": "Sağlayıcı Oluştur",
|
||||
"ZITADEL": {
|
||||
"TITLE": "ZITADEL",
|
||||
"DESCRIPTION": "Başka bir ZITADEL örneğindeki kullanıcıların kimlik doğrulaması yapabilmesi için gerekli verileri girin."
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "OIDC Sağlayıcısı",
|
||||
"DESCRIPTION": "OIDC sağlayıcınız için gerekli verileri girin."
|
||||
@@ -2317,6 +2321,10 @@
|
||||
"STATE": "Durum",
|
||||
"ISSUER": "Yayıncı",
|
||||
"SCOPESLIST": "Kapsam Listesi",
|
||||
"INSTANCEROLESINFO": "Örnek rol eşlemesi",
|
||||
"INSTANCEROLESINFO_DESC": "Bu sağlayıcı ile kimlik doğrulaması yapan kullanıcılara, uzak ZITADEL örneğindeki listelenen organizasyonlarda sahip oldukları örnek üyelik rolleri (IAM_ öneki) verilir. Organizasyon kimliği ve birincil alan adının ikisi de eşleşmelidir.",
|
||||
"ORGANIZATIONID": "Organizasyon Kimliği",
|
||||
"ORGANIZATIONDOMAIN": "Organizasyon Alan Adı",
|
||||
"CLIENTID": "İstemci ID",
|
||||
"CLIENTSECRET": "İstemci Gizli Anahtarı",
|
||||
"LDAPCONNECTION": "Bağlantı",
|
||||
|
||||
@@ -2205,6 +2205,10 @@
|
||||
"TITLE": "Додати постачальника",
|
||||
"DESCRIPTION": "Виберіть одного або декількох з наступних постачальників.",
|
||||
"STEPPERTITLE": "Створити постачальника",
|
||||
"ZITADEL": {
|
||||
"TITLE": "ZITADEL",
|
||||
"DESCRIPTION": "Введіть необхідні дані, щоб користувачі іншого екземпляра ZITADEL могли проходити автентифікацію."
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "OIDC постачальник",
|
||||
"DESCRIPTION": "Введіть необхідні дані для вашого OIDC постачальника."
|
||||
@@ -2317,6 +2321,10 @@
|
||||
"STATE": "Статус",
|
||||
"ISSUER": "Видавець",
|
||||
"SCOPESLIST": "Список областей",
|
||||
"INSTANCEROLESINFO": "Зіставлення ролей екземпляра",
|
||||
"INSTANCEROLESINFO_DESC": "Користувачі, які автентифікуються через цього постачальника, отримують ролі учасника екземпляра (префікс IAM_), які вони мають у перелічених організаціях віддаленого екземпляра ZITADEL. Ідентифікатор організації та основний домен мають збігатися.",
|
||||
"ORGANIZATIONID": "Ідентифікатор організації",
|
||||
"ORGANIZATIONDOMAIN": "Домен організації",
|
||||
"CLIENTID": "ID клієнта",
|
||||
"CLIENTSECRET": "Секрет клієнта",
|
||||
"LDAPCONNECTION": "Підключення",
|
||||
|
||||
@@ -2205,6 +2205,10 @@
|
||||
"TITLE": "创建身份提供者",
|
||||
"DESCRIPTION": "选择以下一个或多个身份提供者。",
|
||||
"STEPPERTITLE": "创建身份提供者",
|
||||
"ZITADEL": {
|
||||
"TITLE": "ZITADEL",
|
||||
"DESCRIPTION": "输入所需数据,以允许其他 ZITADEL 实例的用户进行身份验证。"
|
||||
},
|
||||
"OIDC": {
|
||||
"TITLE": "OIDC 身份提供者",
|
||||
"DESCRIPTION": "输入你的 OIDC 供应商的必要数据。"
|
||||
@@ -2322,6 +2326,10 @@
|
||||
"STATE": "状态",
|
||||
"ISSUER": "Issuer",
|
||||
"SCOPESLIST": "Scopes List",
|
||||
"INSTANCEROLESINFO": "实例角色映射",
|
||||
"INSTANCEROLESINFO_DESC": "通过此提供者进行身份验证的用户将获得他们在远程 ZITADEL 实例的所列组织中拥有的实例成员角色(IAM_ 前缀)。组织 ID 和主域名必须同时匹配。",
|
||||
"ORGANIZATIONID": "组织 ID",
|
||||
"ORGANIZATIONDOMAIN": "组织域名",
|
||||
"CLIENTID": "Client ID",
|
||||
"CLIENTSECRET": "Client Secret",
|
||||
"LDAPCONNECTION": "联系",
|
||||
|
||||
Reference in New Issue
Block a user