mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-14 11:58:02 +00:00
Merge branch 'main' into next-rc
This commit is contained in:
commit
1e7ed4253e
@ -69,6 +69,7 @@ import { StatehandlerService, StatehandlerServiceImpl } from './services/stateha
|
|||||||
import { StorageService } from './services/storage.service';
|
import { StorageService } from './services/storage.service';
|
||||||
import { ThemeService } from './services/theme.service';
|
import { ThemeService } from './services/theme.service';
|
||||||
import { ToastService } from './services/toast.service';
|
import { ToastService } from './services/toast.service';
|
||||||
|
import { LanguagesService } from './services/languages.service';
|
||||||
|
|
||||||
registerLocaleData(localeDe);
|
registerLocaleData(localeDe);
|
||||||
i18nIsoCountries.registerLocale(require('i18n-iso-countries/langs/de.json'));
|
i18nIsoCountries.registerLocale(require('i18n-iso-countries/langs/de.json'));
|
||||||
@ -228,6 +229,7 @@ const authConfig: AuthConfig = {
|
|||||||
AssetService,
|
AssetService,
|
||||||
ToastService,
|
ToastService,
|
||||||
NavigationService,
|
NavigationService,
|
||||||
|
LanguagesService,
|
||||||
{ provide: 'windowObject', useValue: window },
|
{ provide: 'windowObject', useValue: window },
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent],
|
||||||
|
@ -26,7 +26,7 @@ export class GeneralSettingsComponent implements OnInit {
|
|||||||
this.service.getDefaultLanguage().then((langResp) => {
|
this.service.getDefaultLanguage().then((langResp) => {
|
||||||
this.defaultLanguage = langResp.language;
|
this.defaultLanguage = langResp.language;
|
||||||
});
|
});
|
||||||
this.service.getSupportedLanguages().then((supportedResp) => {
|
this.service.getAllowedLanguages().then((supportedResp) => {
|
||||||
this.defaultLanguageOptions = supportedResp.languagesList;
|
this.defaultLanguageOptions = supportedResp.languagesList;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
<h2>{{ 'POLICY.LOGIN_TEXTS.TITLE' | translate }}</h2>
|
<h2>{{ 'POLICY.LOGIN_TEXTS.TITLE' | translate }}</h2>
|
||||||
<p class="cnsl-secondary-text">{{ 'POLICY.LOGIN_TEXTS.DESCRIPTION' | translate }}</p>
|
<p class="cnsl-secondary-text">{{ 'POLICY.LOGIN_TEXTS.DESCRIPTION' | translate }}</p>
|
||||||
|
<cnsl-info-section class="locked" *ngIf="langSvc.isNotAllowed(language) | async" [type]="InfoSectionType.WARN">
|
||||||
|
{{ 'POLICY.LOGIN_TEXTS.ACTIVE_LANGUAGE_NOT_ALLOWED' | translate }}</cnsl-info-section
|
||||||
|
>
|
||||||
|
|
||||||
<div *ngIf="loading" class="spinner-wr">
|
<div *ngIf="loading" class="spinner-wr">
|
||||||
<mat-spinner diameter="30" color="primary"></mat-spinner>
|
<mat-spinner diameter="30" color="primary"></mat-spinner>
|
||||||
@ -24,7 +27,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<form *ngIf="form" class="top-actions" [formGroup]="form">
|
<form *ngIf="allowed$ | async" class="top-actions" [formGroup]="form">
|
||||||
<cnsl-form-field class="keys">
|
<cnsl-form-field class="keys">
|
||||||
<cnsl-label>{{ 'POLICY.LOGIN_TEXTS.KEYNAME' | translate }}</cnsl-label>
|
<cnsl-label>{{ 'POLICY.LOGIN_TEXTS.KEYNAME' | translate }}</cnsl-label>
|
||||||
<mat-select formControlName="currentSubMap" name="currentSubMap">
|
<mat-select formControlName="currentSubMap" name="currentSubMap">
|
||||||
@ -35,18 +38,30 @@
|
|||||||
</cnsl-form-field>
|
</cnsl-form-field>
|
||||||
|
|
||||||
<cnsl-form-field class="language">
|
<cnsl-form-field class="language">
|
||||||
<cnsl-label>{{ 'POLICY.LOGIN_TEXTS.LOCALE' | translate }}</cnsl-label>
|
<cnsl-label>{{ 'POLICY.LOGIN_TEXTS.LANGUAGE' | translate }}</cnsl-label>
|
||||||
<mat-select formControlName="locale" name="locale">
|
<mat-select formControlName="language" name="language">
|
||||||
<mat-option *ngFor="let loc of LOCALES" [value]="loc">
|
<mat-option *ngFor="let lang of allowed$ | async" [value]="lang">
|
||||||
<div class="centerline">
|
<div class="centerline">
|
||||||
<span
|
<span
|
||||||
>{{ loc }}
|
>{{ lang }}
|
||||||
<span class="lighter cnsl-secondary-text"
|
<span class="lighter cnsl-secondary-text"
|
||||||
>| {{ 'POLICY.LOGIN_TEXTS.LOCALES.' + loc | translate }}</span
|
>| {{ 'POLICY.LOGIN_TEXTS.LANGUAGES.' + lang | translate }}</span
|
||||||
></span
|
></span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</mat-option>
|
</mat-option>
|
||||||
|
<mat-optgroup [label]="'POLICY.LOGIN_TEXTS.LANGUAGES_NOT_ALLOWED' | translate" *ngIf="langSvc.restricted$ | async">
|
||||||
|
<mat-option *ngFor="let lang of langSvc.notAllowed$ | async" [value]="lang">
|
||||||
|
<div class="centerline">
|
||||||
|
<span
|
||||||
|
>{{ lang }}
|
||||||
|
<span class="lighter cnsl-secondary-text"
|
||||||
|
>| {{ 'POLICY.LOGIN_TEXTS.LANGUAGES.' + lang | translate }}</span
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</mat-option>
|
||||||
|
</mat-optgroup>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</cnsl-form-field>
|
</cnsl-form-field>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Component, Injector, Input, OnDestroy, OnInit, Type } from '@angular/core';
|
import { Component, Injector, Input, OnDestroy, OnInit, Type } from '@angular/core';
|
||||||
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
import { FormControl, UntypedFormGroup } from '@angular/forms';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
|
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
|
||||||
import { BehaviorSubject, from, interval, Observable, of, Subject, Subscription } from 'rxjs';
|
import { BehaviorSubject, from, interval, Observable, of, Subject, Subscription, switchMap, take, tap } from 'rxjs';
|
||||||
import { map, pairwise, startWith, takeUntil } from 'rxjs/operators';
|
import { map, pairwise, startWith, takeUntil } from 'rxjs/operators';
|
||||||
import {
|
import {
|
||||||
GetCustomLoginTextsRequest as AdminGetCustomLoginTextsRequest,
|
GetCustomLoginTextsRequest as AdminGetCustomLoginTextsRequest,
|
||||||
@ -19,11 +19,11 @@ import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
|||||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||||
import { ToastService } from 'src/app/services/toast.service';
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
|
|
||||||
import { supportedLanguages } from 'src/app/utils/language';
|
|
||||||
import { InfoSectionType } from '../../info-section/info-section.component';
|
import { InfoSectionType } from '../../info-section/info-section.component';
|
||||||
import { WarnDialogComponent } from '../../warn-dialog/warn-dialog.component';
|
import { WarnDialogComponent } from '../../warn-dialog/warn-dialog.component';
|
||||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||||
import { mapRequestValues } from './helper';
|
import { mapRequestValues } from './helper';
|
||||||
|
import { LanguagesService } from '../../../services/languages.service';
|
||||||
|
|
||||||
const MIN_INTERVAL_SECONDS = 10; // if the difference of a newer version to the current exceeds this time, a refresh button is shown.
|
const MIN_INTERVAL_SECONDS = 10; // if the difference of a newer version to the current exceeds this time, a refresh button is shown.
|
||||||
|
|
||||||
@ -110,7 +110,6 @@ export class LoginTextsComponent implements OnInit, OnDestroy {
|
|||||||
@Input() public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
@Input() public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||||
|
|
||||||
public KeyNamesArray: string[] = KeyNamesArray;
|
public KeyNamesArray: string[] = KeyNamesArray;
|
||||||
public LOCALES: string[] = supportedLanguages;
|
|
||||||
|
|
||||||
private sub: Subscription = new Subscription();
|
private sub: Subscription = new Subscription();
|
||||||
|
|
||||||
@ -119,9 +118,15 @@ export class LoginTextsComponent implements OnInit, OnDestroy {
|
|||||||
public destroy$: Subject<void> = new Subject();
|
public destroy$: Subject<void> = new Subject();
|
||||||
public InfoSectionType: any = InfoSectionType;
|
public InfoSectionType: any = InfoSectionType;
|
||||||
public form: UntypedFormGroup = new UntypedFormGroup({
|
public form: UntypedFormGroup = new UntypedFormGroup({
|
||||||
currentSubMap: new UntypedFormControl('emailVerificationDoneText'),
|
currentSubMap: new FormControl<string>('emailVerificationDoneText'),
|
||||||
locale: new UntypedFormControl('en'),
|
language: new FormControl<string>('en'),
|
||||||
});
|
});
|
||||||
|
public allowed$: Observable<string[]> = this.langSvc.allowed$.pipe(
|
||||||
|
take(1),
|
||||||
|
tap(([firstAllowed]) => {
|
||||||
|
this.form.get('language')?.setValue(firstAllowed);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
public isDefault: boolean = false;
|
public isDefault: boolean = false;
|
||||||
|
|
||||||
@ -137,9 +142,10 @@ export class LoginTextsComponent implements OnInit, OnDestroy {
|
|||||||
private injector: Injector,
|
private injector: Injector,
|
||||||
private dialog: MatDialog,
|
private dialog: MatDialog,
|
||||||
private toast: ToastService,
|
private toast: ToastService,
|
||||||
|
public langSvc: LanguagesService,
|
||||||
) {
|
) {
|
||||||
this.form.valueChanges
|
this.form.valueChanges
|
||||||
.pipe(startWith({ currentSubMap: 'emailVerificationDoneText', locale: 'en' }), pairwise(), takeUntil(this.destroy$))
|
.pipe(startWith({ currentSubMap: 'emailVerificationDoneText', language: 'en' }), pairwise(), takeUntil(this.destroy$))
|
||||||
.subscribe((pair) => {
|
.subscribe((pair) => {
|
||||||
this.checkForUnsaved(pair[0].currentSubMap).then((wantsToSave) => {
|
this.checkForUnsaved(pair[0].currentSubMap).then((wantsToSave) => {
|
||||||
if (wantsToSave) {
|
if (wantsToSave) {
|
||||||
@ -162,21 +168,9 @@ export class LoginTextsComponent implements OnInit, OnDestroy {
|
|||||||
switch (this.serviceType) {
|
switch (this.serviceType) {
|
||||||
case PolicyComponentServiceType.MGMT:
|
case PolicyComponentServiceType.MGMT:
|
||||||
this.service = this.injector.get(ManagementService as Type<ManagementService>);
|
this.service = this.injector.get(ManagementService as Type<ManagementService>);
|
||||||
|
|
||||||
this.service.getSupportedLanguages().then((lang) => {
|
|
||||||
this.LOCALES = lang.languagesList;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.loadData();
|
|
||||||
break;
|
break;
|
||||||
case PolicyComponentServiceType.ADMIN:
|
case PolicyComponentServiceType.ADMIN:
|
||||||
this.service = this.injector.get(AdminService as Type<AdminService>);
|
this.service = this.injector.get(AdminService as Type<AdminService>);
|
||||||
|
|
||||||
this.service.getSupportedLanguages().then((lang) => {
|
|
||||||
this.LOCALES = lang.languagesList;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.loadData();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,10 +209,10 @@ export class LoginTextsComponent implements OnInit, OnDestroy {
|
|||||||
public async loadData(): Promise<any> {
|
public async loadData(): Promise<any> {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
const reqDefaultInit = REQUESTMAP[this.serviceType].getDefault;
|
const reqDefaultInit = REQUESTMAP[this.serviceType].getDefault;
|
||||||
reqDefaultInit.setLanguage(this.locale);
|
reqDefaultInit.setLanguage(this.language);
|
||||||
this.getDefaultInitMessageTextMap$ = from(this.getDefaultValues(reqDefaultInit)).pipe(map((m) => m[this.currentSubMap]));
|
this.getDefaultInitMessageTextMap$ = from(this.getDefaultValues(reqDefaultInit)).pipe(map((m) => m[this.currentSubMap]));
|
||||||
|
|
||||||
const reqCustomInit = REQUESTMAP[this.serviceType].get.setLanguage(this.locale);
|
const reqCustomInit = REQUESTMAP[this.serviceType].get.setLanguage(this.language);
|
||||||
return this.getCurrentValues(reqCustomInit)
|
return this.getCurrentValues(reqCustomInit)
|
||||||
.then((policy) => {
|
.then((policy) => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
@ -236,14 +230,14 @@ export class LoginTextsComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async patchSingleCurrentMap(): Promise<any> {
|
private async patchSingleCurrentMap(): Promise<any> {
|
||||||
const reqCustomInit = REQUESTMAP[this.serviceType].get.setLanguage(this.locale);
|
const reqCustomInit = REQUESTMAP[this.serviceType].get.setLanguage(this.language);
|
||||||
this.getCurrentValues(reqCustomInit).then((policy) => {
|
this.getCurrentValues(reqCustomInit).then((policy) => {
|
||||||
this.getCustomInitMessageTextMap$.next(policy[this.currentSubMap]);
|
this.getCustomInitMessageTextMap$.next(policy[this.currentSubMap]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public checkForChanges(): void {
|
public checkForChanges(): void {
|
||||||
const reqCustomInit = REQUESTMAP[this.serviceType].get.setLanguage(this.locale);
|
const reqCustomInit = REQUESTMAP[this.serviceType].get.setLanguage(this.language);
|
||||||
|
|
||||||
(this.service as ManagementService).getCustomLoginTexts(reqCustomInit).then((policy) => {
|
(this.service as ManagementService).getCustomLoginTexts(reqCustomInit).then((policy) => {
|
||||||
this.newerPolicyChangeDate = policy.customText?.details?.changeDate;
|
this.newerPolicyChangeDate = policy.customText?.details?.changeDate;
|
||||||
@ -282,7 +276,7 @@ export class LoginTextsComponent implements OnInit, OnDestroy {
|
|||||||
this.totalCustomPolicy[this.currentSubMap] = values;
|
this.totalCustomPolicy[this.currentSubMap] = values;
|
||||||
|
|
||||||
this.updateRequest = setFcn(this.totalCustomPolicy);
|
this.updateRequest = setFcn(this.totalCustomPolicy);
|
||||||
this.updateRequest.setLanguage(this.locale);
|
this.updateRequest.setLanguage(this.language);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,7 +344,7 @@ export class LoginTextsComponent implements OnInit, OnDestroy {
|
|||||||
if (resp) {
|
if (resp) {
|
||||||
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||||
(this.service as ManagementService)
|
(this.service as ManagementService)
|
||||||
.resetCustomLoginTextToDefault(this.locale)
|
.resetCustomLoginTextToDefault(this.language)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.updateCurrentPolicyDate();
|
this.updateCurrentPolicyDate();
|
||||||
this.isDefault = true;
|
this.isDefault = true;
|
||||||
@ -363,7 +357,7 @@ export class LoginTextsComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
|
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
|
||||||
(this.service as AdminService)
|
(this.service as AdminService)
|
||||||
.resetCustomLoginTextToDefault(this.locale)
|
.resetCustomLoginTextToDefault(this.language)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.updateCurrentPolicyDate();
|
this.updateCurrentPolicyDate();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -397,8 +391,12 @@ export class LoginTextsComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public get locale(): string {
|
public get language(): string {
|
||||||
return this.form.get('locale')?.value;
|
return this.form.get('language')?.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set language(lang: string) {
|
||||||
|
this.form.get('language')?.setValue(lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get currentSubMap(): string {
|
public get currentSubMap(): string {
|
||||||
|
@ -1,70 +1,86 @@
|
|||||||
<h2>{{ 'POLICY.MESSAGE_TEXTS.TITLE' | translate }}</h2>
|
<h2>{{ 'POLICY.MESSAGE_TEXTS.TITLE' | translate }}</h2>
|
||||||
<p class="cnsl-secondary-text">{{ 'POLICY.MESSAGE_TEXTS.DESCRIPTION' | translate }}</p>
|
<p class="cnsl-secondary-text">{{ 'POLICY.MESSAGE_TEXTS.DESCRIPTION' | translate }}</p>
|
||||||
|
<cnsl-info-section class="locked" *ngIf="langSvc.isNotAllowed(language) | async" [type]="InfoSectionType.WARN">
|
||||||
|
{{ 'POLICY.LOGIN_TEXTS.ACTIVE_LANGUAGE_NOT_ALLOWED' | translate }}</cnsl-info-section
|
||||||
|
>
|
||||||
|
|
||||||
<div *ngIf="loading" class="spinner-wr">
|
<div *ngIf="loading" class="spinner-wr">
|
||||||
<mat-spinner diameter="30" color="primary"></mat-spinner>
|
<mat-spinner diameter="30" color="primary"></mat-spinner>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="message-texts-top-actions">
|
<div *ngIf="allowed$ | async">
|
||||||
<cnsl-form-field class="type">
|
<div class="message-texts-top-actions">
|
||||||
<cnsl-label>{{ 'POLICY.MESSAGE_TEXTS.TYPE' | translate }}</cnsl-label>
|
<cnsl-form-field class="type">
|
||||||
<mat-select [(ngModel)]="currentType" name="currentSubMap" (selectionChange)="changedCurrentType()">
|
<cnsl-label>{{ 'POLICY.MESSAGE_TEXTS.TYPE' | translate }}</cnsl-label>
|
||||||
<mat-option *ngFor="let type of MESSAGETYPES | keyvalue" [value]="type.value">
|
<mat-select [(ngModel)]="currentType" name="currentSubMap" (selectionChange)="changedCurrentType()">
|
||||||
{{ 'POLICY.MESSAGE_TEXTS.TYPES.' + type.value | translate }}
|
<mat-option *ngFor="let type of MESSAGETYPES | keyvalue" [value]="type.value">
|
||||||
</mat-option>
|
{{ 'POLICY.MESSAGE_TEXTS.TYPES.' + type.value | translate }}
|
||||||
</mat-select>
|
</mat-option>
|
||||||
</cnsl-form-field>
|
</mat-select>
|
||||||
|
</cnsl-form-field>
|
||||||
|
<cnsl-form-field class="language">
|
||||||
|
<cnsl-label>{{ 'POLICY.LOGIN_TEXTS.LANGUAGE' | translate }}</cnsl-label>
|
||||||
|
<mat-select [(ngModel)]="language" (selectionChange)="changeLocale($event)" name="language">
|
||||||
|
<mat-option *ngFor="let lang of langSvc.allowed$ | async" [value]="lang">
|
||||||
|
<div class="centerline">
|
||||||
|
<span
|
||||||
|
>{{ lang }}
|
||||||
|
<span class="lighter cnsl-secondary-text"
|
||||||
|
>| {{ 'POLICY.LOGIN_TEXTS.LANGUAGES.' + lang | translate }}</span
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</mat-option>
|
||||||
|
<mat-optgroup [label]="'POLICY.LOGIN_TEXTS.LANGUAGES_NOT_ALLOWED' | translate" *ngIf="langSvc.restricted$ | async">
|
||||||
|
<mat-option *ngFor="let lang of langSvc.notAllowed$ | async" [value]="lang">
|
||||||
|
<div class="centerline">
|
||||||
|
<span
|
||||||
|
>{{ lang }}
|
||||||
|
<span class="lighter cnsl-secondary-text"
|
||||||
|
>| {{ 'POLICY.LOGIN_TEXTS.LANGUAGES.' + lang | translate }}</span
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</mat-option>
|
||||||
|
</mat-optgroup>
|
||||||
|
</mat-select>
|
||||||
|
</cnsl-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
<cnsl-form-field class="language">
|
<div class="message-text-content">
|
||||||
<cnsl-label>{{ 'POLICY.LOGIN_TEXTS.LOCALE' | translate }}</cnsl-label>
|
<cnsl-edit-text
|
||||||
<mat-select [(ngModel)]="locale" name="locale" (selectionChange)="changeLocale($event)">
|
[chips]="chips[currentType]"
|
||||||
<mat-option *ngFor="let loc of LOCALES" [value]="loc">
|
[disabled]="(canWrite$ | async) === false"
|
||||||
<div class="centerline">
|
label="one"
|
||||||
<span
|
[default$]="getDefaultMessageTextMap$"
|
||||||
>{{ loc }}
|
[current$]="getCustomMessageTextMap$"
|
||||||
<span class="lighter cnsl-secondary-text"
|
(changedValues)="updateCurrentValues($event)"
|
||||||
>| {{ 'POLICY.LOGIN_TEXTS.LOCALES.' + loc | translate }}</span
|
></cnsl-edit-text>
|
||||||
></span
|
</div>
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</cnsl-form-field>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="message-text-content">
|
<div class="message-text-actions">
|
||||||
<cnsl-edit-text
|
<button
|
||||||
[chips]="chips[currentType]"
|
class="reset-button"
|
||||||
[disabled]="(canWrite$ | async) === false"
|
*ngIf="(getCustomMessageTextMap$ | async) && (getCustomMessageTextMap$ | async)?.['isDefault'] === false"
|
||||||
label="one"
|
[disabled]="(canWrite$ | async) === false"
|
||||||
[default$]="getDefaultMessageTextMap$"
|
(click)="resetDefault()"
|
||||||
[current$]="getCustomMessageTextMap$"
|
color="message-text-warn"
|
||||||
(changedValues)="updateCurrentValues($event)"
|
type="submit"
|
||||||
></cnsl-edit-text>
|
mat-stroked-button
|
||||||
</div>
|
>
|
||||||
|
<div class="cnsl-action-button">
|
||||||
<div class="message-text-actions">
|
<i class="las la-history"></i><span>{{ 'ACTIONS.RESETDEFAULT' | translate }}</span>
|
||||||
<button
|
</div>
|
||||||
class="reset-button"
|
</button>
|
||||||
*ngIf="(getCustomMessageTextMap$ | async) && (getCustomMessageTextMap$ | async)?.['isDefault'] === false"
|
<button
|
||||||
[disabled]="(canWrite$ | async) === false"
|
class="save-button"
|
||||||
(click)="resetDefault()"
|
[disabled]="!updateRequest || (canWrite$ | async) === false"
|
||||||
color="message-text-warn"
|
(click)="saveCurrentMessage()"
|
||||||
type="submit"
|
color="primary"
|
||||||
mat-stroked-button
|
type="submit"
|
||||||
>
|
mat-raised-button
|
||||||
<div class="cnsl-action-button">
|
>
|
||||||
<i class="las la-history"></i><span>{{ 'ACTIONS.RESETDEFAULT' | translate }}</span>
|
{{ 'ACTIONS.SAVE' | translate }}
|
||||||
</div>
|
</button>
|
||||||
</button>
|
</div>
|
||||||
<button
|
|
||||||
class="save-button"
|
|
||||||
[disabled]="!updateRequest || (canWrite$ | async) === false"
|
|
||||||
(click)="saveCurrentMessage()"
|
|
||||||
color="primary"
|
|
||||||
type="submit"
|
|
||||||
mat-raised-button
|
|
||||||
>
|
|
||||||
{{ 'ACTIONS.SAVE' | translate }}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Component, Injector, Input, OnDestroy, OnInit, Type } from '@angular/core';
|
import { Component, Injector, Input, OnDestroy, OnInit, Type } from '@angular/core';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { MatSelectChange } from '@angular/material/select';
|
import { MatSelectChange } from '@angular/material/select';
|
||||||
import { BehaviorSubject, from, Observable, of, Subscription } from 'rxjs';
|
import { BehaviorSubject, from, Observable, of, Subscription, switchMap, take, tap } from 'rxjs';
|
||||||
import {
|
import {
|
||||||
GetDefaultDomainClaimedMessageTextRequest as AdminGetDefaultDomainClaimedMessageTextRequest,
|
GetDefaultDomainClaimedMessageTextRequest as AdminGetDefaultDomainClaimedMessageTextRequest,
|
||||||
GetDefaultInitMessageTextRequest as AdminGetDefaultInitMessageTextRequest,
|
GetDefaultInitMessageTextRequest as AdminGetDefaultInitMessageTextRequest,
|
||||||
@ -57,10 +57,11 @@ import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
|||||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||||
import { ToastService } from 'src/app/services/toast.service';
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
|
|
||||||
import { supportedLanguages } from 'src/app/utils/language';
|
|
||||||
import { InfoSectionType } from '../../info-section/info-section.component';
|
import { InfoSectionType } from '../../info-section/info-section.component';
|
||||||
import { WarnDialogComponent } from '../../warn-dialog/warn-dialog.component';
|
import { WarnDialogComponent } from '../../warn-dialog/warn-dialog.component';
|
||||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
import { LanguagesService } from '../../../services/languages.service';
|
||||||
|
|
||||||
enum MESSAGETYPES {
|
enum MESSAGETYPES {
|
||||||
INIT = 'INIT',
|
INIT = 'INIT',
|
||||||
@ -537,8 +538,15 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
public locale: string = 'en';
|
public language: string = 'en';
|
||||||
public LOCALES: string[] = supportedLanguages;
|
public allowed$: Observable<string[]> = this.langSvc.allowed$.pipe(
|
||||||
|
take(1),
|
||||||
|
tap(([firstAllowed]) => {
|
||||||
|
this.language = firstAllowed;
|
||||||
|
this.loadData(this.currentType);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
private sub: Subscription = new Subscription();
|
private sub: Subscription = new Subscription();
|
||||||
public canWrite$: Observable<boolean> = this.authService.isAllowed([
|
public canWrite$: Observable<boolean> = this.authService.isAllowed([
|
||||||
this.serviceType === PolicyComponentServiceType.ADMIN
|
this.serviceType === PolicyComponentServiceType.ADMIN
|
||||||
@ -553,23 +561,16 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
|||||||
private toast: ToastService,
|
private toast: ToastService,
|
||||||
private injector: Injector,
|
private injector: Injector,
|
||||||
private dialog: MatDialog,
|
private dialog: MatDialog,
|
||||||
|
public langSvc: LanguagesService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
switch (this.serviceType) {
|
switch (this.serviceType) {
|
||||||
case PolicyComponentServiceType.MGMT:
|
case PolicyComponentServiceType.MGMT:
|
||||||
this.service = this.injector.get(ManagementService as Type<ManagementService>);
|
this.service = this.injector.get(ManagementService as Type<ManagementService>);
|
||||||
this.service.getSupportedLanguages().then((lang) => {
|
|
||||||
this.LOCALES = lang.languagesList;
|
|
||||||
});
|
|
||||||
this.loadData(this.currentType);
|
|
||||||
break;
|
break;
|
||||||
case PolicyComponentServiceType.ADMIN:
|
case PolicyComponentServiceType.ADMIN:
|
||||||
this.service = this.injector.get(AdminService as Type<AdminService>);
|
this.service = this.injector.get(AdminService as Type<AdminService>);
|
||||||
this.service.getSupportedLanguages().then((lang) => {
|
|
||||||
this.LOCALES = lang.languagesList;
|
|
||||||
});
|
|
||||||
this.loadData(this.currentType);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -623,7 +624,7 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public changeLocale(selection: MatSelectChange): void {
|
public changeLocale(selection: MatSelectChange): void {
|
||||||
this.locale = selection.value;
|
this.language = selection.value;
|
||||||
this.loadData(this.currentType);
|
this.loadData(this.currentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -631,11 +632,11 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
|||||||
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||||
const reqDefaultInit = REQUESTMAP[this.serviceType][type].getDefault;
|
const reqDefaultInit = REQUESTMAP[this.serviceType][type].getDefault;
|
||||||
|
|
||||||
reqDefaultInit.setLanguage(this.locale);
|
reqDefaultInit.setLanguage(this.language);
|
||||||
this.getDefaultMessageTextMap$ = from(this.getDefaultValues(type, reqDefaultInit));
|
this.getDefaultMessageTextMap$ = from(this.getDefaultValues(type, reqDefaultInit));
|
||||||
}
|
}
|
||||||
|
|
||||||
const reqCustomInit = REQUESTMAP[this.serviceType][type].get.setLanguage(this.locale);
|
const reqCustomInit = REQUESTMAP[this.serviceType][type].get.setLanguage(this.language);
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
return this.getCurrentValues(type, reqCustomInit)
|
return this.getCurrentValues(type, reqCustomInit)
|
||||||
?.then((data) => {
|
?.then((data) => {
|
||||||
@ -652,7 +653,7 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
|||||||
const req = REQUESTMAP[this.serviceType][this.currentType].setFcn;
|
const req = REQUESTMAP[this.serviceType][this.currentType].setFcn;
|
||||||
const mappedValues = req(values);
|
const mappedValues = req(values);
|
||||||
this.updateRequest = mappedValues;
|
this.updateRequest = mappedValues;
|
||||||
this.updateRequest.setLanguage(this.locale);
|
this.updateRequest.setLanguage(this.language);
|
||||||
}
|
}
|
||||||
|
|
||||||
public saveCurrentMessage(): any {
|
public saveCurrentMessage(): any {
|
||||||
@ -741,23 +742,23 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
switch (this.currentType) {
|
switch (this.currentType) {
|
||||||
case MESSAGETYPES.INIT:
|
case MESSAGETYPES.INIT:
|
||||||
return handler(this.service.resetCustomInitMessageTextToDefault(this.locale));
|
return handler(this.service.resetCustomInitMessageTextToDefault(this.language));
|
||||||
case MESSAGETYPES.VERIFYPHONE:
|
case MESSAGETYPES.VERIFYPHONE:
|
||||||
return handler(this.service.resetCustomVerifyPhoneMessageTextToDefault(this.locale));
|
return handler(this.service.resetCustomVerifyPhoneMessageTextToDefault(this.language));
|
||||||
case MESSAGETYPES.VERIFYSMSOTP:
|
case MESSAGETYPES.VERIFYSMSOTP:
|
||||||
return handler(this.service.resetCustomVerifySMSOTPMessageTextToDefault(this.locale));
|
return handler(this.service.resetCustomVerifySMSOTPMessageTextToDefault(this.language));
|
||||||
case MESSAGETYPES.VERIFYEMAILOTP:
|
case MESSAGETYPES.VERIFYEMAILOTP:
|
||||||
return handler(this.service.resetCustomVerifyEmailOTPMessageTextToDefault(this.locale));
|
return handler(this.service.resetCustomVerifyEmailOTPMessageTextToDefault(this.language));
|
||||||
case MESSAGETYPES.VERIFYEMAIL:
|
case MESSAGETYPES.VERIFYEMAIL:
|
||||||
return handler(this.service.resetCustomVerifyEmailMessageTextToDefault(this.locale));
|
return handler(this.service.resetCustomVerifyEmailMessageTextToDefault(this.language));
|
||||||
case MESSAGETYPES.PASSWORDRESET:
|
case MESSAGETYPES.PASSWORDRESET:
|
||||||
return handler(this.service.resetCustomPasswordResetMessageTextToDefault(this.locale));
|
return handler(this.service.resetCustomPasswordResetMessageTextToDefault(this.language));
|
||||||
case MESSAGETYPES.DOMAINCLAIMED:
|
case MESSAGETYPES.DOMAINCLAIMED:
|
||||||
return handler(this.service.resetCustomDomainClaimedMessageTextToDefault(this.locale));
|
return handler(this.service.resetCustomDomainClaimedMessageTextToDefault(this.language));
|
||||||
case MESSAGETYPES.PASSWORDLESS:
|
case MESSAGETYPES.PASSWORDLESS:
|
||||||
return handler(this.service.resetCustomPasswordlessRegistrationMessageTextToDefault(this.locale));
|
return handler(this.service.resetCustomPasswordlessRegistrationMessageTextToDefault(this.language));
|
||||||
case MESSAGETYPES.PASSWORDCHANGE:
|
case MESSAGETYPES.PASSWORDCHANGE:
|
||||||
return handler(this.service.resetCustomPasswordChangeMessageTextToDefault(this.locale));
|
return handler(this.service.resetCustomPasswordChangeMessageTextToDefault(this.language));
|
||||||
default:
|
default:
|
||||||
return Promise.reject();
|
return Promise.reject();
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@
|
|||||||
<cnsl-form-field class="formfield">
|
<cnsl-form-field class="formfield">
|
||||||
<cnsl-label>{{ 'USER.PROFILE.PREFERRED_LANGUAGE' | translate }}</cnsl-label>
|
<cnsl-label>{{ 'USER.PROFILE.PREFERRED_LANGUAGE' | translate }}</cnsl-label>
|
||||||
<mat-select formControlName="preferredLanguage">
|
<mat-select formControlName="preferredLanguage">
|
||||||
<mat-option *ngFor="let language of languages" [value]="language">
|
<mat-option *ngFor="let language of langSvc.supported$ | async" [value]="language">
|
||||||
{{ 'LANGUAGES.' + language | translate }}
|
{{ 'LANGUAGES.' + language | translate }}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
|
@ -20,7 +20,7 @@ import { AdminService } from 'src/app/services/admin.service';
|
|||||||
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
|
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
|
||||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||||
import { ToastService } from 'src/app/services/toast.service';
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
import { supportedLanguages } from 'src/app/utils/language';
|
import { LanguagesService } from '../../services/languages.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'cnsl-org-create',
|
selector: 'cnsl-org-create',
|
||||||
@ -46,7 +46,6 @@ export class OrgCreateComponent {
|
|||||||
public pwdForm?: UntypedFormGroup;
|
public pwdForm?: UntypedFormGroup;
|
||||||
|
|
||||||
public genders: Gender[] = [Gender.GENDER_FEMALE, Gender.GENDER_MALE, Gender.GENDER_UNSPECIFIED];
|
public genders: Gender[] = [Gender.GENDER_FEMALE, Gender.GENDER_MALE, Gender.GENDER_UNSPECIFIED];
|
||||||
public languages: string[] = supportedLanguages;
|
|
||||||
|
|
||||||
public policy?: PasswordComplexityPolicy.AsObject;
|
public policy?: PasswordComplexityPolicy.AsObject;
|
||||||
public usePassword: boolean = false;
|
public usePassword: boolean = false;
|
||||||
@ -60,6 +59,7 @@ export class OrgCreateComponent {
|
|||||||
private _location: Location,
|
private _location: Location,
|
||||||
private fb: UntypedFormBuilder,
|
private fb: UntypedFormBuilder,
|
||||||
private mgmtService: ManagementService,
|
private mgmtService: ManagementService,
|
||||||
|
public langSvc: LanguagesService,
|
||||||
breadcrumbService: BreadcrumbService,
|
breadcrumbService: BreadcrumbService,
|
||||||
) {
|
) {
|
||||||
const instanceBread = new Breadcrumb({
|
const instanceBread = new Breadcrumb({
|
||||||
@ -70,10 +70,6 @@ export class OrgCreateComponent {
|
|||||||
|
|
||||||
breadcrumbService.setBreadcrumb([instanceBread]);
|
breadcrumbService.setBreadcrumb([instanceBread]);
|
||||||
this.initForm();
|
this.initForm();
|
||||||
|
|
||||||
this.adminService.getSupportedLanguages().then((supportedResp) => {
|
|
||||||
this.languages = supportedResp.languagesList;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public createSteps: number = 2;
|
public createSteps: number = 2;
|
||||||
|
@ -91,7 +91,7 @@
|
|||||||
<cnsl-form-field>
|
<cnsl-form-field>
|
||||||
<cnsl-label>{{ 'USER.PROFILE.PREFERRED_LANGUAGE' | translate }}</cnsl-label>
|
<cnsl-label>{{ 'USER.PROFILE.PREFERRED_LANGUAGE' | translate }}</cnsl-label>
|
||||||
<mat-select formControlName="preferredLanguage">
|
<mat-select formControlName="preferredLanguage">
|
||||||
<mat-option *ngFor="let language of languages" [value]="language">
|
<mat-option *ngFor="let language of langSvc.supported$ | async" [value]="language">
|
||||||
{{ 'LANGUAGES.' + language | translate }}
|
{{ 'LANGUAGES.' + language | translate }}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
|
@ -2,7 +2,7 @@ import { Location } from '@angular/common';
|
|||||||
import { ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
import { ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||||
import { AbstractControl, UntypedFormBuilder, UntypedFormGroup, ValidatorFn, Validators } from '@angular/forms';
|
import { AbstractControl, UntypedFormBuilder, UntypedFormGroup, ValidatorFn, Validators } from '@angular/forms';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { Subject, debounceTime } from 'rxjs';
|
import { Subject, debounceTime, Observable } from 'rxjs';
|
||||||
import { AddHumanUserRequest } from 'src/app/proto/generated/zitadel/management_pb';
|
import { AddHumanUserRequest } from 'src/app/proto/generated/zitadel/management_pb';
|
||||||
import { Domain } from 'src/app/proto/generated/zitadel/org_pb';
|
import { Domain } from 'src/app/proto/generated/zitadel/org_pb';
|
||||||
import { PasswordComplexityPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
|
import { PasswordComplexityPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
|
||||||
@ -13,7 +13,6 @@ import { ToastService } from 'src/app/services/toast.service';
|
|||||||
|
|
||||||
import { CountryCallingCodesService, CountryPhoneCode } from 'src/app/services/country-calling-codes.service';
|
import { CountryCallingCodesService, CountryPhoneCode } from 'src/app/services/country-calling-codes.service';
|
||||||
import { formatPhone } from 'src/app/utils/formatPhone';
|
import { formatPhone } from 'src/app/utils/formatPhone';
|
||||||
import { supportedLanguages } from 'src/app/utils/language';
|
|
||||||
import {
|
import {
|
||||||
containsLowerCaseValidator,
|
containsLowerCaseValidator,
|
||||||
containsNumberValidator,
|
containsNumberValidator,
|
||||||
@ -25,6 +24,7 @@ import {
|
|||||||
phoneValidator,
|
phoneValidator,
|
||||||
requiredValidator,
|
requiredValidator,
|
||||||
} from '../../../modules/form-field/validators/validators';
|
} from '../../../modules/form-field/validators/validators';
|
||||||
|
import { LanguagesService } from '../../../services/languages.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'cnsl-user-create',
|
selector: 'cnsl-user-create',
|
||||||
@ -34,7 +34,6 @@ import {
|
|||||||
export class UserCreateComponent implements OnInit, OnDestroy {
|
export class UserCreateComponent implements OnInit, OnDestroy {
|
||||||
public user: AddHumanUserRequest.AsObject = new AddHumanUserRequest().toObject();
|
public user: AddHumanUserRequest.AsObject = new AddHumanUserRequest().toObject();
|
||||||
public genders: Gender[] = [Gender.GENDER_FEMALE, Gender.GENDER_MALE, Gender.GENDER_UNSPECIFIED];
|
public genders: Gender[] = [Gender.GENDER_FEMALE, Gender.GENDER_MALE, Gender.GENDER_UNSPECIFIED];
|
||||||
public languages: string[] = supportedLanguages;
|
|
||||||
public selected: CountryPhoneCode | undefined = {
|
public selected: CountryPhoneCode | undefined = {
|
||||||
countryCallingCode: '1',
|
countryCallingCode: '1',
|
||||||
countryCode: 'US',
|
countryCode: 'US',
|
||||||
@ -61,6 +60,7 @@ export class UserCreateComponent implements OnInit, OnDestroy {
|
|||||||
private changeDetRef: ChangeDetectorRef,
|
private changeDetRef: ChangeDetectorRef,
|
||||||
private _location: Location,
|
private _location: Location,
|
||||||
private countryCallingCodesService: CountryCallingCodesService,
|
private countryCallingCodesService: CountryCallingCodesService,
|
||||||
|
public langSvc: LanguagesService,
|
||||||
breadcrumbService: BreadcrumbService,
|
breadcrumbService: BreadcrumbService,
|
||||||
) {
|
) {
|
||||||
breadcrumbService.setBreadcrumb([
|
breadcrumbService.setBreadcrumb([
|
||||||
@ -69,7 +69,6 @@ export class UserCreateComponent implements OnInit, OnDestroy {
|
|||||||
routerLink: ['/org'],
|
routerLink: ['/org'],
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.loadOrg();
|
this.loadOrg();
|
||||||
this.mgmtService
|
this.mgmtService
|
||||||
@ -88,10 +87,6 @@ export class UserCreateComponent implements OnInit, OnDestroy {
|
|||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.changeDetRef.detectChanges();
|
this.changeDetRef.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.mgmtService.getSupportedLanguages().then((lang) => {
|
|
||||||
this.languages = lang.languagesList;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public close(): void {
|
public close(): void {
|
||||||
|
@ -19,7 +19,12 @@
|
|||||||
|
|
||||||
<div class="max-width-container">
|
<div class="max-width-container">
|
||||||
<cnsl-meta-layout>
|
<cnsl-meta-layout>
|
||||||
<cnsl-sidenav [(ngModel)]="currentSetting" [settingsList]="settingsList" queryParam="id">
|
<cnsl-sidenav
|
||||||
|
[(ngModel)]="currentSetting"
|
||||||
|
[settingsList]="settingsList"
|
||||||
|
queryParam="id"
|
||||||
|
(ngModelChange)="settingChanged()"
|
||||||
|
>
|
||||||
<ng-container *ngIf="currentSetting === 'general'">
|
<ng-container *ngIf="currentSetting === 'general'">
|
||||||
<cnsl-card
|
<cnsl-card
|
||||||
*ngIf="user && user.human && user.human.profile"
|
*ngIf="user && user.human && user.human.profile"
|
||||||
@ -30,7 +35,7 @@
|
|||||||
[showEditImage]="true"
|
[showEditImage]="true"
|
||||||
[preferredLoginName]="user.preferredLoginName"
|
[preferredLoginName]="user.preferredLoginName"
|
||||||
[genders]="genders"
|
[genders]="genders"
|
||||||
[languages]="languages"
|
[languages]="(langSvc.supported$ | async) || []"
|
||||||
[username]="user.userName"
|
[username]="user.userName"
|
||||||
[user]="user.human"
|
[user]="user.human"
|
||||||
[disabled]="false"
|
[disabled]="false"
|
||||||
|
@ -6,7 +6,7 @@ import { MatDialog } from '@angular/material/dialog';
|
|||||||
import { ActivatedRoute, Params } from '@angular/router';
|
import { ActivatedRoute, Params } from '@angular/router';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { Buffer } from 'buffer';
|
import { Buffer } from 'buffer';
|
||||||
import { Subscription, take } from 'rxjs';
|
import { from, Observable, Subscription, take } from 'rxjs';
|
||||||
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
||||||
import { phoneValidator, requiredValidator } from 'src/app/modules/form-field/validators/validators';
|
import { phoneValidator, requiredValidator } from 'src/app/modules/form-field/validators/validators';
|
||||||
import { InfoDialogComponent } from 'src/app/modules/info-dialog/info-dialog.component';
|
import { InfoDialogComponent } from 'src/app/modules/info-dialog/info-dialog.component';
|
||||||
@ -24,8 +24,8 @@ import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
|||||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||||
import { ToastService } from 'src/app/services/toast.service';
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
import { formatPhone } from 'src/app/utils/formatPhone';
|
import { formatPhone } from 'src/app/utils/formatPhone';
|
||||||
import { supportedLanguages } from 'src/app/utils/language';
|
|
||||||
import { EditDialogComponent, EditDialogType } from './edit-dialog/edit-dialog.component';
|
import { EditDialogComponent, EditDialogType } from './edit-dialog/edit-dialog.component';
|
||||||
|
import { LanguagesService } from '../../../../services/languages.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'cnsl-auth-user-detail',
|
selector: 'cnsl-auth-user-detail',
|
||||||
@ -35,7 +35,6 @@ import { EditDialogComponent, EditDialogType } from './edit-dialog/edit-dialog.c
|
|||||||
export class AuthUserDetailComponent implements OnDestroy {
|
export class AuthUserDetailComponent implements OnDestroy {
|
||||||
public user?: User.AsObject;
|
public user?: User.AsObject;
|
||||||
public genders: Gender[] = [Gender.GENDER_MALE, Gender.GENDER_FEMALE, Gender.GENDER_DIVERSE];
|
public genders: Gender[] = [Gender.GENDER_MALE, Gender.GENDER_FEMALE, Gender.GENDER_DIVERSE];
|
||||||
public languages: string[] = supportedLanguages;
|
|
||||||
|
|
||||||
private subscription: Subscription = new Subscription();
|
private subscription: Subscription = new Subscription();
|
||||||
|
|
||||||
@ -65,6 +64,7 @@ export class AuthUserDetailComponent implements OnDestroy {
|
|||||||
];
|
];
|
||||||
public currentSetting: string | undefined = this.settingsList[0].id;
|
public currentSetting: string | undefined = this.settingsList[0].id;
|
||||||
public loginPolicy?: LoginPolicy.AsObject;
|
public loginPolicy?: LoginPolicy.AsObject;
|
||||||
|
private savedLanguage?: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public translate: TranslateService,
|
public translate: TranslateService,
|
||||||
@ -77,10 +77,12 @@ export class AuthUserDetailComponent implements OnDestroy {
|
|||||||
private mediaMatcher: MediaMatcher,
|
private mediaMatcher: MediaMatcher,
|
||||||
private _location: Location,
|
private _location: Location,
|
||||||
activatedRoute: ActivatedRoute,
|
activatedRoute: ActivatedRoute,
|
||||||
|
public langSvc: LanguagesService,
|
||||||
) {
|
) {
|
||||||
activatedRoute.queryParams.pipe(take(1)).subscribe((params: Params) => {
|
activatedRoute.queryParams.pipe(take(1)).subscribe((params: Params) => {
|
||||||
const { id } = params;
|
const { id } = params;
|
||||||
if (id) {
|
if (id) {
|
||||||
|
this.cleanupTranslation();
|
||||||
this.currentSetting = id;
|
this.currentSetting = id;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -97,10 +99,6 @@ export class AuthUserDetailComponent implements OnDestroy {
|
|||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.refreshUser();
|
this.refreshUser();
|
||||||
|
|
||||||
this.userService.getSupportedLanguages().then((lang) => {
|
|
||||||
this.languages = lang.languagesList;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.userService.getMyLoginPolicy().then((policy) => {
|
this.userService.getMyLoginPolicy().then((policy) => {
|
||||||
if (policy.policy) {
|
if (policy.policy) {
|
||||||
this.loginPolicy = policy.policy;
|
this.loginPolicy = policy.policy;
|
||||||
@ -109,6 +107,7 @@ export class AuthUserDetailComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private changeSelection(small: boolean): void {
|
private changeSelection(small: boolean): void {
|
||||||
|
this.cleanupTranslation();
|
||||||
if (small) {
|
if (small) {
|
||||||
this.currentSetting = undefined;
|
this.currentSetting = undefined;
|
||||||
} else {
|
} else {
|
||||||
@ -138,6 +137,7 @@ export class AuthUserDetailComponent implements OnDestroy {
|
|||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
this.savedLanguage = resp.user?.human?.profile?.preferredLanguage;
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@ -147,9 +147,22 @@ export class AuthUserDetailComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ngOnDestroy(): void {
|
public ngOnDestroy(): void {
|
||||||
|
this.cleanupTranslation();
|
||||||
this.subscription.unsubscribe();
|
this.subscription.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public settingChanged(): void {
|
||||||
|
this.cleanupTranslation();
|
||||||
|
}
|
||||||
|
|
||||||
|
private cleanupTranslation(): void {
|
||||||
|
if (this?.savedLanguage) {
|
||||||
|
this.translate.use(this?.savedLanguage);
|
||||||
|
} else {
|
||||||
|
this.translate.use(this.translate.defaultLang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public changeUsername(): void {
|
public changeUsername(): void {
|
||||||
const dialogRef = this.dialog.open(EditDialogComponent, {
|
const dialogRef = this.dialog.open(EditDialogComponent, {
|
||||||
data: {
|
data: {
|
||||||
@ -193,6 +206,7 @@ export class AuthUserDetailComponent implements OnDestroy {
|
|||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.toast.showInfo('USER.TOAST.SAVED', true);
|
this.toast.showInfo('USER.TOAST.SAVED', true);
|
||||||
|
this.savedLanguage = this.user?.human?.profile?.preferredLanguage;
|
||||||
this.refreshChanges$.emit();
|
this.refreshChanges$.emit();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
@ -83,7 +83,7 @@
|
|||||||
[preferredLoginName]="user.preferredLoginName"
|
[preferredLoginName]="user.preferredLoginName"
|
||||||
[disabled]="(canWrite$ | async) === false"
|
[disabled]="(canWrite$ | async) === false"
|
||||||
[genders]="genders"
|
[genders]="genders"
|
||||||
[languages]="languages"
|
[languages]="(langSvc.supported$ | async) || []"
|
||||||
[username]="user.userName"
|
[username]="user.userName"
|
||||||
[user]="user.human"
|
[user]="user.human"
|
||||||
(submitData)="saveProfile($event)"
|
(submitData)="saveProfile($event)"
|
||||||
|
@ -22,10 +22,11 @@ import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/
|
|||||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||||
import { ToastService } from 'src/app/services/toast.service';
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
import { formatPhone } from 'src/app/utils/formatPhone';
|
import { formatPhone } from 'src/app/utils/formatPhone';
|
||||||
import { supportedLanguages } from 'src/app/utils/language';
|
|
||||||
import { EditDialogComponent, EditDialogType } from '../auth-user-detail/edit-dialog/edit-dialog.component';
|
import { EditDialogComponent, EditDialogType } from '../auth-user-detail/edit-dialog/edit-dialog.component';
|
||||||
import { ResendEmailDialogComponent } from '../auth-user-detail/resend-email-dialog/resend-email-dialog.component';
|
import { ResendEmailDialogComponent } from '../auth-user-detail/resend-email-dialog/resend-email-dialog.component';
|
||||||
import { MachineSecretDialogComponent } from './machine-secret-dialog/machine-secret-dialog.component';
|
import { MachineSecretDialogComponent } from './machine-secret-dialog/machine-secret-dialog.component';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { LanguagesService } from '../../../../services/languages.service';
|
||||||
|
|
||||||
const GENERAL: SidenavSetting = { id: 'general', i18nKey: 'USER.SETTINGS.GENERAL' };
|
const GENERAL: SidenavSetting = { id: 'general', i18nKey: 'USER.SETTINGS.GENERAL' };
|
||||||
const GRANTS: SidenavSetting = { id: 'grants', i18nKey: 'USER.SETTINGS.USERGRANTS' };
|
const GRANTS: SidenavSetting = { id: 'grants', i18nKey: 'USER.SETTINGS.USERGRANTS' };
|
||||||
@ -45,7 +46,6 @@ export class UserDetailComponent implements OnInit {
|
|||||||
public user!: User.AsObject;
|
public user!: User.AsObject;
|
||||||
public metadata: Metadata.AsObject[] = [];
|
public metadata: Metadata.AsObject[] = [];
|
||||||
public genders: Gender[] = [Gender.GENDER_MALE, Gender.GENDER_FEMALE, Gender.GENDER_DIVERSE];
|
public genders: Gender[] = [Gender.GENDER_MALE, Gender.GENDER_FEMALE, Gender.GENDER_DIVERSE];
|
||||||
public languages: string[] = supportedLanguages;
|
|
||||||
|
|
||||||
public ChangeType: any = ChangeType;
|
public ChangeType: any = ChangeType;
|
||||||
|
|
||||||
@ -76,6 +76,7 @@ export class UserDetailComponent implements OnInit {
|
|||||||
private router: Router,
|
private router: Router,
|
||||||
activatedRoute: ActivatedRoute,
|
activatedRoute: ActivatedRoute,
|
||||||
private mediaMatcher: MediaMatcher,
|
private mediaMatcher: MediaMatcher,
|
||||||
|
public langSvc: LanguagesService,
|
||||||
breadcrumbService: BreadcrumbService,
|
breadcrumbService: BreadcrumbService,
|
||||||
) {
|
) {
|
||||||
activatedRoute.queryParams.pipe(take(1)).subscribe((params: Params) => {
|
activatedRoute.queryParams.pipe(take(1)).subscribe((params: Params) => {
|
||||||
@ -100,10 +101,6 @@ export class UserDetailComponent implements OnInit {
|
|||||||
this.mediaMatcher.matchMedia(mediaq).onchange = (small) => {
|
this.mediaMatcher.matchMedia(mediaq).onchange = (small) => {
|
||||||
this.changeSelection(small.matches);
|
this.changeSelection(small.matches);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.mgmtUserService.getSupportedLanguages().then((lang) => {
|
|
||||||
this.languages = lang.languagesList;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private changeSelection(small: boolean): void {
|
private changeSelection(small: boolean): void {
|
||||||
|
@ -54,6 +54,8 @@ import {
|
|||||||
DeactivateSMSProviderResponse,
|
DeactivateSMSProviderResponse,
|
||||||
DeleteProviderRequest,
|
DeleteProviderRequest,
|
||||||
DeleteProviderResponse,
|
DeleteProviderResponse,
|
||||||
|
GetAllowedLanguagesRequest,
|
||||||
|
GetAllowedLanguagesResponse,
|
||||||
GetCustomDomainClaimedMessageTextRequest,
|
GetCustomDomainClaimedMessageTextRequest,
|
||||||
GetCustomDomainClaimedMessageTextResponse,
|
GetCustomDomainClaimedMessageTextResponse,
|
||||||
GetCustomDomainPolicyRequest,
|
GetCustomDomainPolicyRequest,
|
||||||
@ -433,6 +435,11 @@ export class AdminService {
|
|||||||
return this.grpcService.admin.getSupportedLanguages(req, null).then((resp) => resp.toObject());
|
return this.grpcService.admin.getSupportedLanguages(req, null).then((resp) => resp.toObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getAllowedLanguages(): Promise<GetAllowedLanguagesResponse.AsObject> {
|
||||||
|
const req = new GetAllowedLanguagesRequest();
|
||||||
|
return this.grpcService.admin.getAllowedLanguages(req, null).then((resp) => resp.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
public getDefaultLoginTexts(req: GetDefaultLoginTextsRequest): Promise<GetDefaultLoginTextsResponse.AsObject> {
|
public getDefaultLoginTexts(req: GetDefaultLoginTextsRequest): Promise<GetDefaultLoginTextsResponse.AsObject> {
|
||||||
return this.grpcService.admin.getDefaultLoginTexts(req, null).then((resp) => resp.toObject());
|
return this.grpcService.admin.getDefaultLoginTexts(req, null).then((resp) => resp.toObject());
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,6 @@ import {
|
|||||||
GetMyProfileResponse,
|
GetMyProfileResponse,
|
||||||
GetMyUserRequest,
|
GetMyUserRequest,
|
||||||
GetMyUserResponse,
|
GetMyUserResponse,
|
||||||
GetSupportedLanguagesRequest,
|
|
||||||
GetSupportedLanguagesResponse,
|
|
||||||
ListMyAuthFactorsRequest,
|
ListMyAuthFactorsRequest,
|
||||||
ListMyAuthFactorsResponse,
|
ListMyAuthFactorsResponse,
|
||||||
ListMyLinkedIDPsRequest,
|
ListMyLinkedIDPsRequest,
|
||||||
@ -494,11 +492,6 @@ export class GrpcAuthService {
|
|||||||
return this.grpcService.auth.resendMyEmailVerification(req, null).then((resp) => resp.toObject());
|
return this.grpcService.auth.resendMyEmailVerification(req, null).then((resp) => resp.toObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSupportedLanguages(): Promise<GetSupportedLanguagesResponse.AsObject> {
|
|
||||||
const req = new GetSupportedLanguagesRequest();
|
|
||||||
return this.grpcService.auth.getSupportedLanguages(req, null).then((resp) => resp.toObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
public getMyLoginPolicy(): Promise<GetMyLoginPolicyResponse.AsObject> {
|
public getMyLoginPolicy(): Promise<GetMyLoginPolicyResponse.AsObject> {
|
||||||
const req = new GetMyLoginPolicyRequest();
|
const req = new GetMyLoginPolicyRequest();
|
||||||
return this.grpcService.auth.getMyLoginPolicy(req, null).then((resp) => resp.toObject());
|
return this.grpcService.auth.getMyLoginPolicy(req, null).then((resp) => resp.toObject());
|
||||||
|
47
console/src/app/services/languages.service.ts
Normal file
47
console/src/app/services/languages.service.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { forkJoin, Observable, ReplaySubject, Subscription } from 'rxjs';
|
||||||
|
import { map, withLatestFrom } from 'rxjs/operators';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { AdminService } from './admin.service';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class LanguagesService {
|
||||||
|
private supportedSubject$ = new ReplaySubject<string[]>(1);
|
||||||
|
public supported$: Observable<string[]> = this.supportedSubject$.asObservable();
|
||||||
|
private allowedSubject$ = new ReplaySubject<string[]>(1);
|
||||||
|
public allowed$: Observable<string[]> = this.allowedSubject$.asObservable();
|
||||||
|
public notAllowed$: Observable<string[]> = this.allowed$.pipe(
|
||||||
|
withLatestFrom(this.supported$),
|
||||||
|
map(([allowed, supported]) => {
|
||||||
|
return supported.filter((s) => !allowed.includes(s));
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
public restricted$: Observable<boolean> = this.notAllowed$.pipe(
|
||||||
|
map((notallowed) => {
|
||||||
|
return notallowed.length > 0;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
constructor(private adminSvc: AdminService) {
|
||||||
|
const sub: Subscription = forkJoin([
|
||||||
|
this.adminSvc.getSupportedLanguages(),
|
||||||
|
this.adminSvc.getAllowedLanguages(),
|
||||||
|
]).subscribe({
|
||||||
|
next: ([{ languagesList: supported }, { languagesList: allowed }]) => {
|
||||||
|
this.supportedSubject$.next(supported);
|
||||||
|
this.allowedSubject$.next(allowed);
|
||||||
|
},
|
||||||
|
complete: () => sub.unsubscribe(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: call this in https://github.com/zitadel/zitadel/pull/6965
|
||||||
|
public newAllowed(languages: string[]) {
|
||||||
|
this.allowedSubject$.next(languages);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isNotAllowed(language: string): Observable<boolean> {
|
||||||
|
return this.notAllowed$.pipe(map((notAllowed) => notAllowed.includes(language)));
|
||||||
|
}
|
||||||
|
}
|
@ -551,11 +551,6 @@ export class ManagementService {
|
|||||||
|
|
||||||
constructor(private readonly grpcService: GrpcService) {}
|
constructor(private readonly grpcService: GrpcService) {}
|
||||||
|
|
||||||
public getSupportedLanguages(): Promise<GetSupportedLanguagesResponse.AsObject> {
|
|
||||||
const req = new GetSupportedLanguagesRequest();
|
|
||||||
return this.grpcService.mgmt.getSupportedLanguages(req, null).then((resp) => resp.toObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
public getDefaultLoginTexts(req: GetDefaultLoginTextsRequest): Promise<GetDefaultLoginTextsResponse.AsObject> {
|
public getDefaultLoginTexts(req: GetDefaultLoginTextsRequest): Promise<GetDefaultLoginTextsResponse.AsObject> {
|
||||||
return this.grpcService.mgmt.getDefaultLoginTexts(req, null).then((resp) => resp.toObject());
|
return this.grpcService.mgmt.getDefaultLoginTexts(req, null).then((resp) => resp.toObject());
|
||||||
}
|
}
|
||||||
|
@ -1255,8 +1255,10 @@
|
|||||||
"RESET_DESCRIPTION": "На път сте да възстановите всички стойности по подразбиране. ",
|
"RESET_DESCRIPTION": "На път сте да възстановите всички стойности по подразбиране. ",
|
||||||
"UNSAVED_TITLE": "Продължаване без запазване?",
|
"UNSAVED_TITLE": "Продължаване без запазване?",
|
||||||
"UNSAVED_DESCRIPTION": "Направихте промени без да запазите. ",
|
"UNSAVED_DESCRIPTION": "Направихте промени без да запазите. ",
|
||||||
"LOCALE": "Локален код",
|
"ACTIVE_LANGUAGE_NOT_ALLOWED": "Избрахте език, който не е разрешен. Можете да продължите да променяте текстовете. Но ако искате вашите потребители да могат да използват този език, променете ограниченията на вашите екземпляри.",
|
||||||
"LOCALES": {
|
"LANGUAGES_NOT_ALLOWED": "Не е разрешено:",
|
||||||
|
"LANGUAGE": "Език",
|
||||||
|
"LANGUAGES": {
|
||||||
"de": "Deutsch",
|
"de": "Deutsch",
|
||||||
"en": "Английски",
|
"en": "Английски",
|
||||||
"es": "Español",
|
"es": "Español",
|
||||||
|
@ -1262,8 +1262,10 @@
|
|||||||
"RESET_DESCRIPTION": "Chystáte se obnovit všechny výchozí hodnoty. Všechny vaše změny budou trvale smazány. Opravdu chcete pokračovat?",
|
"RESET_DESCRIPTION": "Chystáte se obnovit všechny výchozí hodnoty. Všechny vaše změny budou trvale smazány. Opravdu chcete pokračovat?",
|
||||||
"UNSAVED_TITLE": "Pokračovat bez uložení?",
|
"UNSAVED_TITLE": "Pokračovat bez uložení?",
|
||||||
"UNSAVED_DESCRIPTION": "Provedli jste změny bez uložení. Chcete je nyní uložit?",
|
"UNSAVED_DESCRIPTION": "Provedli jste změny bez uložení. Chcete je nyní uložit?",
|
||||||
"LOCALE": "Kód jazyka",
|
"ACTIVE_LANGUAGE_NOT_ALLOWED": "Vybrali jste jazyk, který není povolen. Můžete pokračovat v úpravách textů. Ale pokud chcete, aby vaši uživatelé mohli tento jazyk skutečně používat, změňte omezení vašich instancí.",
|
||||||
"LOCALES": {
|
"LANGUAGES_NOT_ALLOWED": "Nepovolené jazyky:",
|
||||||
|
"LANGUAGE": "Jazyk",
|
||||||
|
"LANGUAGES": {
|
||||||
"de": "Deutsch",
|
"de": "Deutsch",
|
||||||
"en": "English",
|
"en": "English",
|
||||||
"es": "Español",
|
"es": "Español",
|
||||||
|
@ -1261,8 +1261,10 @@
|
|||||||
"RESET_DESCRIPTION": "Sie sind im Begriff alle Standardwerte wiederherzustellen. Alle von Ihnen gesetzten Änderungen werden unwiderruflich gelöscht. Wollen Sie fortfahren?",
|
"RESET_DESCRIPTION": "Sie sind im Begriff alle Standardwerte wiederherzustellen. Alle von Ihnen gesetzten Änderungen werden unwiderruflich gelöscht. Wollen Sie fortfahren?",
|
||||||
"UNSAVED_TITLE": "Ohne speichern fortfahren?",
|
"UNSAVED_TITLE": "Ohne speichern fortfahren?",
|
||||||
"UNSAVED_DESCRIPTION": "Sie haben Änderungen vorgenommen ohne zu speichern. Möchten Sie jetzt speichern?",
|
"UNSAVED_DESCRIPTION": "Sie haben Änderungen vorgenommen ohne zu speichern. Möchten Sie jetzt speichern?",
|
||||||
"LOCALE": "Sprachcode",
|
"ACTIVE_LANGUAGE_NOT_ALLOWED": "Sie haben eine Sprache ausgewählt, die nicht erlaubt ist. Sie können weiterhin die Texte ändern. Wenn Sie jedoch möchten, dass Ihre Benutzer diese Sprache tatsächlich verwenden können, ändern Sie die Einschränkungen Ihrer Instanz.",
|
||||||
"LOCALES": {
|
"LANGUAGES_NOT_ALLOWED": "Nicht erlaubt:",
|
||||||
|
"LANGUAGE": "Sprache",
|
||||||
|
"LANGUAGES": {
|
||||||
"de": "Deutsch",
|
"de": "Deutsch",
|
||||||
"en": "English",
|
"en": "English",
|
||||||
"es": "Español",
|
"es": "Español",
|
||||||
|
@ -1262,8 +1262,10 @@
|
|||||||
"RESET_DESCRIPTION": "You are about to restore all default values. All changes you have made will be permanently deleted. Do you really want to continue?",
|
"RESET_DESCRIPTION": "You are about to restore all default values. All changes you have made will be permanently deleted. Do you really want to continue?",
|
||||||
"UNSAVED_TITLE": "Continue without saving?",
|
"UNSAVED_TITLE": "Continue without saving?",
|
||||||
"UNSAVED_DESCRIPTION": "You have made changes without saving. Do you want to save now?",
|
"UNSAVED_DESCRIPTION": "You have made changes without saving. Do you want to save now?",
|
||||||
"LOCALE": "Locale Code",
|
"ACTIVE_LANGUAGE_NOT_ALLOWED": "You selected a language that is not allowed. You can go on modifying the texts. But if you want your users to actually be able to use this language, change your instances restrictions.",
|
||||||
"LOCALES": {
|
"LANGUAGES_NOT_ALLOWED": "Not allowed:",
|
||||||
|
"LANGUAGE": "Language",
|
||||||
|
"LANGUAGES": {
|
||||||
"de": "Deutsch",
|
"de": "Deutsch",
|
||||||
"en": "English",
|
"en": "English",
|
||||||
"es": "Español",
|
"es": "Español",
|
||||||
|
@ -1262,8 +1262,10 @@
|
|||||||
"RESET_DESCRIPTION": "Estás a punto de restaurar todos los valores por defecto. Todos los cambios que has hecho serán borrados permanentemente. ¿Estás seguro de que quieres continuar?",
|
"RESET_DESCRIPTION": "Estás a punto de restaurar todos los valores por defecto. Todos los cambios que has hecho serán borrados permanentemente. ¿Estás seguro de que quieres continuar?",
|
||||||
"UNSAVED_TITLE": "¿Continuar sin guardar?",
|
"UNSAVED_TITLE": "¿Continuar sin guardar?",
|
||||||
"UNSAVED_DESCRIPTION": "Has hecho cambios sin guardar. ¿Quieres guardarlos ahora?",
|
"UNSAVED_DESCRIPTION": "Has hecho cambios sin guardar. ¿Quieres guardarlos ahora?",
|
||||||
"LOCALE": "Código de idioma",
|
"ACTIVE_LANGUAGE_NOT_ALLOWED": "Has seleccionado un idioma que no está permitido. Puedes seguir modificando los textos. Pero si quieres que tus usuarios realmente puedan usar este idioma, cambia las restricciones de tus instancias.",
|
||||||
"LOCALES": {
|
"LANGUAGES_NOT_ALLOWED": "No permitido:",
|
||||||
|
"LANGUAGE": "Idioma",
|
||||||
|
"LANGUAGES": {
|
||||||
"de": "Deutsch",
|
"de": "Deutsch",
|
||||||
"en": "English",
|
"en": "English",
|
||||||
"es": "Español",
|
"es": "Español",
|
||||||
|
@ -1261,8 +1261,10 @@
|
|||||||
"RESET_DESCRIPTION": "Vous êtes sur le point de restaurer toutes les valeurs par défaut. Toutes les modifications que vous avez apportées seront définitivement supprimées. Voulez-vous vraiment continuer ?",
|
"RESET_DESCRIPTION": "Vous êtes sur le point de restaurer toutes les valeurs par défaut. Toutes les modifications que vous avez apportées seront définitivement supprimées. Voulez-vous vraiment continuer ?",
|
||||||
"UNSAVED_TITLE": "Continuer sans sauvegarder ?",
|
"UNSAVED_TITLE": "Continuer sans sauvegarder ?",
|
||||||
"UNSAVED_DESCRIPTION": "Vous avez apporté des modifications sans les sauvegarder. Voulez-vous les enregistrer maintenant ?",
|
"UNSAVED_DESCRIPTION": "Vous avez apporté des modifications sans les sauvegarder. Voulez-vous les enregistrer maintenant ?",
|
||||||
"LOCALE": "Code Locale",
|
"ACTIVE_LANGUAGE_NOT_ALLOWED": "Vous avez sélectionné une langue qui n'est pas autorisée. Vous pouvez continuer à modifier les textes. Mais si vous voulez que vos utilisateurs puissent réellement utiliser cette langue, modifiez les restrictions de vos instances.",
|
||||||
"LOCALES": {
|
"LANGUAGES_NOT_ALLOWED": "Non autorisé:",
|
||||||
|
"LANGUAGE": "Langue",
|
||||||
|
"LANGUAGES": {
|
||||||
"de": "Deutsch",
|
"de": "Deutsch",
|
||||||
"en": "English",
|
"en": "English",
|
||||||
"es": "Español",
|
"es": "Español",
|
||||||
|
@ -1261,8 +1261,10 @@
|
|||||||
"RESET_DESCRIPTION": "Stai per ripristinare tutti i valori predefiniti. Tutte le modifiche che hai fatto saranno cancellate in modo permanente. Vuoi davvero continuare?",
|
"RESET_DESCRIPTION": "Stai per ripristinare tutti i valori predefiniti. Tutte le modifiche che hai fatto saranno cancellate in modo permanente. Vuoi davvero continuare?",
|
||||||
"UNSAVED_TITLE": "Continuare senza salvare?",
|
"UNSAVED_TITLE": "Continuare senza salvare?",
|
||||||
"UNSAVED_DESCRIPTION": "Hai fatto delle modifiche senza salvare. Vuoi salvare ora?",
|
"UNSAVED_DESCRIPTION": "Hai fatto delle modifiche senza salvare. Vuoi salvare ora?",
|
||||||
"LOCALE": "Codice locale",
|
"ACTIVE_LANGUAGE_NOT_ALLOWED": "Hai selezionato una lingua non consentita. Puoi continuare a modificare i testi. Ma se vuoi che i tuoi utenti possano effettivamente utilizzare questa lingua, cambia le restrizioni delle tue istanze.",
|
||||||
"LOCALES": {
|
"LANGUAGE": "Lingua",
|
||||||
|
"LANGUAGES_NOT_ALLOWED": "Non consentito:",
|
||||||
|
"LANGUAGES": {
|
||||||
"de": "Deutsch",
|
"de": "Deutsch",
|
||||||
"en": "English",
|
"en": "English",
|
||||||
"es": "Español",
|
"es": "Español",
|
||||||
|
@ -1257,8 +1257,10 @@
|
|||||||
"RESET_DESCRIPTION": "すべてのデフォルト値を復元しようとしています。ユーザーが行ったすべての変更は完全に削除されます。本当によろしいですか?",
|
"RESET_DESCRIPTION": "すべてのデフォルト値を復元しようとしています。ユーザーが行ったすべての変更は完全に削除されます。本当によろしいですか?",
|
||||||
"UNSAVED_TITLE": "保存せずに続行しますか?",
|
"UNSAVED_TITLE": "保存せずに続行しますか?",
|
||||||
"UNSAVED_DESCRIPTION": "あなたは保存せずに変更を加えました。今すぐ保存しますか?",
|
"UNSAVED_DESCRIPTION": "あなたは保存せずに変更を加えました。今すぐ保存しますか?",
|
||||||
"LOCALE": "ロケールコード",
|
"ACTIVE_LANGUAGE_NOT_ALLOWED": "許可されていない言語を選択しました。テキストを変更し続けることはできますが、実際にこの言語を使用できるようにするには、インスタンスの制限を変更してください。",
|
||||||
"LOCALES": {
|
"LANGUAGES_NOT_ALLOWED": "許可されていない言語:",
|
||||||
|
"LANGUAGE": "言語",
|
||||||
|
"LANGUAGES": {
|
||||||
"de": "Deutsch",
|
"de": "Deutsch",
|
||||||
"en": "English",
|
"en": "English",
|
||||||
"es": "Español",
|
"es": "Español",
|
||||||
|
@ -1263,8 +1263,10 @@
|
|||||||
"RESET_DESCRIPTION": "Се подготвувате да ги вратите сите стандардни вредности. Сите промени што ги направивте ќе бидат трајно избришани. Дали сте сигурни дека сакате да продолжите?",
|
"RESET_DESCRIPTION": "Се подготвувате да ги вратите сите стандардни вредности. Сите промени што ги направивте ќе бидат трајно избришани. Дали сте сигурни дека сакате да продолжите?",
|
||||||
"UNSAVED_TITLE": "Дали сакате да продолжите без зачувување?",
|
"UNSAVED_TITLE": "Дали сакате да продолжите без зачувување?",
|
||||||
"UNSAVED_DESCRIPTION": "Имате направено промени без зачувување. Дали сакате да ги зачувате сега?",
|
"UNSAVED_DESCRIPTION": "Имате направено промени без зачувување. Дали сакате да ги зачувате сега?",
|
||||||
"LOCALE": "Locale Code",
|
"ACTIVE_LANGUAGE_NOT_ALLOWED": "Избравте јазик кој не е дозволен. Можете да продолжите да ги менувате текстовите. Но, ако сакате вашите корисници да можат да го користат овој јазик, променете ги ограничувањата на вашата инстанца.",
|
||||||
"LOCALES": {
|
"LANGUAGES_NOT_ALLOWED": "Не е дозволено:",
|
||||||
|
"LANGUAGE": "Јазик",
|
||||||
|
"LANGUAGES": {
|
||||||
"de": "Deutsch",
|
"de": "Deutsch",
|
||||||
"en": "English",
|
"en": "English",
|
||||||
"es": "Español",
|
"es": "Español",
|
||||||
|
@ -1261,9 +1261,11 @@
|
|||||||
"RESET_TITLE": "Herstel Standaard Waarden",
|
"RESET_TITLE": "Herstel Standaard Waarden",
|
||||||
"RESET_DESCRIPTION": "U staat op het punt om alle standaardwaarden te herstellen. Alle wijzigingen die u heeft gemaakt zullen permanent worden verwijderd. Weet u zeker dat u wilt doorgaan?",
|
"RESET_DESCRIPTION": "U staat op het punt om alle standaardwaarden te herstellen. Alle wijzigingen die u heeft gemaakt zullen permanent worden verwijderd. Weet u zeker dat u wilt doorgaan?",
|
||||||
"UNSAVED_TITLE": "Doorgaan zonder opslaan?",
|
"UNSAVED_TITLE": "Doorgaan zonder opslaan?",
|
||||||
"UNSAAVED_DESCRIPTION": "U heeft wijzigingen gemaakt zonder op te slaan. Wilt u nu opslaan?",
|
"UNSAVED_DESCRIPTION": "U heeft wijzigingen gemaakt zonder op te slaan. Wilt u nu opslaan?",
|
||||||
"LOCALE": "Locale Code",
|
"ACTIVE_LANGUAGE_NOT_ALLOWED": "U heeft een taal geselecteerd die niet is toegestaan. U kunt doorgaan met het wijzigen van de teksten. Maar als u wilt dat uw gebruikers deze taal daadwerkelijk kunnen gebruiken, wijzig dan de beperkingen van uw instantie.",
|
||||||
"LOCALES": {
|
"LANGUAGES_NOT_ALLOWED": "Niet toegestaan:",
|
||||||
|
"LANGUAGE": "Taal",
|
||||||
|
"LANGUAGES": {
|
||||||
"de": "Deutsch",
|
"de": "Deutsch",
|
||||||
"en": "English",
|
"en": "English",
|
||||||
"es": "Español",
|
"es": "Español",
|
||||||
|
@ -1261,8 +1261,10 @@
|
|||||||
"RESET_DESCRIPTION": "Masz zamiar przywrócić domyślne linki dla TOS i polityki prywatności. Czy na pewno chcesz kontynuować?",
|
"RESET_DESCRIPTION": "Masz zamiar przywrócić domyślne linki dla TOS i polityki prywatności. Czy na pewno chcesz kontynuować?",
|
||||||
"UNSAVED_TITLE": "Kontynuuj bez zapisywania?",
|
"UNSAVED_TITLE": "Kontynuuj bez zapisywania?",
|
||||||
"UNSAVED_DESCRIPTION": "Wprowadziłeś zmiany bez zapisywania. Czy chcesz zapisać teraz?",
|
"UNSAVED_DESCRIPTION": "Wprowadziłeś zmiany bez zapisywania. Czy chcesz zapisać teraz?",
|
||||||
"LOCALE": "Kod Języka",
|
"ACTIVE_LANGUAGE_NOT_ALLOWED": "Wybrałeś język, który nie jest dozwolony. Możesz kontynuować modyfikowanie tekstów. Ale jeśli chcesz, aby twoi użytkownicy mogli faktycznie używać tego języka, zmień ograniczenia swoich instancji.",
|
||||||
"LOCALES": {
|
"LANGUAGES_NOT_ALLOWED": "Niedozwolone:",
|
||||||
|
"LANGUAGE": "Język",
|
||||||
|
"LANGUAGES": {
|
||||||
"de": "Deutsch",
|
"de": "Deutsch",
|
||||||
"en": "English",
|
"en": "English",
|
||||||
"es": "Español",
|
"es": "Español",
|
||||||
|
@ -1263,8 +1263,10 @@
|
|||||||
"RESET_DESCRIPTION": "Você está prestes a restaurar todos os valores padrão. Todas as alterações que você fez serão excluídas permanentemente. Deseja realmente continuar?",
|
"RESET_DESCRIPTION": "Você está prestes a restaurar todos os valores padrão. Todas as alterações que você fez serão excluídas permanentemente. Deseja realmente continuar?",
|
||||||
"UNSAVED_TITLE": "Continuar sem salvar?",
|
"UNSAVED_TITLE": "Continuar sem salvar?",
|
||||||
"UNSAVED_DESCRIPTION": "Você fez alterações sem salvar. Deseja salvar agora?",
|
"UNSAVED_DESCRIPTION": "Você fez alterações sem salvar. Deseja salvar agora?",
|
||||||
"LOCALE": "Código de localidade",
|
"ACTIVE_LANGUAGE_NOT_ALLOWED": "Você selecionou um idioma que não é permitido. Você pode continuar modificando os textos. Mas se deseja que seus usuários realmente possam usar este idioma, altere as restrições de suas instâncias.",
|
||||||
"LOCALES": {
|
"LANGUAGES_NOT_ALLOWED": "Não permitido:",
|
||||||
|
"LANGUAGE": "Idioma",
|
||||||
|
"LANGUAGES": {
|
||||||
"de": "Deutsch",
|
"de": "Deutsch",
|
||||||
"en": "English",
|
"en": "English",
|
||||||
"es": "Español",
|
"es": "Español",
|
||||||
|
@ -1248,8 +1248,10 @@
|
|||||||
"RESET_DESCRIPTION": "Вы собираетесь восстановить все значения по умолчанию. Все внесенные вами изменения будут безвозвратно удалены. Вы действительно хотите продолжить?",
|
"RESET_DESCRIPTION": "Вы собираетесь восстановить все значения по умолчанию. Все внесенные вами изменения будут безвозвратно удалены. Вы действительно хотите продолжить?",
|
||||||
"UNSAVED_TITLE": "Продолжить без сохранения?",
|
"UNSAVED_TITLE": "Продолжить без сохранения?",
|
||||||
"UNSAVED_DESCRIPTION": "Вы внесли изменения без сохранения. Вы хотите сохранить сейчас?",
|
"UNSAVED_DESCRIPTION": "Вы внесли изменения без сохранения. Вы хотите сохранить сейчас?",
|
||||||
"LOCALE": "Код региона",
|
"ACTIVE_LANGUAGE_NOT_ALLOWED": "Вы выбрали язык, который не разрешен. Вы можете продолжить изменять тексты. Но если вы хотите, чтобы ваши пользователи могли фактически использовать этот язык, измените ограничения ваших экземпляров.",
|
||||||
"LOCALES": {
|
"LANGUAGES_NOT_ALLOWED": "Не разрешено:",
|
||||||
|
"LANGUAGE": "Язык",
|
||||||
|
"LANGUAGES": {
|
||||||
"de": "Deutsch",
|
"de": "Deutsch",
|
||||||
"en": "English",
|
"en": "English",
|
||||||
"es": "Español",
|
"es": "Español",
|
||||||
@ -1261,6 +1263,7 @@
|
|||||||
"bg": "Български",
|
"bg": "Български",
|
||||||
"pt": "Portuguese",
|
"pt": "Portuguese",
|
||||||
"mk": "Македонски",
|
"mk": "Македонски",
|
||||||
|
"cs": "Čeština",
|
||||||
"ru": "Русский",
|
"ru": "Русский",
|
||||||
"nl": "Nederlands"
|
"nl": "Nederlands"
|
||||||
},
|
},
|
||||||
|
@ -1260,8 +1260,10 @@
|
|||||||
"RESET_DESCRIPTION": "您即将恢复所有默认值。您所做的所有更改都将被永久删除。你真的要继续吗?",
|
"RESET_DESCRIPTION": "您即将恢复所有默认值。您所做的所有更改都将被永久删除。你真的要继续吗?",
|
||||||
"UNSAVED_TITLE": "继续但不保存?",
|
"UNSAVED_TITLE": "继续但不保存?",
|
||||||
"UNSAVED_DESCRIPTION": "您在未保存的情况下进行了更改。您现在要保存吗?",
|
"UNSAVED_DESCRIPTION": "您在未保存的情况下进行了更改。您现在要保存吗?",
|
||||||
"LOCALE": "本地化",
|
"ACTIVE_LANGUAGE_NOT_ALLOWED": "您选择了不允许的语言。您可以继续修改文本。但是,如果您希望您的用户实际上能够使用此语言,请更改您的实例限制。",
|
||||||
"LOCALES": {
|
"LANGUAGES_NOT_ALLOWED": "不允许:",
|
||||||
|
"LANGUAGE": "语言",
|
||||||
|
"LANGUAGES": {
|
||||||
"de": "Deutsch",
|
"de": "Deutsch",
|
||||||
"en": "English",
|
"en": "English",
|
||||||
"es": "Español",
|
"es": "Español",
|
||||||
|
@ -17,6 +17,7 @@ Database:
|
|||||||
Port: 5432
|
Port: 5432
|
||||||
Database: zitadel
|
Database: zitadel
|
||||||
MaxOpenConns: 25
|
MaxOpenConns: 25
|
||||||
|
MaxIdleConns: 10
|
||||||
MaxConnLifetime: 1h
|
MaxConnLifetime: 1h
|
||||||
MaxConnIdleTime: 5m
|
MaxConnIdleTime: 5m
|
||||||
Options:
|
Options:
|
||||||
|
@ -2,7 +2,6 @@ package admin
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/api/authz"
|
"github.com/zitadel/zitadel/internal/api/authz"
|
||||||
"github.com/zitadel/zitadel/internal/api/grpc/object"
|
"github.com/zitadel/zitadel/internal/api/grpc/object"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
@ -31,3 +30,15 @@ func (s *Server) SetDefaultLanguage(ctx context.Context, req *admin_pb.SetDefaul
|
|||||||
func (s *Server) GetDefaultLanguage(ctx context.Context, _ *admin_pb.GetDefaultLanguageRequest) (*admin_pb.GetDefaultLanguageResponse, error) {
|
func (s *Server) GetDefaultLanguage(ctx context.Context, _ *admin_pb.GetDefaultLanguageRequest) (*admin_pb.GetDefaultLanguageResponse, error) {
|
||||||
return &admin_pb.GetDefaultLanguageResponse{Language: authz.GetInstance(ctx).DefaultLanguage().String()}, nil
|
return &admin_pb.GetDefaultLanguageResponse{Language: authz.GetInstance(ctx).DefaultLanguage().String()}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) GetAllowedLanguages(ctx context.Context, _ *admin_pb.GetAllowedLanguagesRequest) (*admin_pb.GetAllowedLanguagesResponse, error) {
|
||||||
|
restrictions, err := s.query.GetInstanceRestrictions(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
allowed := restrictions.AllowedLanguages
|
||||||
|
if len(allowed) == 0 {
|
||||||
|
allowed = i18n.SupportedLanguages()
|
||||||
|
}
|
||||||
|
return &admin_pb.GetAllowedLanguagesResponse{Languages: domain.LanguagesToStrings(allowed)}, nil
|
||||||
|
}
|
||||||
|
@ -22,7 +22,7 @@ import (
|
|||||||
func TestServer_Restrictions_DisallowPublicOrgRegistration(t *testing.T) {
|
func TestServer_Restrictions_DisallowPublicOrgRegistration(t *testing.T) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
domain, _, iamOwnerCtx := Tester.UseIsolatedInstance(ctx, SystemCTX)
|
domain, _, iamOwnerCtx := Tester.UseIsolatedInstance(t, ctx, SystemCTX)
|
||||||
regOrgUrl, err := url.Parse("http://" + domain + ":8080/ui/login/register/org")
|
regOrgUrl, err := url.Parse("http://" + domain + ":8080/ui/login/register/org")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
// The CSRF cookie must be sent with every request.
|
// The CSRF cookie must be sent with every request.
|
||||||
@ -68,7 +68,7 @@ func awaitPubOrgRegDisallowed(t *testing.T, ctx context.Context, client *http.Cl
|
|||||||
// awaitGetSSRGetResponse cuts the CSRF token from the response body if it exists
|
// awaitGetSSRGetResponse cuts the CSRF token from the response body if it exists
|
||||||
func awaitGetSSRGetResponse(t *testing.T, ctx context.Context, client *http.Client, parsedURL *url.URL, expectCode int) string {
|
func awaitGetSSRGetResponse(t *testing.T, ctx context.Context, client *http.Client, parsedURL *url.URL, expectCode int) string {
|
||||||
var csrfToken []byte
|
var csrfToken []byte
|
||||||
await(t, ctx, func() bool {
|
await(t, ctx, func(tt *assert.CollectT) {
|
||||||
resp, err := client.Get(parsedURL.String())
|
resp, err := client.Get(parsedURL.String())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
@ -78,18 +78,18 @@ func awaitGetSSRGetResponse(t *testing.T, ctx context.Context, client *http.Clie
|
|||||||
if hasCsrfToken {
|
if hasCsrfToken {
|
||||||
csrfToken, _, _ = bytes.Cut(after, []byte(`">`))
|
csrfToken, _, _ = bytes.Cut(after, []byte(`">`))
|
||||||
}
|
}
|
||||||
return assert.Equal(NoopAssertionT, resp.StatusCode, expectCode)
|
assert.Equal(tt, resp.StatusCode, expectCode)
|
||||||
})
|
})
|
||||||
return string(csrfToken)
|
return string(csrfToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
// awaitPostFormResponse needs a valid CSRF token to make it to the actual endpoint implementation and get the expected status code
|
// awaitPostFormResponse needs a valid CSRF token to make it to the actual endpoint implementation and get the expected status code
|
||||||
func awaitPostFormResponse(t *testing.T, ctx context.Context, client *http.Client, parsedURL *url.URL, expectCode int, csrfToken string) {
|
func awaitPostFormResponse(t *testing.T, ctx context.Context, client *http.Client, parsedURL *url.URL, expectCode int, csrfToken string) {
|
||||||
await(t, ctx, func() bool {
|
await(t, ctx, func(tt *assert.CollectT) {
|
||||||
resp, err := client.PostForm(parsedURL.String(), url.Values{
|
resp, err := client.PostForm(parsedURL.String(), url.Values{
|
||||||
"gorilla.csrf.Token": {csrfToken},
|
"gorilla.csrf.Token": {csrfToken},
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
return assert.Equal(NoopAssertionT, resp.StatusCode, expectCode)
|
assert.Equal(tt, resp.StatusCode, expectCode)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -5,20 +5,21 @@ package admin_test
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"github.com/zitadel/zitadel/internal/integration"
|
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/admin"
|
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/management"
|
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/text"
|
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/user"
|
|
||||||
"golang.org/x/text/language"
|
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/pkg/grpc/admin"
|
||||||
|
"github.com/zitadel/zitadel/pkg/grpc/management"
|
||||||
|
"github.com/zitadel/zitadel/pkg/grpc/text"
|
||||||
|
"github.com/zitadel/zitadel/pkg/grpc/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestServer_Restrictions_AllowedLanguages(t *testing.T) {
|
func TestServer_Restrictions_AllowedLanguages(t *testing.T) {
|
||||||
@ -29,11 +30,10 @@ func TestServer_Restrictions_AllowedLanguages(t *testing.T) {
|
|||||||
defaultAndAllowedLanguage = language.German
|
defaultAndAllowedLanguage = language.German
|
||||||
supportedLanguagesStr = []string{language.German.String(), language.English.String(), language.Japanese.String()}
|
supportedLanguagesStr = []string{language.German.String(), language.English.String(), language.Japanese.String()}
|
||||||
disallowedLanguage = language.Spanish
|
disallowedLanguage = language.Spanish
|
||||||
unsupportedLanguage1 = language.Afrikaans
|
unsupportedLanguage = language.Afrikaans
|
||||||
unsupportedLanguage2 = language.Albanian
|
|
||||||
)
|
)
|
||||||
|
|
||||||
domain, _, iamOwnerCtx := Tester.UseIsolatedInstance(ctx, SystemCTX)
|
domain, _, iamOwnerCtx := Tester.UseIsolatedInstance(t, ctx, SystemCTX)
|
||||||
t.Run("assumed defaults are correct", func(tt *testing.T) {
|
t.Run("assumed defaults are correct", func(tt *testing.T) {
|
||||||
tt.Run("languages are not restricted by default", func(ttt *testing.T) {
|
tt.Run("languages are not restricted by default", func(ttt *testing.T) {
|
||||||
restrictions, err := Tester.Client.Admin.GetRestrictions(iamOwnerCtx, &admin.GetRestrictionsRequest{})
|
restrictions, err := Tester.Client.Admin.GetRestrictions(iamOwnerCtx, &admin.GetRestrictionsRequest{})
|
||||||
@ -46,7 +46,7 @@ func TestServer_Restrictions_AllowedLanguages(t *testing.T) {
|
|||||||
require.Equal(ttt, language.Make(defaultLang.Language), language.English)
|
require.Equal(ttt, language.Make(defaultLang.Language), language.English)
|
||||||
})
|
})
|
||||||
tt.Run("the discovery endpoint returns all supported languages", func(ttt *testing.T) {
|
tt.Run("the discovery endpoint returns all supported languages", func(ttt *testing.T) {
|
||||||
checkDiscoveryEndpoint(ttt, domain, supportedLanguagesStr, nil)
|
awaitDiscoveryEndpoint(ttt, domain, supportedLanguagesStr, nil)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
t.Run("restricting the default language fails", func(tt *testing.T) {
|
t.Run("restricting the default language fails", func(tt *testing.T) {
|
||||||
@ -67,6 +67,14 @@ func TestServer_Restrictions_AllowedLanguages(t *testing.T) {
|
|||||||
t.Run("restricting allowed languages works", func(tt *testing.T) {
|
t.Run("restricting allowed languages works", func(tt *testing.T) {
|
||||||
setAndAwaitAllowedLanguages(iamOwnerCtx, tt, []string{defaultAndAllowedLanguage.String()})
|
setAndAwaitAllowedLanguages(iamOwnerCtx, tt, []string{defaultAndAllowedLanguage.String()})
|
||||||
})
|
})
|
||||||
|
t.Run("GetAllowedLanguage returns only the allowed languages", func(tt *testing.T) {
|
||||||
|
expectContains, expectNotContains := []string{defaultAndAllowedLanguage.String()}, []string{disallowedLanguage.String()}
|
||||||
|
adminResp, err := Tester.Client.Admin.GetAllowedLanguages(iamOwnerCtx, &admin.GetAllowedLanguagesRequest{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
langs := adminResp.GetLanguages()
|
||||||
|
assert.Condition(t, contains(langs, expectContains))
|
||||||
|
assert.Condition(t, not(contains(langs, expectNotContains)))
|
||||||
|
})
|
||||||
t.Run("setting the default language to a disallowed language fails", func(tt *testing.T) {
|
t.Run("setting the default language to a disallowed language fails", func(tt *testing.T) {
|
||||||
_, err := Tester.Client.Admin.SetDefaultLanguage(iamOwnerCtx, &admin.SetDefaultLanguageRequest{Language: disallowedLanguage.String()})
|
_, err := Tester.Client.Admin.SetDefaultLanguage(iamOwnerCtx, &admin.SetDefaultLanguageRequest{Language: disallowedLanguage.String()})
|
||||||
expectStatus, ok := status.FromError(err)
|
expectStatus, ok := status.FromError(err)
|
||||||
@ -79,29 +87,31 @@ func TestServer_Restrictions_AllowedLanguages(t *testing.T) {
|
|||||||
require.Condition(tt, contains(supported.GetLanguages(), supportedLanguagesStr))
|
require.Condition(tt, contains(supported.GetLanguages(), supportedLanguagesStr))
|
||||||
})
|
})
|
||||||
t.Run("the disallowed language is not listed in the discovery endpoint", func(tt *testing.T) {
|
t.Run("the disallowed language is not listed in the discovery endpoint", func(tt *testing.T) {
|
||||||
checkDiscoveryEndpoint(tt, domain, []string{defaultAndAllowedLanguage.String()}, []string{disallowedLanguage.String()})
|
awaitDiscoveryEndpoint(tt, domain, []string{defaultAndAllowedLanguage.String()}, []string{disallowedLanguage.String()})
|
||||||
})
|
})
|
||||||
t.Run("the login ui is rendered in the default language", func(tt *testing.T) {
|
t.Run("the login ui is rendered in the default language", func(tt *testing.T) {
|
||||||
checkLoginUILanguage(tt, domain, disallowedLanguage, defaultAndAllowedLanguage, "Allgemeine Geschäftsbedingungen und Datenschutz")
|
awaitLoginUILanguage(tt, domain, disallowedLanguage, defaultAndAllowedLanguage, "Allgemeine Geschäftsbedingungen und Datenschutz")
|
||||||
})
|
})
|
||||||
t.Run("preferred languages are not restricted by the supported languages", func(tt *testing.T) {
|
t.Run("preferred languages are not restricted by the supported languages", func(tt *testing.T) {
|
||||||
var importedUser *management.ImportHumanUserResponse
|
|
||||||
tt.Run("import user", func(ttt *testing.T) {
|
|
||||||
var err error
|
|
||||||
importedUser, err = importUser(iamOwnerCtx, unsupportedLanguage1)
|
|
||||||
require.NoError(ttt, err)
|
|
||||||
})
|
|
||||||
tt.Run("change user profile", func(ttt *testing.T) {
|
tt.Run("change user profile", func(ttt *testing.T) {
|
||||||
_, err := Tester.Client.Mgmt.UpdateHumanProfile(iamOwnerCtx, &management.UpdateHumanProfileRequest{
|
resp, err := Tester.Client.Mgmt.ListUsers(iamOwnerCtx, &management.ListUsersRequest{Queries: []*user.SearchQuery{{Query: &user.SearchQuery_UserNameQuery{UserNameQuery: &user.UserNameQuery{
|
||||||
UserId: importedUser.GetUserId(),
|
UserName: "zitadel-admin@zitadel.localhost"}},
|
||||||
FirstName: "hodor",
|
}}})
|
||||||
LastName: "hodor",
|
|
||||||
NickName: integration.RandString(5),
|
|
||||||
DisplayName: "hodor",
|
|
||||||
PreferredLanguage: unsupportedLanguage2.String(),
|
|
||||||
Gender: user.Gender_GENDER_MALE,
|
|
||||||
})
|
|
||||||
require.NoError(ttt, err)
|
require.NoError(ttt, err)
|
||||||
|
require.Len(ttt, resp.GetResult(), 1)
|
||||||
|
humanAdmin := resp.GetResult()[0]
|
||||||
|
profile := humanAdmin.GetHuman().GetProfile()
|
||||||
|
require.NotEqual(ttt, unsupportedLanguage.String(), profile.GetPreferredLanguage())
|
||||||
|
_, updateErr := Tester.Client.Mgmt.UpdateHumanProfile(iamOwnerCtx, &management.UpdateHumanProfileRequest{
|
||||||
|
PreferredLanguage: unsupportedLanguage.String(),
|
||||||
|
UserId: humanAdmin.GetId(),
|
||||||
|
FirstName: profile.GetFirstName(),
|
||||||
|
LastName: profile.GetLastName(),
|
||||||
|
NickName: profile.GetNickName(),
|
||||||
|
DisplayName: profile.GetDisplayName(),
|
||||||
|
Gender: profile.GetGender(),
|
||||||
|
})
|
||||||
|
require.NoError(ttt, updateErr)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
t.Run("custom texts are only restricted by the supported languages", func(tt *testing.T) {
|
t.Run("custom texts are only restricted by the supported languages", func(tt *testing.T) {
|
||||||
@ -137,11 +147,11 @@ func TestServer_Restrictions_AllowedLanguages(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("allowing the language makes it usable again", func(tt *testing.T) {
|
t.Run("allowing the language makes it usable again", func(tt *testing.T) {
|
||||||
tt.Run("the disallowed language is listed in the discovery endpoint again", func(ttt *testing.T) {
|
tt.Run("the previously disallowed language is listed in the discovery endpoint again", func(ttt *testing.T) {
|
||||||
checkDiscoveryEndpoint(ttt, domain, []string{defaultAndAllowedLanguage.String()}, []string{disallowedLanguage.String()})
|
awaitDiscoveryEndpoint(ttt, domain, []string{disallowedLanguage.String()}, nil)
|
||||||
})
|
})
|
||||||
tt.Run("the login ui is rendered in the allowed language", func(ttt *testing.T) {
|
tt.Run("the login ui is rendered in the previously disallowed language", func(ttt *testing.T) {
|
||||||
checkLoginUILanguage(ttt, domain, disallowedLanguage, disallowedLanguage, "Términos y condiciones")
|
awaitLoginUILanguage(ttt, domain, disallowedLanguage, disallowedLanguage, "Términos y condiciones")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -151,14 +161,14 @@ func setAndAwaitAllowedLanguages(ctx context.Context, t *testing.T, selectLangua
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
awaitCtx, awaitCancel := context.WithTimeout(ctx, 10*time.Second)
|
awaitCtx, awaitCancel := context.WithTimeout(ctx, 10*time.Second)
|
||||||
defer awaitCancel()
|
defer awaitCancel()
|
||||||
await(t, awaitCtx, func() bool {
|
await(t, awaitCtx, func(tt *assert.CollectT) {
|
||||||
restrictions, getErr := Tester.Client.Admin.GetRestrictions(awaitCtx, &admin.GetRestrictionsRequest{})
|
restrictions, getErr := Tester.Client.Admin.GetRestrictions(awaitCtx, &admin.GetRestrictionsRequest{})
|
||||||
expectLanguages := selectLanguages
|
expectLanguages := selectLanguages
|
||||||
if len(selectLanguages) == 0 {
|
if len(selectLanguages) == 0 {
|
||||||
expectLanguages = nil
|
expectLanguages = nil
|
||||||
}
|
}
|
||||||
return assert.NoError(NoopAssertionT, getErr) &&
|
assert.NoError(tt, getErr)
|
||||||
assert.Equal(NoopAssertionT, expectLanguages, restrictions.GetAllowedLanguages())
|
assert.Equal(tt, expectLanguages, restrictions.GetAllowedLanguages())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,66 +177,57 @@ func setAndAwaitDefaultLanguage(ctx context.Context, t *testing.T, lang language
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
awaitCtx, awaitCancel := context.WithTimeout(ctx, 10*time.Second)
|
awaitCtx, awaitCancel := context.WithTimeout(ctx, 10*time.Second)
|
||||||
defer awaitCancel()
|
defer awaitCancel()
|
||||||
await(t, awaitCtx, func() bool {
|
await(t, awaitCtx, func(tt *assert.CollectT) {
|
||||||
defaultLang, getErr := Tester.Client.Admin.GetDefaultLanguage(awaitCtx, &admin.GetDefaultLanguageRequest{})
|
defaultLang, getErr := Tester.Client.Admin.GetDefaultLanguage(awaitCtx, &admin.GetDefaultLanguageRequest{})
|
||||||
return assert.NoError(NoopAssertionT, getErr) &&
|
assert.NoError(tt, getErr)
|
||||||
assert.Equal(NoopAssertionT, lang.String(), defaultLang.GetLanguage())
|
assert.Equal(tt, lang.String(), defaultLang.GetLanguage())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func importUser(ctx context.Context, preferredLanguage language.Tag) (*management.ImportHumanUserResponse, error) {
|
func awaitDiscoveryEndpoint(t *testing.T, domain string, containsUILocales, notContainsUILocales []string) {
|
||||||
random := integration.RandString(5)
|
awaitCtx, awaitCancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
return Tester.Client.Mgmt.ImportHumanUser(ctx, &management.ImportHumanUserRequest{
|
defer awaitCancel()
|
||||||
UserName: "integration-test-user_" + random,
|
await(t, awaitCtx, func(tt *assert.CollectT) {
|
||||||
Profile: &management.ImportHumanUserRequest_Profile{
|
req, err := http.NewRequestWithContext(awaitCtx, http.MethodGet, "http://"+domain+":8080/.well-known/openid-configuration", nil)
|
||||||
FirstName: "hodor",
|
require.NoError(tt, err)
|
||||||
LastName: "hodor",
|
resp, err := http.DefaultClient.Do(req)
|
||||||
NickName: "hodor",
|
require.NoError(tt, err)
|
||||||
PreferredLanguage: preferredLanguage.String(),
|
require.Equal(tt, http.StatusOK, resp.StatusCode)
|
||||||
},
|
body, err := io.ReadAll(resp.Body)
|
||||||
Email: &management.ImportHumanUserRequest_Email{
|
defer func() {
|
||||||
Email: random + "@hodor.hodor",
|
require.NoError(tt, resp.Body.Close())
|
||||||
IsEmailVerified: true,
|
}()
|
||||||
},
|
require.NoError(tt, err)
|
||||||
PasswordChangeRequired: false,
|
doc := struct {
|
||||||
Password: "Password1!",
|
UILocalesSupported []string `json:"ui_locales_supported"`
|
||||||
|
}{}
|
||||||
|
require.NoError(tt, json.Unmarshal(body, &doc))
|
||||||
|
if containsUILocales != nil {
|
||||||
|
assert.Condition(tt, contains(doc.UILocalesSupported, containsUILocales))
|
||||||
|
}
|
||||||
|
if notContainsUILocales != nil {
|
||||||
|
assert.Condition(tt, not(contains(doc.UILocalesSupported, notContainsUILocales)))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkDiscoveryEndpoint(t *testing.T, domain string, containsUILocales, notContainsUILocales []string) {
|
func awaitLoginUILanguage(t *testing.T, domain string, acceptLanguage language.Tag, expectLang language.Tag, containsText string) {
|
||||||
resp, err := http.Get("http://" + domain + ":8080/.well-known/openid-configuration")
|
awaitCtx, awaitCancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
require.NoError(t, err)
|
defer awaitCancel()
|
||||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
await(t, awaitCtx, func(tt *assert.CollectT) {
|
||||||
body, err := io.ReadAll(resp.Body)
|
req, err := http.NewRequestWithContext(awaitCtx, http.MethodGet, "http://"+domain+":8080/ui/login/register", nil)
|
||||||
defer func() {
|
req.Header.Set("Accept-Language", acceptLanguage.String())
|
||||||
require.NoError(t, resp.Body.Close())
|
require.NoError(t, err)
|
||||||
}()
|
resp, err := http.DefaultClient.Do(req)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
doc := struct {
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||||
UILocalesSupported []string `json:"ui_locales_supported"`
|
body, err := io.ReadAll(resp.Body)
|
||||||
}{}
|
defer func() {
|
||||||
require.NoError(t, json.Unmarshal(body, &doc))
|
require.NoError(t, resp.Body.Close())
|
||||||
if containsUILocales != nil {
|
}()
|
||||||
assert.Condition(NoopAssertionT, contains(doc.UILocalesSupported, containsUILocales))
|
require.NoError(t, err)
|
||||||
}
|
assert.Containsf(t, string(body), containsText, "login ui language is in "+expectLang.String())
|
||||||
if notContainsUILocales != nil {
|
})
|
||||||
assert.Condition(NoopAssertionT, not(contains(doc.UILocalesSupported, notContainsUILocales)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkLoginUILanguage(t *testing.T, domain string, acceptLanguage language.Tag, expectLang language.Tag, containsText string) {
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://"+domain+":8080/ui/login/register", nil)
|
|
||||||
req.Header.Set("Accept-Language", acceptLanguage.String())
|
|
||||||
require.NoError(t, err)
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
defer func() {
|
|
||||||
require.NoError(t, resp.Body.Close())
|
|
||||||
}()
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Containsf(t, string(body), containsText, "login ui language is in "+expectLang.String())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We would love to use assert.Contains here, but it doesn't work with slices of strings
|
// We would love to use assert.Contains here, but it doesn't work with slices of strings
|
||||||
|
@ -17,8 +17,6 @@ import (
|
|||||||
var (
|
var (
|
||||||
AdminCTX, SystemCTX context.Context
|
AdminCTX, SystemCTX context.Context
|
||||||
Tester *integration.Tester
|
Tester *integration.Tester
|
||||||
// NoopAssertionT is useful in combination with assert.Eventuallyf to use testify assertions in a callback
|
|
||||||
NoopAssertionT = new(noopAssertionT)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
@ -36,17 +34,17 @@ func TestMain(m *testing.M) {
|
|||||||
}())
|
}())
|
||||||
}
|
}
|
||||||
|
|
||||||
func await(t *testing.T, ctx context.Context, cb func() bool) {
|
func await(t *testing.T, ctx context.Context, cb func(*assert.CollectT)) {
|
||||||
deadline, ok := ctx.Deadline()
|
deadline, ok := ctx.Deadline()
|
||||||
require.True(t, ok, "context must have deadline")
|
require.True(t, ok, "context must have deadline")
|
||||||
assert.Eventuallyf(
|
require.EventuallyWithT(
|
||||||
t,
|
t,
|
||||||
func() bool {
|
func(tt *assert.CollectT) {
|
||||||
defer func() {
|
defer func() {
|
||||||
// Panics are not recovered and don't mark the test as failed, so we need to do that ourselves
|
// Panics are not recovered and don't mark the test as failed, so we need to do that ourselves
|
||||||
require.Nil(t, recover(), "panic in await callback")
|
require.Nil(t, recover(), "panic in await callback")
|
||||||
}()
|
}()
|
||||||
return cb()
|
cb(tt)
|
||||||
},
|
},
|
||||||
time.Until(deadline),
|
time.Until(deadline),
|
||||||
100*time.Millisecond,
|
100*time.Millisecond,
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/i18n"
|
"github.com/zitadel/zitadel/internal/i18n"
|
||||||
|
|
||||||
mgmt_pb "github.com/zitadel/zitadel/pkg/grpc/management"
|
mgmt_pb "github.com/zitadel/zitadel/pkg/grpc/management"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestServer_ListInstances(t *testing.T) {
|
func TestServer_ListInstances(t *testing.T) {
|
||||||
domain, instanceID, _ := Tester.UseIsolatedInstance(CTX, SystemCTX)
|
domain, instanceID, _ := Tester.UseIsolatedInstance(t, CTX, SystemCTX)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -20,7 +20,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestServer_Limits_AuditLogRetention(t *testing.T) {
|
func TestServer_Limits_AuditLogRetention(t *testing.T) {
|
||||||
_, instanceID, iamOwnerCtx := Tester.UseIsolatedInstance(CTX, SystemCTX)
|
_, instanceID, iamOwnerCtx := Tester.UseIsolatedInstance(t, CTX, SystemCTX)
|
||||||
userID, projectID, appID, projectGrantID := seedObjects(iamOwnerCtx, t)
|
userID, projectID, appID, projectGrantID := seedObjects(iamOwnerCtx, t)
|
||||||
beforeTime := time.Now()
|
beforeTime := time.Now()
|
||||||
zeroCounts := &eventCounts{}
|
zeroCounts := &eventCounts{}
|
||||||
|
@ -23,7 +23,7 @@ import (
|
|||||||
var callURL = "http://localhost:" + integration.PortQuotaServer
|
var callURL = "http://localhost:" + integration.PortQuotaServer
|
||||||
|
|
||||||
func TestServer_QuotaNotification_Limit(t *testing.T) {
|
func TestServer_QuotaNotification_Limit(t *testing.T) {
|
||||||
_, instanceID, iamOwnerCtx := Tester.UseIsolatedInstance(CTX, SystemCTX)
|
_, instanceID, iamOwnerCtx := Tester.UseIsolatedInstance(t, CTX, SystemCTX)
|
||||||
amount := 10
|
amount := 10
|
||||||
percent := 50
|
percent := 50
|
||||||
percentAmount := amount * percent / 100
|
percentAmount := amount * percent / 100
|
||||||
@ -67,7 +67,7 @@ func TestServer_QuotaNotification_Limit(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestServer_QuotaNotification_NoLimit(t *testing.T) {
|
func TestServer_QuotaNotification_NoLimit(t *testing.T) {
|
||||||
_, instanceID, iamOwnerCtx := Tester.UseIsolatedInstance(CTX, SystemCTX)
|
_, instanceID, iamOwnerCtx := Tester.UseIsolatedInstance(t, CTX, SystemCTX)
|
||||||
amount := 10
|
amount := 10
|
||||||
percent := 50
|
percent := 50
|
||||||
percentAmount := amount * percent / 100
|
percentAmount := amount * percent / 100
|
||||||
@ -149,7 +149,7 @@ func awaitNotification(t *testing.T, bodies chan []byte, unit quota.Unit, percen
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestServer_AddAndRemoveQuota(t *testing.T) {
|
func TestServer_AddAndRemoveQuota(t *testing.T) {
|
||||||
_, instanceID, _ := Tester.UseIsolatedInstance(CTX, SystemCTX)
|
_, instanceID, _ := Tester.UseIsolatedInstance(t, CTX, SystemCTX)
|
||||||
|
|
||||||
got, err := Tester.Client.System.SetQuota(SystemCTX, &system.SetQuotaRequest{
|
got, err := Tester.Client.System.SetQuota(SystemCTX, &system.SetQuotaRequest{
|
||||||
InstanceId: instanceID,
|
InstanceId: instanceID,
|
||||||
|
@ -927,6 +927,12 @@ func (s *Server) VerifyClient(ctx context.Context, r *op.Request[op.ClientCreden
|
|||||||
if client.State != domain.AppStateActive {
|
if client.State != domain.AppStateActive {
|
||||||
return nil, oidc.ErrInvalidClient().WithDescription("client is not active")
|
return nil, oidc.ErrInvalidClient().WithDescription("client is not active")
|
||||||
}
|
}
|
||||||
|
if client.Settings == nil {
|
||||||
|
client.Settings = &query.OIDCSettings{
|
||||||
|
AccessTokenLifetime: s.defaultAccessTokenLifetime,
|
||||||
|
IdTokenLifetime: s.defaultIdTokenLifetime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch client.AuthMethodType {
|
switch client.AuthMethodType {
|
||||||
case domain.OIDCAuthMethodTypeBasic, domain.OIDCAuthMethodTypePost:
|
case domain.OIDCAuthMethodTypeBasic, domain.OIDCAuthMethodTypePost:
|
||||||
|
@ -92,11 +92,11 @@ func (c *Client) RestrictAdditionalAccessTokenScopes() func(scopes []string) []s
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) AccessTokenLifetime() time.Duration {
|
func (c *Client) AccessTokenLifetime() time.Duration {
|
||||||
return c.client.AccessTokenLifetime
|
return c.client.Settings.AccessTokenLifetime
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) IDTokenLifetime() time.Duration {
|
func (c *Client) IDTokenLifetime() time.Duration {
|
||||||
return c.client.IDTokenLifetime
|
return c.client.Settings.IdTokenLifetime
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) AccessTokenType() op.AccessTokenType {
|
func (c *Client) AccessTokenType() op.AccessTokenType {
|
||||||
|
@ -122,19 +122,21 @@ func NewServer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
server := &Server{
|
server := &Server{
|
||||||
LegacyServer: op.NewLegacyServer(provider, endpoints(config.CustomEndpoints)),
|
LegacyServer: op.NewLegacyServer(provider, endpoints(config.CustomEndpoints)),
|
||||||
features: config.Features,
|
features: config.Features,
|
||||||
repo: repo,
|
repo: repo,
|
||||||
query: query,
|
query: query,
|
||||||
command: command,
|
command: command,
|
||||||
keySet: newKeySet(context.TODO(), time.Hour, query.GetActivePublicKeyByID),
|
keySet: newKeySet(context.TODO(), time.Hour, query.GetActivePublicKeyByID),
|
||||||
defaultLoginURL: fmt.Sprintf("%s%s?%s=", login.HandlerPrefix, login.EndpointLogin, login.QueryAuthRequestID),
|
defaultLoginURL: fmt.Sprintf("%s%s?%s=", login.HandlerPrefix, login.EndpointLogin, login.QueryAuthRequestID),
|
||||||
defaultLoginURLV2: config.DefaultLoginURLV2,
|
defaultLoginURLV2: config.DefaultLoginURLV2,
|
||||||
defaultLogoutURLV2: config.DefaultLogoutURLV2,
|
defaultLogoutURLV2: config.DefaultLogoutURLV2,
|
||||||
fallbackLogger: fallbackLogger,
|
defaultAccessTokenLifetime: config.DefaultAccessTokenLifetime,
|
||||||
hashAlg: crypto.NewBCrypt(10), // as we are only verifying in oidc, the cost is already part of the hash string and the config here is irrelevant.
|
defaultIdTokenLifetime: config.DefaultIdTokenLifetime,
|
||||||
signingKeyAlgorithm: config.SigningKeyAlgorithm,
|
fallbackLogger: fallbackLogger,
|
||||||
assetAPIPrefix: assets.AssetAPI(externalSecure),
|
hashAlg: crypto.NewBCrypt(10), // as we are only verifying in oidc, the cost is already part of the hash string and the config here is irrelevant.
|
||||||
|
signingKeyAlgorithm: config.SigningKeyAlgorithm,
|
||||||
|
assetAPIPrefix: assets.AssetAPI(externalSecure),
|
||||||
}
|
}
|
||||||
metricTypes := []metrics.MetricType{metrics.MetricTypeRequestCount, metrics.MetricTypeStatusCode, metrics.MetricTypeTotalCount}
|
metricTypes := []metrics.MetricType{metrics.MetricTypeRequestCount, metrics.MetricTypeStatusCode, metrics.MetricTypeTotalCount}
|
||||||
server.Handler = op.RegisterLegacyServer(server, op.WithHTTPMiddleware(
|
server.Handler = op.RegisterLegacyServer(server, op.WithHTTPMiddleware(
|
||||||
|
@ -3,6 +3,7 @@ package oidc
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
"github.com/zitadel/oidc/v3/pkg/oidc"
|
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||||
@ -27,9 +28,11 @@ type Server struct {
|
|||||||
command *command.Commands
|
command *command.Commands
|
||||||
keySet *keySetCache
|
keySet *keySetCache
|
||||||
|
|
||||||
defaultLoginURL string
|
defaultLoginURL string
|
||||||
defaultLoginURLV2 string
|
defaultLoginURLV2 string
|
||||||
defaultLogoutURLV2 string
|
defaultLogoutURLV2 string
|
||||||
|
defaultAccessTokenLifetime time.Duration
|
||||||
|
defaultIdTokenLifetime time.Duration
|
||||||
|
|
||||||
fallbackLogger *slog.Logger
|
fallbackLogger *slog.Logger
|
||||||
hashAlg crypto.HashAlgorithm
|
hashAlg crypto.HashAlgorithm
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
crewjam_saml "github.com/crewjam/saml"
|
crewjam_saml "github.com/crewjam/saml"
|
||||||
"github.com/muhlemmer/gu"
|
"github.com/muhlemmer/gu"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
"github.com/zitadel/oidc/v3/pkg/oidc"
|
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||||
@ -59,7 +60,7 @@ func newClient(cc *grpc.ClientConn) Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tester) UseIsolatedInstance(iamOwnerCtx, systemCtx context.Context) (primaryDomain, instanceId string, authenticatedIamOwnerCtx context.Context) {
|
func (t *Tester) UseIsolatedInstance(tt *testing.T, iamOwnerCtx, systemCtx context.Context) (primaryDomain, instanceId string, authenticatedIamOwnerCtx context.Context) {
|
||||||
primaryDomain = RandString(5) + ".integration.localhost"
|
primaryDomain = RandString(5) + ".integration.localhost"
|
||||||
instance, err := t.Client.System.CreateInstance(systemCtx, &system.CreateInstanceRequest{
|
instance, err := t.Client.System.CreateInstance(systemCtx, &system.CreateInstanceRequest{
|
||||||
InstanceName: "testinstance",
|
InstanceName: "testinstance",
|
||||||
@ -80,7 +81,27 @@ func (t *Tester) UseIsolatedInstance(iamOwnerCtx, systemCtx context.Context) (pr
|
|||||||
t.Users.Set(instanceId, IAMOwner, &User{
|
t.Users.Set(instanceId, IAMOwner, &User{
|
||||||
Token: instance.GetPat(),
|
Token: instance.GetPat(),
|
||||||
})
|
})
|
||||||
return primaryDomain, instanceId, t.WithInstanceAuthorization(iamOwnerCtx, IAMOwner, instanceId)
|
newCtx := t.WithInstanceAuthorization(iamOwnerCtx, IAMOwner, instanceId)
|
||||||
|
// the following serves two purposes:
|
||||||
|
// 1. it ensures that the instance is ready to be used
|
||||||
|
// 2. it enables a normal login with the default admin user credentials
|
||||||
|
require.EventuallyWithT(tt, func(collectT *assert.CollectT) {
|
||||||
|
_, importErr := t.Client.Mgmt.ImportHumanUser(newCtx, &mgmt.ImportHumanUserRequest{
|
||||||
|
UserName: "zitadel-admin@zitadel.localhost",
|
||||||
|
Email: &mgmt.ImportHumanUserRequest_Email{
|
||||||
|
Email: "zitadel-admin@zitadel.localhost",
|
||||||
|
IsEmailVerified: true,
|
||||||
|
},
|
||||||
|
Password: "Password1!",
|
||||||
|
Profile: &mgmt.ImportHumanUserRequest_Profile{
|
||||||
|
FirstName: "hodor",
|
||||||
|
LastName: "hodor",
|
||||||
|
NickName: "hodor",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assert.NoError(collectT, importErr)
|
||||||
|
}, 2*time.Minute, 100*time.Millisecond, "instance not ready")
|
||||||
|
return primaryDomain, instanceId, newCtx
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Tester) CreateHumanUser(ctx context.Context) *user.AddHumanUserResponse {
|
func (s *Tester) CreateHumanUser(ctx context.Context) *user.AddHumanUserResponse {
|
||||||
|
@ -21,7 +21,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestServer_QuotaNotification_Limit(t *testing.T) {
|
func TestServer_QuotaNotification_Limit(t *testing.T) {
|
||||||
_, instanceID, iamOwnerCtx := Tester.UseIsolatedInstance(CTX, SystemCTX)
|
_, instanceID, iamOwnerCtx := Tester.UseIsolatedInstance(t, CTX, SystemCTX)
|
||||||
amount := 10
|
amount := 10
|
||||||
percent := 50
|
percent := 50
|
||||||
percentAmount := amount * percent / 100
|
percentAmount := amount * percent / 100
|
||||||
@ -67,7 +67,7 @@ func TestServer_QuotaNotification_Limit(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestServer_QuotaNotification_NoLimit(t *testing.T) {
|
func TestServer_QuotaNotification_NoLimit(t *testing.T) {
|
||||||
_, instanceID, iamOwnerCtx := Tester.UseIsolatedInstance(CTX, SystemCTX)
|
_, instanceID, iamOwnerCtx := Tester.UseIsolatedInstance(t, CTX, SystemCTX)
|
||||||
amount := 10
|
amount := 10
|
||||||
percent := 50
|
percent := 50
|
||||||
percentAmount := amount * percent / 100
|
percentAmount := amount * percent / 100
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestServer_TelemetryPushMilestones(t *testing.T) {
|
func TestServer_TelemetryPushMilestones(t *testing.T) {
|
||||||
primaryDomain, instanceID, iamOwnerCtx := Tester.UseIsolatedInstance(CTX, SystemCTX)
|
primaryDomain, instanceID, iamOwnerCtx := Tester.UseIsolatedInstance(t, CTX, SystemCTX)
|
||||||
t.Log("testing against instance with primary domain", primaryDomain)
|
t.Log("testing against instance with primary domain", primaryDomain)
|
||||||
awaitMilestone(t, Tester.MilestoneChan, primaryDomain, "InstanceCreated")
|
awaitMilestone(t, Tester.MilestoneChan, primaryDomain, "InstanceCreated")
|
||||||
project, err := Tester.Client.Mgmt.AddProject(iamOwnerCtx, &management.AddProjectRequest{Name: "integration"})
|
project, err := Tester.Client.Mgmt.AddProject(iamOwnerCtx, &management.AddProjectRequest{Name: "integration"})
|
||||||
|
@ -29,18 +29,18 @@ keys as (
|
|||||||
group by identifier
|
group by identifier
|
||||||
),
|
),
|
||||||
settings as (
|
settings as (
|
||||||
select instance_id, access_token_lifetime, id_token_lifetime
|
select instance_id, json_build_object('access_token_lifetime', access_token_lifetime, 'id_token_lifetime', id_token_lifetime) as settings
|
||||||
from projections.oidc_settings2
|
from projections.oidc_settings2
|
||||||
where aggregate_id = $1
|
where aggregate_id = $1
|
||||||
and instance_id = $1
|
and instance_id = $1
|
||||||
)
|
)
|
||||||
|
|
||||||
select row_to_json(r) as client from (
|
select row_to_json(r) as client from (
|
||||||
select c.*, r.project_role_keys, k.public_keys, s.access_token_lifetime, s.id_token_lifetime
|
select c.*, r.project_role_keys, k.public_keys, s.settings
|
||||||
from client c
|
from client c
|
||||||
left join roles r on r.project_id = c.project_id
|
left join roles r on r.project_id = c.project_id
|
||||||
left join keys k on k.client_id = c.client_id
|
left join keys k on k.client_id = c.client_id
|
||||||
join settings s on s.instance_id = s.instance_id
|
left join settings s on s.instance_id = s.instance_id
|
||||||
) r;
|
) r;
|
||||||
|
|
||||||
--execute q('230690539048009730', '236647088211951618@tests', true);
|
--execute q('230690539048009730', '236647088211951618@tests', true);
|
@ -37,8 +37,7 @@ type OIDCClient struct {
|
|||||||
PublicKeys map[string][]byte `json:"public_keys,omitempty"`
|
PublicKeys map[string][]byte `json:"public_keys,omitempty"`
|
||||||
ProjectID string `json:"project_id,omitempty"`
|
ProjectID string `json:"project_id,omitempty"`
|
||||||
ProjectRoleKeys []string `json:"project_role_keys,omitempty"`
|
ProjectRoleKeys []string `json:"project_role_keys,omitempty"`
|
||||||
AccessTokenLifetime time.Duration `json:"access_token_lifetime,omitempty"`
|
Settings *OIDCSettings `json:"settings,omitempty"`
|
||||||
IDTokenLifetime time.Duration `json:"id_token_lifetime,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:embed embed/oidc_client_by_id.sql
|
//go:embed embed/oidc_client_by_id.sql
|
||||||
|
@ -24,6 +24,8 @@ var (
|
|||||||
testdataOidcClientPublic string
|
testdataOidcClientPublic string
|
||||||
//go:embed testdata/oidc_client_secret.json
|
//go:embed testdata/oidc_client_secret.json
|
||||||
testdataOidcClientSecret string
|
testdataOidcClientSecret string
|
||||||
|
//go:embed testdata/oidc_client_no_settings.json
|
||||||
|
testdataOidcClientNoSettings string
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestQueries_GetOIDCClientByID(t *testing.T) {
|
func TestQueries_GetOIDCClientByID(t *testing.T) {
|
||||||
@ -81,8 +83,10 @@ low2kyJov38V4Uk2I8kuXpLcnrpw5Tio2ooiUE27b0vHZqBKOei9Uo88qCrn3EKx
|
|||||||
ProjectID: "236645808328409090",
|
ProjectID: "236645808328409090",
|
||||||
PublicKeys: map[string][]byte{"236647201860747266": []byte(pubkey)},
|
PublicKeys: map[string][]byte{"236647201860747266": []byte(pubkey)},
|
||||||
ProjectRoleKeys: []string{"role1", "role2"},
|
ProjectRoleKeys: []string{"role1", "role2"},
|
||||||
AccessTokenLifetime: 43200000000000,
|
Settings: &OIDCSettings{
|
||||||
IDTokenLifetime: 43200000000000,
|
AccessTokenLifetime: 43200000000000,
|
||||||
|
IdTokenLifetime: 43200000000000,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -110,8 +114,10 @@ low2kyJov38V4Uk2I8kuXpLcnrpw5Tio2ooiUE27b0vHZqBKOei9Uo88qCrn3EKx
|
|||||||
PublicKeys: nil,
|
PublicKeys: nil,
|
||||||
ProjectID: "236645808328409090",
|
ProjectID: "236645808328409090",
|
||||||
ProjectRoleKeys: []string{"role1", "role2"},
|
ProjectRoleKeys: []string{"role1", "role2"},
|
||||||
AccessTokenLifetime: 43200000000000,
|
Settings: &OIDCSettings{
|
||||||
IDTokenLifetime: 43200000000000,
|
AccessTokenLifetime: 43200000000000,
|
||||||
|
IdTokenLifetime: 43200000000000,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -143,8 +149,43 @@ low2kyJov38V4Uk2I8kuXpLcnrpw5Tio2ooiUE27b0vHZqBKOei9Uo88qCrn3EKx
|
|||||||
PublicKeys: nil,
|
PublicKeys: nil,
|
||||||
ProjectID: "236645808328409090",
|
ProjectID: "236645808328409090",
|
||||||
ProjectRoleKeys: []string{"role1", "role2"},
|
ProjectRoleKeys: []string{"role1", "role2"},
|
||||||
AccessTokenLifetime: 43200000000000,
|
Settings: &OIDCSettings{
|
||||||
IDTokenLifetime: 43200000000000,
|
AccessTokenLifetime: 43200000000000,
|
||||||
|
IdTokenLifetime: 43200000000000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no oidc settings",
|
||||||
|
mock: mockQuery(expQuery, cols, []driver.Value{testdataOidcClientNoSettings}, "instanceID", "clientID", true),
|
||||||
|
want: &OIDCClient{
|
||||||
|
InstanceID: "239520764275982338",
|
||||||
|
AppID: "239520764276441090",
|
||||||
|
State: domain.AppStateActive,
|
||||||
|
ClientID: "239520764779364354@zitadel",
|
||||||
|
ClientSecret: nil,
|
||||||
|
RedirectURIs: []string{
|
||||||
|
"http://test2-qucuh5.localhost:9000/ui/console/auth/callback",
|
||||||
|
"http://test.localhost.com:9000/ui/console/auth/callback"},
|
||||||
|
ResponseTypes: []domain.OIDCResponseType{0},
|
||||||
|
GrantTypes: []domain.OIDCGrantType{0},
|
||||||
|
ApplicationType: domain.OIDCApplicationTypeUserAgent,
|
||||||
|
AuthMethodType: domain.OIDCAuthMethodTypeNone,
|
||||||
|
PostLogoutRedirectURIs: []string{
|
||||||
|
"http://test2-qucuh5.localhost:9000/ui/console/signedout",
|
||||||
|
"http://test.localhost.com:9000/ui/console/signedout",
|
||||||
|
},
|
||||||
|
IsDevMode: true,
|
||||||
|
AccessTokenType: domain.OIDCTokenTypeBearer,
|
||||||
|
AccessTokenRoleAssertion: false,
|
||||||
|
IDTokenRoleAssertion: false,
|
||||||
|
IDTokenUserinfoAssertion: false,
|
||||||
|
ClockSkew: 0,
|
||||||
|
AdditionalOrigins: nil,
|
||||||
|
PublicKeys: nil,
|
||||||
|
ProjectID: "239520764276178946",
|
||||||
|
ProjectRoleKeys: nil,
|
||||||
|
Settings: nil,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -69,10 +69,10 @@ type OIDCSettings struct {
|
|||||||
ResourceOwner string
|
ResourceOwner string
|
||||||
Sequence uint64
|
Sequence uint64
|
||||||
|
|
||||||
AccessTokenLifetime time.Duration
|
AccessTokenLifetime time.Duration `json:"access_token_lifetime,omitempty"`
|
||||||
IdTokenLifetime time.Duration
|
IdTokenLifetime time.Duration `json:"id_token_lifetime,omitempty"`
|
||||||
RefreshTokenIdleExpiration time.Duration
|
RefreshTokenIdleExpiration time.Duration `json:"refresh_token_idle_expiration,omitempty"`
|
||||||
RefreshTokenExpiration time.Duration
|
RefreshTokenExpiration time.Duration `json:"refresh_token_expiration,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) OIDCSettingsByAggID(ctx context.Context, aggregateID string) (settings *OIDCSettings, err error) {
|
func (q *Queries) OIDCSettingsByAggID(ctx context.Context, aggregateID string) (settings *OIDCSettings, err error) {
|
||||||
|
6
internal/query/testdata/oidc_client_jwt.json
vendored
6
internal/query/testdata/oidc_client_jwt.json
vendored
@ -22,6 +22,8 @@
|
|||||||
"public_keys": {
|
"public_keys": {
|
||||||
"236647201860747266": "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFB\nT0NBUThBTUlJQkNnS0NBUUVBMnVmQUwxYjcyYkl5MWFyK1dzNmIKR29oSkpRRkI3ZGZSYXBEcWVx\nTThVa3A2Q1ZkUHpxL3BPejF2aUFxNTB5eldaSnJ5Risyd3NoRkFLR0Y5QTIvQgoyWWY5YkpYUFov\nS2JrRnJZVDNOVHZZRGt2bGFTVGw5bU1uenJVMjlzNDhGMVBUV0tmQitDM2FNc09FRzFCdWZWCnM2\nM3FGNG5yRVBqU2JobGpJY285RlpxNFhwcEl6aE1RMGZEZEEvK1h5Z0NKcXZ1YUwwTGliTTFLcmxV\nZG51NzEKWWVraFNKakVQbnZPaXNYSWs0SVh5d29HSU93dGp4a0R2Tkl0UXZhTVZsZHI0L2tiNnV2\nYmdkV3dxNUV3QlpYcQpsb3cya3lKb3YzOFY0VWsySThrdVhwTGNucnB3NVRpbzJvb2lVRTI3YjB2\nSFpxQktPZWk5VW84OHFDcm4zRUt4CjZRSURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0t\nLS0K"
|
"236647201860747266": "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFB\nT0NBUThBTUlJQkNnS0NBUUVBMnVmQUwxYjcyYkl5MWFyK1dzNmIKR29oSkpRRkI3ZGZSYXBEcWVx\nTThVa3A2Q1ZkUHpxL3BPejF2aUFxNTB5eldaSnJ5Risyd3NoRkFLR0Y5QTIvQgoyWWY5YkpYUFov\nS2JrRnJZVDNOVHZZRGt2bGFTVGw5bU1uenJVMjlzNDhGMVBUV0tmQitDM2FNc09FRzFCdWZWCnM2\nM3FGNG5yRVBqU2JobGpJY285RlpxNFhwcEl6aE1RMGZEZEEvK1h5Z0NKcXZ1YUwwTGliTTFLcmxV\nZG51NzEKWWVraFNKakVQbnZPaXNYSWs0SVh5d29HSU93dGp4a0R2Tkl0UXZhTVZsZHI0L2tiNnV2\nYmdkV3dxNUV3QlpYcQpsb3cya3lKb3YzOFY0VWsySThrdVhwTGNucnB3NVRpbzJvb2lVRTI3YjB2\nSFpxQktPZWk5VW84OHFDcm4zRUt4CjZRSURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0t\nLS0K"
|
||||||
},
|
},
|
||||||
"access_token_lifetime": 43200000000000,
|
"settings": {
|
||||||
"id_token_lifetime": 43200000000000
|
"access_token_lifetime": 43200000000000,
|
||||||
|
"id_token_lifetime": 43200000000000
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
30
internal/query/testdata/oidc_client_no_settings.json
vendored
Normal file
30
internal/query/testdata/oidc_client_no_settings.json
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"instance_id": "239520764275982338",
|
||||||
|
"app_id": "239520764276441090",
|
||||||
|
"client_id": "239520764779364354@zitadel",
|
||||||
|
"client_secret": null,
|
||||||
|
"redirect_uris": [
|
||||||
|
"http://test2-qucuh5.localhost:9000/ui/console/auth/callback",
|
||||||
|
"http://test.localhost.com:9000/ui/console/auth/callback"
|
||||||
|
],
|
||||||
|
"response_types": [0],
|
||||||
|
"grant_types": [0],
|
||||||
|
"application_type": 1,
|
||||||
|
"auth_method_type": 2,
|
||||||
|
"post_logout_redirect_uris": [
|
||||||
|
"http://test2-qucuh5.localhost:9000/ui/console/signedout",
|
||||||
|
"http://test.localhost.com:9000/ui/console/signedout"
|
||||||
|
],
|
||||||
|
"is_dev_mode": true,
|
||||||
|
"access_token_type": 0,
|
||||||
|
"access_token_role_assertion": false,
|
||||||
|
"id_token_role_assertion": false,
|
||||||
|
"id_token_userinfo_assertion": false,
|
||||||
|
"clock_skew": 0,
|
||||||
|
"additional_origins": null,
|
||||||
|
"project_id": "239520764276178946",
|
||||||
|
"state": 1,
|
||||||
|
"project_role_keys": null,
|
||||||
|
"public_keys": null,
|
||||||
|
"settings": null
|
||||||
|
}
|
@ -20,6 +20,8 @@
|
|||||||
"state": 1,
|
"state": 1,
|
||||||
"project_role_keys": ["role1", "role2"],
|
"project_role_keys": ["role1", "role2"],
|
||||||
"public_keys": null,
|
"public_keys": null,
|
||||||
"access_token_lifetime": 43200000000000,
|
"settings": {
|
||||||
"id_token_lifetime": 43200000000000
|
"access_token_lifetime": 43200000000000,
|
||||||
|
"id_token_lifetime": 43200000000000
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
"state": 1,
|
"state": 1,
|
||||||
"project_role_keys": ["role1", "role2"],
|
"project_role_keys": ["role1", "role2"],
|
||||||
"public_keys": null,
|
"public_keys": null,
|
||||||
"access_token_lifetime": 43200000000000,
|
"settings": {
|
||||||
"id_token_lifetime": 43200000000000
|
"access_token_lifetime": 43200000000000,
|
||||||
|
"id_token_lifetime": 43200000000000
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,7 +230,7 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (zitadel.v1.auth_option) = {
|
option (zitadel.v1.auth_option) = {
|
||||||
permission: "iam.read";
|
permission: "authenticated";
|
||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
@ -240,6 +240,22 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpc GetAllowedLanguages(GetAllowedLanguagesRequest) returns (GetAllowedLanguagesResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
get: "/languages/allowed";
|
||||||
|
};
|
||||||
|
|
||||||
|
option (zitadel.v1.auth_option) = {
|
||||||
|
permission: "authenticated";
|
||||||
|
};
|
||||||
|
|
||||||
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
|
summary: "Allowed Languages";
|
||||||
|
description: "If the languages are restricted, only those are returned. Else, all supported languages are returned."
|
||||||
|
tags: "Restrictions";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
rpc SetDefaultLanguage(SetDefaultLanguageRequest) returns (SetDefaultLanguageResponse) {
|
rpc SetDefaultLanguage(SetDefaultLanguageRequest) returns (SetDefaultLanguageResponse) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
put: "/languages/default/{language}";
|
put: "/languages/default/{language}";
|
||||||
@ -3868,6 +3884,17 @@ message GetSupportedLanguagesResponse {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//This is an empty request
|
||||||
|
message GetAllowedLanguagesRequest {}
|
||||||
|
|
||||||
|
message GetAllowedLanguagesResponse {
|
||||||
|
repeated string languages = 1 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "[\"en\", \"de\", \"it\"]"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
message SetDefaultLanguageRequest {
|
message SetDefaultLanguageRequest {
|
||||||
string language = 1 [
|
string language = 1 [
|
||||||
(validate.rules).string = {min_len: 1, max_len: 10},
|
(validate.rules).string = {min_len: 1, max_len: 10},
|
||||||
|
@ -143,10 +143,10 @@ service AuthService {
|
|||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
summary: "Supported Languages";
|
summary: "Supported Languages";
|
||||||
description: "The supported/default languages of the system will be returned by the language abbreviation."
|
description: "Use GetSupportedLanguages on the admin service instead."
|
||||||
|
deprecated: true;
|
||||||
tags: "General";
|
tags: "General";
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rpc GetMyUser(GetMyUserRequest) returns (GetMyUserResponse) {
|
rpc GetMyUser(GetMyUserRequest) returns (GetMyUserResponse) {
|
||||||
@ -996,7 +996,6 @@ message HealthzResponse {}
|
|||||||
//This is an empty request
|
//This is an empty request
|
||||||
message GetSupportedLanguagesRequest {}
|
message GetSupportedLanguagesRequest {}
|
||||||
|
|
||||||
//This is an empty response
|
|
||||||
message GetSupportedLanguagesResponse {
|
message GetSupportedLanguagesResponse {
|
||||||
repeated string languages = 1 [
|
repeated string languages = 1 [
|
||||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
@ -268,7 +268,8 @@ service ManagementService {
|
|||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
summary: "Supported Languages";
|
summary: "Supported Languages";
|
||||||
description: "The supported/default languages of the system will be returned by the language abbreviation."
|
description: "Use GetSupportedLanguages on the admin service instead."
|
||||||
|
deprecated: true;
|
||||||
tags: "General";
|
tags: "General";
|
||||||
responses: {
|
responses: {
|
||||||
key: "200"
|
key: "200"
|
||||||
|
Loading…
Reference in New Issue
Block a user