feat: manage restrictions in console (#6965)

* feat: return 404 or 409 if org reg disallowed

* fix: system limit permissions

* feat: add iam limits api

* feat: disallow public org registrations on default instance

* add integration test

* test: integration

* fix test

* docs: describe public org registrations

* avoid updating docs deps

* fix system limits integration test

* silence integration tests

* fix linting

* ignore strange linter complaints

* review

* improve reset properties naming

* redefine the api

* use restrictions aggregate

* test query

* simplify and test projection

* test commands

* fix unit tests

* move integration test

* support restrictions on default instance

* also test GetRestrictions

* self review

* lint

* abstract away resource owner

* fix tests

* configure supported languages

* fix allowed languages

* fix tests

* default lang must not be restricted

* preferred language must be allowed

* change preferred languages

* check languages everywhere

* lint

* test command side

* lint

* add integration test

* add integration test

* restrict supported ui locales

* lint

* lint

* cleanup

* lint

* allow undefined preferred language

* fix integration tests

* update main

* fix env var

* ignore linter

* ignore linter

* improve integration test config

* reduce cognitive complexity

* compile

* fix(console): switch back to saved language

* feat(API): get allowed languages

* fix(console): only make allowed languages selectable

* warn when editing not allowed languages

* feat: manage restrictions in console

* check for duplicates

* remove useless restriction checks

* review

* revert restriction renaming

* manage languages

* fix language restrictions

* lint

* generate

* allow custom texts for supported langs for now

* fix tests

* cleanup

* cleanup

* cleanup

* lint

* unsupported preferred lang is allowed

* fix integration test

* allow unsupported preferred languages

* lint

* fix languages lists

* simplify default language selection

* translate

* discard

* lint

* load languages for tests

* load languages

* lint

* cleanup

* lint

* cleanup

* get allowed only on admin

* cleanup

* reduce flakiness on very limited postgres

* simplify langSvc

* refactor according to suggestions in pr

* lint

* improve ux

* update central allowed languages

* set first allowed language as default

* readd lost translations

* disable sorting disallowed languages

* fix permissions

* lint

* selectionchange for language in msg texts

* initialize login texts

* init message texts

* lint

* fix drag and drop list styles

* start from 1

* cleanup

* prettier

* correct orgdefaultlabel

* unsubscribe

* lint

* docs: describe language settings

---------

Co-authored-by: peintnermax <max@caos.ch>
This commit is contained in:
Elio Bischof 2023-12-07 13:31:01 +01:00 committed by GitHub
parent 3842319d07
commit d639c5200a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 961 additions and 449 deletions

View File

@ -1,27 +0,0 @@
<h2>{{ 'SETTING.DEFAULTLANGUAGE' | translate }}</h2>
<div class="spinner-wr">
<mat-spinner diameter="30" *ngIf="loading" color="primary"></mat-spinner>
</div>
<cnsl-form-field class="default-language" label="Default Language" required="true">
<cnsl-label>{{ 'SETTING.DEFAULTLANGUAGE' | translate }}</cnsl-label>
<mat-select [(ngModel)]="defaultLanguage" [disabled]="(['iam.policy.write'] | hasRole | async) === false">
<mat-option *ngFor="let lang of defaultLanguageOptions" [value]="lang">
{{ lang }} - {{ 'SETTING.LANGUAGE.' + lang | translate }}
</mat-option>
</mat-select>
</cnsl-form-field>
<div class="general-btn-container">
<button
class="save-button"
(click)="savePolicy()"
color="primary"
type="submit"
mat-raised-button
[disabled]="(['iam.policy.write'] | hasRole | async) === false"
>
{{ 'ACTIONS.SAVE' | translate }}
</button>
</div>

View File

@ -1,18 +0,0 @@
.spinner-wr {
margin: 0.5rem 0;
}
.default-language {
max-width: 400px;
display: block;
}
.general-btn-container {
display: flex;
justify-content: flex-start;
margin-top: 1rem;
.save-button {
display: block;
}
}

View File

@ -1,56 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { SetDefaultLanguageResponse } from 'src/app/proto/generated/zitadel/admin_pb';
import { AdminService } from 'src/app/services/admin.service';
import { ToastService } from 'src/app/services/toast.service';
@Component({
selector: 'cnsl-general-settings',
templateUrl: './general-settings.component.html',
styleUrls: ['./general-settings.component.scss'],
})
export class GeneralSettingsComponent implements OnInit {
public defaultLanguage: string = '';
public defaultLanguageOptions: string[] = [];
public loading: boolean = false;
constructor(
private service: AdminService,
private toast: ToastService,
) {}
ngOnInit(): void {
this.fetchData();
}
private fetchData(): void {
this.service.getDefaultLanguage().then((langResp) => {
this.defaultLanguage = langResp.language;
});
this.service.getAllowedLanguages().then((supportedResp) => {
this.defaultLanguageOptions = supportedResp.languagesList;
});
}
private updateData(): Promise<SetDefaultLanguageResponse.AsObject> {
return (this.service as AdminService).setDefaultLanguage(this.defaultLanguage);
}
public savePolicy(): void {
const prom = this.updateData();
this.loading = true;
if (prom) {
prom
.then(() => {
this.toast.showInfo('POLICY.LOGIN_POLICY.SAVED', true);
this.loading = false;
setTimeout(() => {
this.fetchData();
}, 2000);
})
.catch((error) => {
this.loading = false;
this.toast.showError(error);
});
}
}
}

View File

@ -1,29 +0,0 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select';
import { TranslateModule } from '@ngx-translate/core';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { CardModule } from '../../card/card.module';
import { FormFieldModule } from '../../form-field/form-field.module';
import { GeneralSettingsComponent } from './general-settings.component';
@NgModule({
declarations: [GeneralSettingsComponent],
imports: [
CommonModule,
CardModule,
FormsModule,
MatButtonModule,
FormFieldModule,
MatProgressSpinnerModule,
MatSelectModule,
HasRolePipeModule,
TranslateModule,
],
exports: [GeneralSettingsComponent],
})
export class GeneralSettingsModule {}

View File

@ -0,0 +1,135 @@
<h2>{{ 'SETTING.LANGUAGES.TITLE' | translate }}</h2>
<div class="spinner-wr">
<mat-spinner diameter="30" *ngIf="loading" color="primary"></mat-spinner>
</div>
<div>
<form class="languages-container-wrapper">
<div class="languages-container">
<div
class="languages-list"
cdkDropList
id="allowed"
[cdkDropListConnectedTo]="['notallowed']"
(cdkDropListDropped)="drop($event)"
[cdkDropListData]="(localState$ | async)!.allowed"
>
<div class="languages-top-row">
<span class="label">{{ 'SETTING.LANGUAGES.ALLOWED' | translate }}</span>
<button
class="list-button"
[disabled]="(canWriteRestrictions$ | async) === false"
(click)="allowAll()"
mat-stroked-button
>
<div class="cnsl-action-button">
<i class="las la-object-group"></i><span>{{ 'SETTING.LANGUAGES.ALLOW_ALL' | translate }}</span>
</div>
</button>
</div>
<div class="languages-drop-target">
<div
class="card languages-box"
*ngFor="let lang of (localState$ | async)!.allowed; index as i"
[cdkDragData]="lang"
cdkDrag
[cdkDragDisabled]="(canWriteRestrictions$ | async) === false"
mat-list-item
>
<span class="index">{{ i + 1 }}</span>
<span class="locale">{{ lang }}</span>
<span class="lang">{{ 'SETTING.LANGUAGES.OPTIONS.' + lang | translate }} </span>
<span *ngIf="lang === (defaultLang$ | async)" class="state orgdefaultlabel">{{
'SETTING.LANGUAGES.DEFAULT' | translate
}}</span>
<button
(click)="$event.stopPropagation()"
*ngIf="lang !== (defaultLang$ | async) && (isRemotelyAllowed$(lang) | async) === true"
mat-icon-button
class="more-button"
[matMenuTriggerFor]="actions"
data-e2e="table-actions-button"
>
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #actions="matMenu" xPosition="before" class="language-actions-menu">
<button
menuActions
mat-menu-item
(click)="setDefaultLang(lang)"
[disabled]="(canWriteDefaultLanguage$ | async) === false"
>
{{ 'SETTING.LANGUAGES.SETASDEFAULT' | translate }}
</button>
</mat-menu>
</div>
</div>
</div>
</div>
<div class="languages-container">
<div
class="languages-list"
cdkDropList
id="notallowed"
[cdkDropListConnectedTo]="['allowed']"
(cdkDropListDropped)="drop($event)"
[cdkDropListData]="(localState$ | async)!.notAllowed"
[cdkDropListEnterPredicate]="defaultLangPredicate"
cdkDropListSortingDisabled
>
<div class="languages-top-row">
<span class="label">{{ 'SETTING.LANGUAGES.NOT_ALLOWED' | translate }}</span>
<button
class="list-button"
[disabled]="(canWriteRestrictions$ | async) === false"
(click)="disallowAll()"
mat-stroked-button
>
<div class="cnsl-action-button">
<i class="las la-object-group"></i><span>{{ 'SETTING.LANGUAGES.DISALLOW_ALL' | translate }}</span>
</div>
</button>
</div>
<div class="languages-drop-target">
<div
class="card languages-box"
*ngFor="let lang of (localState$ | async)!.notAllowed"
[cdkDragData]="lang"
cdkDrag
[cdkDragDisabled]="(canWriteRestrictions$ | async) === false"
mat-list-item
>
<span class="locale">{{ lang }}</span>
<span class="lang">{{ 'SETTING.LANGUAGES.OPTIONS.' + lang | translate }} </span>
</div>
</div>
</div>
</div>
<div class="general-btn-container">
<button
class="reset-button"
[disabled]="(canWriteRestrictions$ | async) === false"
(click)="discard()"
color="message-text-warn"
mat-stroked-button
>
<div class="cnsl-action-button">
<i class="las la-history"></i><span>{{ 'ACTIONS.UNSAVED.DIALOG.DISCARD' | translate }}</span>
</div>
</button>
<button
class="save-button"
color="primary"
type="submit"
(click)="submit()"
mat-raised-button
[disabled]="(canWriteRestrictions$ | async) === false"
>
{{ 'ACTIONS.SAVE' | translate }}
</button>
</div>
</form>
</div>

View File

@ -0,0 +1,112 @@
.languages-container-wrapper {
display: grid;
grid-template-columns: 1fr 1fr;
column-gap: 2rem;
@media only screen and (max-width: 700px) {
grid-template-columns: 1fr;
}
.languages-container {
display: inline-block;
max-width: 400px;
vertical-align: top;
width: 100%;
.spinner-wr {
margin: 0.5rem 0;
}
.default-language {
max-width: 400px;
display: block;
}
}
.general-btn-container {
display: flex;
justify-content: flex-start;
margin-top: 1rem;
.save-button {
display: block;
margin-left: 1rem;
}
}
}
.languages-list {
overflow: hidden;
display: block;
height: 100%;
.languages-top-row {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 1rem;
margin-top: 1.5rem;
.label {
margin-right: 1rem;
flex: 1;
white-space: nowrap;
}
.list-button {
white-space: nowrap;
}
}
}
.languages-box {
position: relative;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 0.5rem 0.5rem 0.5rem 2rem;
height: 56px;
cursor: move;
margin: 2px 0;
.index {
position: absolute;
top: 4px;
left: 4px;
opacity: 0.5;
font-size: 10px;
}
.locale {
width: 35px;
margin-right: 1rem;
}
.lang {
flex: 1;
}
[hoveractions] {
display: none;
}
.more-button {
height: 40px;
width: 40px;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
}
&:hover {
[hoveractions] {
display: flex;
}
}
.defaultlanglabel {
margin-left: 0.5rem;
}
}

View File

@ -1,19 +1,19 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { GeneralSettingsComponent } from './general-settings.component';
import { LanguageSettingsComponent } from './language-settings.component';
describe('GeneralSettingsComponent', () => {
let component: GeneralSettingsComponent;
let fixture: ComponentFixture<GeneralSettingsComponent>;
describe('LanguageSettingsComponent', () => {
let component: LanguageSettingsComponent;
let fixture: ComponentFixture<LanguageSettingsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [GeneralSettingsComponent],
declarations: [LanguageSettingsComponent],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(GeneralSettingsComponent);
fixture = TestBed.createComponent(LanguageSettingsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

View File

@ -0,0 +1,116 @@
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { AdminService } from 'src/app/services/admin.service';
import { ToastService } from 'src/app/services/toast.service';
import { UntypedFormBuilder } from '@angular/forms';
import { LanguagesService } from '../../../services/languages.service';
import { BehaviorSubject, concat, forkJoin, from, Observable, of, Subject, switchMap, take, takeUntil } from 'rxjs';
import { GrpcAuthService } from '../../../services/grpc-auth.service';
import { CdkDrag, CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
import { catchError, map } from 'rxjs/operators';
interface State {
allowed: string[];
notAllowed: string[];
}
@Component({
selector: 'cnsl-language-settings',
templateUrl: './language-settings.component.html',
styleUrls: ['./language-settings.component.scss'],
})
export class LanguageSettingsComponent {
public canWriteRestrictions$: Observable<boolean> = this.authService.isAllowed(['iam.restrictions.write']);
public canWriteDefaultLanguage$: Observable<boolean> = this.authService.isAllowed(['iam.write']);
public localState$ = new BehaviorSubject<State>({ allowed: [], notAllowed: [] });
public remoteState$ = new BehaviorSubject<State>({ allowed: [], notAllowed: [] });
public defaultLang$ = new BehaviorSubject<string>('');
public loading: boolean = false;
constructor(
private service: AdminService,
private toast: ToastService,
private langSvc: LanguagesService,
private authService: GrpcAuthService,
) {
const sub = forkJoin([
langSvc.allowed$.pipe(take(1)),
langSvc.notAllowed$.pipe(take(1)),
from(this.service.getDefaultLanguage()).pipe(take(1)),
]).subscribe({
next: ([allowed, notAllowed, { language: defaultLang }]) => {
this.defaultLang$.next(defaultLang);
this.remoteState$.next({ notAllowed: [...notAllowed], ...{ allowed: [...allowed] } });
this.localState$.next({ notAllowed: [...notAllowed], ...{ allowed: [...allowed] } });
},
error: this.toast.showError,
complete: () => {
sub.unsubscribe();
},
});
}
drop(event: CdkDragDrop<string[]>) {
if (event.previousContainer === event.container) {
moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
} else {
transferArrayItem(event.previousContainer.data, event.container.data, event.previousIndex, event.currentIndex);
}
}
public defaultLangPredicate = (lang: CdkDrag<string>) => {
return !!lang?.data && lang.data !== this.defaultLang$.value;
};
public isRemotelyAllowed$(lang: string): Observable<boolean> {
return this.remoteState$.pipe(map(({ allowed }) => allowed.includes(lang)));
}
public allowAll(): void {
this.localState$.next({ allowed: [...this.allLocalLangs()], notAllowed: [] });
}
public disallowAll(): void {
const disallowed = this.allLocalLangs().filter((lang) => lang !== this.defaultLang$.value);
this.localState$.next({ allowed: [this.defaultLang$.value], notAllowed: disallowed });
}
public submit(): void {
const { allowed, notAllowed } = this.localState$.value;
const sub = from(this.service.setRestrictions(undefined, allowed)).subscribe({
next: () => {
this.remoteState$.next({
allowed: [...allowed],
notAllowed: [...notAllowed],
});
this.langSvc.newAllowed(allowed);
this.toast.showInfo('SETTING.LANGUAGES.ALLOWED_SAVED', true);
},
error: this.toast.showError,
complete: () => {
sub.unsubscribe();
},
});
}
public discard(): void {
this.localState$.next(this.remoteState$.value);
}
public setDefaultLang(lang: string): void {
const sub = from(this.service.setDefaultLanguage(lang)).subscribe({
next: () => {
this.defaultLang$.next(lang);
this.toast.showInfo('SETTING.LANGUAGES.DEFAULT_SAVED', true);
},
error: this.toast.showError,
complete: () => {
sub.unsubscribe();
},
});
}
private allLocalLangs(): string[] {
return [...this.localState$.value.allowed, ...this.localState$.value.notAllowed];
}
}

View File

@ -0,0 +1,46 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select';
import { TranslateModule } from '@ngx-translate/core';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { CardModule } from '../../card/card.module';
import { FormFieldModule } from '../../form-field/form-field.module';
import { LanguageSettingsComponent } from './language-settings.component';
import { MatListModule } from '@angular/material/list';
import { MatFormFieldModule } from '@angular/material/form-field';
import { DragDropModule } from '@angular/cdk/drag-drop';
import { MatRadioModule } from '@angular/material/radio';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TableActionsModule } from '../../table-actions/table-actions.module';
import { MatMenuModule } from '@angular/material/menu';
import { MatIconModule } from '@angular/material/icon';
@NgModule({
declarations: [LanguageSettingsComponent],
imports: [
CommonModule,
CardModule,
FormsModule,
ReactiveFormsModule,
MatFormFieldModule,
MatButtonModule,
MatSelectModule,
FormFieldModule,
MatProgressSpinnerModule,
MatSelectModule,
HasRolePipeModule,
TranslateModule,
MatListModule,
DragDropModule,
MatRadioModule,
MatTooltipModule,
MatMenuModule,
MatIconModule,
],
exports: [LanguageSettingsComponent],
})
export class LanguageSettingsModule {}

View File

@ -242,14 +242,20 @@
| async) === false
"
>
{{ 'POLICY.DATA.ALLOWREGISTER' | translate }}
{{ 'POLICY.DATA.ALLOWREGISTERUSERS' | translate }}
</mat-checkbox>
</div>
<div class="login-policy-row">
<mat-checkbox
class="login-policy-toggle"
color="primary"
ngDefaultControl
[(ngModel)]="allowOrgRegistration"
*ngIf="serviceType === PolicyComponentServiceType.ADMIN"
[disabled]="(['iam.restrictions.write'] | hasRole | async) === false"
>
{{ 'POLICY.DATA.ALLOWREGISTERORGS' | translate }}
</mat-checkbox>
<!-- <ng-template #regInfo>
<cnsl-info-section class="info">
{{ 'POLICY.DATA.ALLOWREGISTER_DESC' | translate }}
</cnsl-info-section>
</ng-template> -->
</div>
<div class="login-policy-row">
<mat-checkbox
@ -428,6 +434,7 @@
type="submit"
mat-raised-button
[disabled]="
(['iam.restrictions.write'] | hasRole | async) === false &&
([
serviceType === PolicyComponentServiceType.ADMIN
? 'iam.policy.write'

View File

@ -1,8 +1,8 @@
import { Component, Injector, Input, OnInit, Type } from '@angular/core';
import { Component, Injector, Input, OnDestroy, OnInit, Type } from '@angular/core';
import { AbstractControl, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { Duration } from 'google-protobuf/google/protobuf/duration_pb';
import { take } from 'rxjs';
import { firstValueFrom, forkJoin, from, Observable, of, Subject, take } from 'rxjs';
import {
GetLoginPolicyResponse as AdminGetLoginPolicyResponse,
UpdateLoginPolicyRequest,
@ -24,19 +24,21 @@ import { InfoSectionType } from '../../info-section/info-section.component';
import { WarnDialogComponent } from '../../warn-dialog/warn-dialog.component';
import { PolicyComponentServiceType } from '../policy-component-types.enum';
import { LoginMethodComponentType } from './factor-table/factor-table.component';
import { catchError, map, takeUntil } from 'rxjs/operators';
@Component({
selector: 'cnsl-login-policy',
templateUrl: './login-policy.component.html',
styleUrls: ['./login-policy.component.scss'],
})
export class LoginPolicyComponent implements OnInit {
export class LoginPolicyComponent implements OnInit, OnDestroy {
public LoginMethodComponentType: any = LoginMethodComponentType;
public passwordlessTypes: Array<PasswordlessType> = [
PasswordlessType.PASSWORDLESS_TYPE_NOT_ALLOWED,
PasswordlessType.PASSWORDLESS_TYPE_ALLOWED,
];
public loginData?: LoginPolicy.AsObject;
public allowOrgRegistration: boolean = false;
public service!: ManagementService | AdminService;
public PolicyComponentServiceType: any = PolicyComponentServiceType;
@ -52,6 +54,8 @@ export class LoginPolicyComponent implements OnInit {
secondFactorCheckLifetime: [{ disabled: true }, [requiredValidator]],
multiFactorCheckLifetime: [{ disabled: true }, [requiredValidator]],
});
private destroy$: Subject<void> = new Subject();
constructor(
private toast: ToastService,
private injector: Injector,
@ -60,43 +64,59 @@ export class LoginPolicyComponent implements OnInit {
private dialog: MatDialog,
) {}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
public fetchData(): void {
this.getData()
.then((resp) => {
if (resp.policy) {
this.loginData = resp.policy;
this.loading = false;
const data$ = forkJoin([
this.serviceType === PolicyComponentServiceType.ADMIN
? from((this.service as AdminService).getRestrictions()).pipe(
map(({ disallowPublicOrgRegistration }) => disallowPublicOrgRegistration),
)
: of(true),
from(this.getData()),
]);
this.passwordCheckLifetime?.setValue(
this.loginData.passwordCheckLifetime?.seconds ? this.loginData.passwordCheckLifetime?.seconds / 60 / 60 : 0,
);
this.externalLoginCheckLifetime?.setValue(
this.loginData.externalLoginCheckLifetime?.seconds
? this.loginData.externalLoginCheckLifetime?.seconds / 60 / 60
: 0,
);
this.mfaInitSkipLifetime?.setValue(
this.loginData.mfaInitSkipLifetime?.seconds ? this.loginData.mfaInitSkipLifetime?.seconds / 60 / 60 : 0,
);
this.secondFactorCheckLifetime?.setValue(
this.loginData.secondFactorCheckLifetime?.seconds
? this.loginData.secondFactorCheckLifetime?.seconds / 60 / 60
: 0,
);
this.multiFactorCheckLifetime?.setValue(
this.loginData.multiFactorCheckLifetime?.seconds
? this.loginData.multiFactorCheckLifetime?.seconds / 60 / 60
: 0,
);
const sub = data$.pipe(takeUntil(this.destroy$)).subscribe({
next: ([disallowPublicOrgRegistration, resp]) => {
this.allowOrgRegistration = !disallowPublicOrgRegistration;
if (!resp.policy) {
return;
}
})
.catch((error) => {
this.toast.showError(error);
});
this.loginData = resp.policy;
this.loading = false;
this.passwordCheckLifetime?.setValue(
this.loginData.passwordCheckLifetime?.seconds ? this.loginData.passwordCheckLifetime?.seconds / 60 / 60 : 0,
);
this.externalLoginCheckLifetime?.setValue(
this.loginData.externalLoginCheckLifetime?.seconds
? this.loginData.externalLoginCheckLifetime?.seconds / 60 / 60
: 0,
);
this.mfaInitSkipLifetime?.setValue(
this.loginData.mfaInitSkipLifetime?.seconds ? this.loginData.mfaInitSkipLifetime?.seconds / 60 / 60 : 0,
);
this.secondFactorCheckLifetime?.setValue(
this.loginData.secondFactorCheckLifetime?.seconds
? this.loginData.secondFactorCheckLifetime?.seconds / 60 / 60
: 0,
);
this.multiFactorCheckLifetime?.setValue(
this.loginData.multiFactorCheckLifetime?.seconds ? this.loginData.multiFactorCheckLifetime?.seconds / 60 / 60 : 0,
);
},
error: this.toast.showError,
complete: () => {
sub.unsubscribe();
},
});
}
public ngOnInit(): void {
@ -142,7 +162,9 @@ export class LoginPolicyComponent implements OnInit {
}
}
private async updateData(): Promise<UpdateLoginPolicyResponse.AsObject> {
private async updateData(): Promise<any> {
const calls: Observable<any>[] = [];
if (this.loginData) {
switch (this.serviceType) {
case PolicyComponentServiceType.MGMT:
@ -179,7 +201,8 @@ export class LoginPolicyComponent implements OnInit {
mgmtreq.setIgnoreUnknownUsernames(this.loginData.ignoreUnknownUsernames);
mgmtreq.setDefaultRedirectUri(this.loginData.defaultRedirectUri);
return (this.service as ManagementService).addCustomLoginPolicy(mgmtreq);
calls.push(from((this.service as ManagementService).addCustomLoginPolicy(mgmtreq)));
break;
} else {
const mgmtreq = new UpdateCustomLoginPolicyRequest();
mgmtreq.setAllowExternalIdp(this.loginData.allowExternalIdp);
@ -211,7 +234,8 @@ export class LoginPolicyComponent implements OnInit {
mgmtreq.setIgnoreUnknownUsernames(this.loginData.ignoreUnknownUsernames);
mgmtreq.setDefaultRedirectUri(this.loginData.defaultRedirectUri);
return (this.service as ManagementService).updateCustomLoginPolicy(mgmtreq);
calls.push(from((this.service as ManagementService).updateCustomLoginPolicy(mgmtreq)));
break;
}
case PolicyComponentServiceType.ADMIN:
const adminreq = new UpdateLoginPolicyRequest();
@ -243,11 +267,21 @@ export class LoginPolicyComponent implements OnInit {
adminreq.setIgnoreUnknownUsernames(this.loginData.ignoreUnknownUsernames);
adminreq.setDefaultRedirectUri(this.loginData.defaultRedirectUri);
return (this.service as AdminService).updateLoginPolicy(adminreq);
calls.push(from((this.service as AdminService).setRestrictions(!this.allowOrgRegistration)));
calls.push(from((this.service as AdminService).updateLoginPolicy(adminreq)));
break;
}
} else {
return Promise.reject();
calls.push(from(Promise.reject()));
}
return firstValueFrom(
forkJoin(calls).pipe(
catchError((error, caught) => {
// We just ignore the policy not changed error!
return (error as { message: string }).message.includes('INSTANCE-5M9vdd') ? of(true) : caught;
}),
),
);
}
public savePolicy(): void {

View File

@ -6,9 +6,6 @@
[settingsList]="settingsList"
queryParam="id"
>
<ng-container *ngIf="currentSetting === 'general' && serviceType === PolicyComponentServiceType.ADMIN">
<cnsl-general-settings></cnsl-general-settings>
</ng-container>
<ng-container *ngIf="currentSetting === 'complexity'">
<cnsl-password-complexity-policy [serviceType]="serviceType"></cnsl-password-complexity-policy>
</ng-container>
@ -57,4 +54,7 @@
<ng-container *ngIf="currentSetting === 'privacypolicy'">
<cnsl-privacy-policy [serviceType]="serviceType"></cnsl-privacy-policy>
</ng-container>
<ng-container *ngIf="currentSetting === 'languages' && serviceType === PolicyComponentServiceType.ADMIN">
<cnsl-language-settings></cnsl-language-settings>
</ng-container>
</cnsl-sidenav>

View File

@ -7,7 +7,7 @@ import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.mod
import { CardModule } from '../card/card.module';
import DomainsModule from '../domains/domains.module';
import { DomainPolicyModule } from '../policies/domain-policy/domain-policy.module';
import { GeneralSettingsModule } from '../policies/general-settings/general-settings.module';
import { LanguageSettingsModule } from '../policies/language-settings/language-settings.module';
import { IdpSettingsModule } from '../policies/idp-settings/idp-settings.module';
import { LoginPolicyModule } from '../policies/login-policy/login-policy.module';
import { LoginTextsPolicyModule } from '../policies/login-texts/login-texts.module';
@ -36,7 +36,7 @@ import { SettingsListComponent } from './settings-list.component';
PasswordComplexityPolicyModule,
PasswordLockoutPolicyModule,
PrivateLabelingPolicyModule,
GeneralSettingsModule,
LanguageSettingsModule,
NotificationPolicyModule,
IdpSettingsModule,
PrivacyPolicyModule,

View File

@ -1,9 +1,9 @@
import { PolicyComponentServiceType } from '../policies/policy-component-types.enum';
import { SidenavSetting } from '../sidenav/sidenav.component';
export const GENERAL: SidenavSetting = {
id: 'general',
i18nKey: 'SETTINGS.LIST.GENERAL',
export const LANGUAGES: SidenavSetting = {
id: 'languages',
i18nKey: 'SETTINGS.LIST.LANGUAGES',
requiredRoles: {
[PolicyComponentServiceType.ADMIN]: ['iam.policy.read'],
},

View File

@ -10,7 +10,7 @@ import {
BRANDING,
COMPLEXITY,
DOMAIN,
GENERAL,
LANGUAGES,
IDP,
LOCKOUT,
LOGIN,
@ -34,7 +34,6 @@ export class InstanceSettingsComponent implements OnInit, OnDestroy {
public id: string = '';
public PolicyComponentServiceType: any = PolicyComponentServiceType;
public defaultSettingsList: SidenavSetting[] = [
GENERAL,
// notifications
// { showWarn: true, ...NOTIFICATIONS },
NOTIFICATIONS,
@ -53,6 +52,7 @@ export class InstanceSettingsComponent implements OnInit, OnDestroy {
LOGINTEXTS,
// others
PRIVACYPOLICY,
LANGUAGES,
OIDC,
SECRETS,
SECURITY,

View File

@ -128,6 +128,7 @@ import {
GetPrivacyPolicyResponse,
GetProviderByIDRequest,
GetProviderByIDResponse,
GetRestrictionsResponse,
GetSecretGeneratorRequest,
GetSecretGeneratorResponse,
GetSecurityPolicyRequest,
@ -194,6 +195,7 @@ import {
ResetCustomDomainPolicyToDefaultResponse,
ResetCustomLoginTextsToDefaultRequest,
ResetCustomLoginTextsToDefaultResponse,
SelectLanguages,
SetCustomLoginTextsRequest,
SetCustomLoginTextsResponse,
SetDefaultDomainClaimedMessageTextRequest,
@ -218,6 +220,8 @@ import {
SetDefaultVerifyPhoneMessageTextResponse,
SetDefaultVerifySMSOTPMessageTextRequest,
SetDefaultVerifySMSOTPMessageTextResponse,
SetRestrictionsRequest,
SetRestrictionsResponse,
SetSecurityPolicyRequest,
SetSecurityPolicyResponse,
SetUpOrgRequest,
@ -830,6 +834,29 @@ export class AdminService {
return this.grpcService.admin.setDefaultLanguage(req, null).then((resp) => resp.toObject());
}
/* restrictions */
public getRestrictions(): Promise<GetRestrictionsResponse.AsObject> {
const req = new GetDefaultLanguageRequest();
return this.grpcService.admin.getRestrictions(req, null).then((resp) => resp.toObject());
}
public setRestrictions(
disallowPublicOrgRegistration?: boolean,
allowedLanguages?: string[],
): Promise<SetRestrictionsResponse.AsObject> {
const req = new SetRestrictionsRequest();
if (disallowPublicOrgRegistration !== undefined) {
req.setDisallowPublicOrgRegistration(disallowPublicOrgRegistration);
}
if (allowedLanguages !== undefined) {
const langs = new SelectLanguages();
langs.setListList(allowedLanguages);
req.setAllowedLanguages(langs);
}
return this.grpcService.admin.setRestrictions(req, null).then((resp) => resp.toObject());
}
/* notification policy */
public getNotificationPolicy(): Promise<GetNotificationPolicyResponse.AsObject> {

View File

@ -36,7 +36,6 @@ export class LanguagesService {
});
}
// TODO: call this in https://github.com/zitadel/zitadel/pull/6965
public newAllowed(languages: string[]) {
this.allowedSubject$.next(languages);
}

View File

@ -36,7 +36,7 @@ export class ToastService {
}
}
public showError(error: any | string, isGrpc: boolean = true, i18nKey: boolean = false): void {
public showError = (error: any | string, isGrpc: boolean = true, i18nKey: boolean = false) => {
if (isGrpc) {
const { message, code, metadata } = error;
if (code !== 16) {
@ -57,7 +57,7 @@ export class ToastService {
} else {
this.showMessage(error as string, '', false);
}
}
};
private showMessage(message: string, action: string, success: boolean): Observable<void> {
const ref = this.snackBar.open(message, action, {

View File

@ -1014,7 +1014,7 @@
"DESCRIPTION": "Тези настройки разширяват и презаписват настройките на вашия екземпляр."
},
"LIST": {
"GENERAL": "Общ",
"LANGUAGES": "Езици",
"LOGIN": "Поведение при влизане и сигурност",
"LOCKOUT": "Блокиране",
"COMPLEXITY": "Сложност на паролата",
@ -1043,22 +1043,32 @@
}
},
"SETTING": {
"DEFAULTLANGUAGE": "Език по подразбиране",
"LANGUAGE": {
"de": "Deutsch",
"en": "Английски",
"es": "Español",
"fr": "Français",
"it": "Италиано",
"ja": "日本語",
"pl": "Полски",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
"LANGUAGES": {
"TITLE": "Настройки на езика",
"DEFAULT": "Език по подразбиране",
"ALLOWED": "Разрешени езици",
"NOT_ALLOWED": "Не са разрешени езици",
"ALLOW_ALL": "Разрешете всички езици",
"DISALLOW_ALL": "Забранете всички езици",
"SETASDEFAULT": "Задайте като език по подразбиране",
"DEFAULT_SAVED": "Езикът по подразбиране е запазен успешно.",
"ALLOWED_SAVED": "Разрешените езици са запазени успешно.",
"OPTIONS": {
"de": "Deutsch",
"en": "Английски",
"es": "Español",
"fr": "Français",
"it": "Италиано",
"ja": "日本語",
"pl": "Полски",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
}
},
"SMTP": {
"TITLE": "SMTP настройки",
@ -1371,7 +1381,8 @@
"SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN": "SMTP адресът на изпращача съвпада с домейна на екземпляра",
"ALLOWUSERNAMEPASSWORD": "Потребителско име Паролата е разрешена",
"ALLOWEXTERNALIDP": "Допуска се външен IDP",
"ALLOWREGISTER": "Регистрацията е разрешена",
"ALLOWREGISTERUSERS": "Регистрирайте потребители",
"ALLOWREGISTERORGS": "Регистрирайте организации",
"ALLOWUSERNAMEPASSWORD_DESC": "Разрешено е конвенционалното влизане с потребителско име и парола.",
"ALLOWEXTERNALIDP_DESC": "Входът е разрешен за основните доставчици на самоличност",
"ALLOWREGISTER_DESC": "Ако опцията е избрана, в входа се появява допълнителна стъпка за регистрация на потребител.",

View File

@ -1021,7 +1021,7 @@
"DESCRIPTION": "Tato nastavení rozšiřují a přepisují nastavení vaší instance."
},
"LIST": {
"GENERAL": "Obecné",
"LANGUAGES": "Jazyky",
"LOGIN": "Chování při přihlášení a bezpečnost",
"LOCKOUT": "Blokování",
"COMPLEXITY": "Složitost hesla",
@ -1050,22 +1050,32 @@
}
},
"SETTING": {
"DEFAULTLANGUAGE": "Výchozí jazyk",
"LANGUAGE": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
"LANGUAGES": {
"TITLE": "Nastavení jazyka",
"DEFAULT": "Výchozí jazyk",
"ALLOWED": "Povolené jazyky",
"NOT_ALLOWED": "Nepovolené jazyky",
"ALLOW_ALL": "Povolit všechny jazyky",
"DISALLOW_ALL": "Zakázat všechny jazyky",
"SETASDEFAULT": "Nastavit jako výchozí",
"DEFAULT_SAVED": "Výchozí jazyk byl úspěšně nastaven.",
"ALLOWED_SAVED": "Povolené jazyky byly úspěšně nastaveny.",
"OPTIONS": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
}
},
"SMTP": {
"TITLE": "Nastavení SMTP",
@ -1379,6 +1389,8 @@
"ALLOWUSERNAMEPASSWORD": "Povoleno uživatelské jméno a heslo",
"ALLOWEXTERNALIDP": "Povoleno externí IDP",
"ALLOWREGISTER": "Registrace povolena",
"ALLOWREGISTERUSERS": "Registrace uživatelů povolena",
"ALLOWREGISTERORGS": "Registrace organizací povolena",
"ALLOWUSERNAMEPASSWORD_DESC": "Je povoleno klasické přihlášení s uživatelským jménem a heslem.",
"ALLOWEXTERNALIDP_DESC": "Přihlášení je povoleno pro níže uvedené poskytovatele identity.",
"ALLOWREGISTER_DESC": "Pokud je možnost vybrána, objeví se při přihlášení další krok pro registraci uživatele.",

View File

@ -1020,7 +1020,7 @@
"DESCRIPTION": "Diese Einstellungen erweitern bzw. überschreiben die Einstellungen Ihrer Instanz."
},
"LIST": {
"GENERAL": "Allgemein",
"LANGUAGES": "Sprachen",
"LOGIN": "Loginverhalten und Sicherheit",
"LOCKOUT": "Sperrmechanismen",
"COMPLEXITY": "Passwordkomplexität",
@ -1049,22 +1049,32 @@
}
},
"SETTING": {
"DEFAULTLANGUAGE": "Standardsprache",
"LANGUAGE": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
"LANGUAGES": {
"TITLE": "Spracheinstellungen",
"DEFAULT": "Standardsprache",
"ALLOWED": "Erlaubte Sprachen",
"NOT_ALLOWED": "Nicht erlaubte Sprachen",
"ALLOW_ALL": "Alle Sprachen erlauben",
"DISALLOW_ALL": "Alle Sprachen verbieten",
"SETASDEFAULT": "Als Standard setzen",
"DEFAULT_SAVED": "Standard Sprache gesetzt",
"ALLOWED_SAVED": "Erlaubte Sprachen gesetzt",
"OPTIONS": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
}
},
"SMTP": {
"TITLE": "SMTP Einstellungen",
@ -1377,7 +1387,8 @@
"SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN": "SMTP Sender Adresse entspricht Instanzdomain",
"ALLOWUSERNAMEPASSWORD": "Benutzername Passwort erlaubt",
"ALLOWEXTERNALIDP": "Externer IDP erlaubt",
"ALLOWREGISTER": "Registrieren erlaubt",
"ALLOWREGISTERUSERS": "Benutzerregistrierung erlaubt",
"ALLOWREGISTERORGS": "Organisationsregistrierung erlaubt",
"ALLOWUSERNAMEPASSWORD_DESC": "Der konventionelle Login mit Benutzername und Passwort wird erlaubt.",
"ALLOWEXTERNALIDP_DESC": "Der Login wird für die darunter liegenden Identitätsanbieter erlaubt.",
"ALLOWREGISTER_DESC": "Ist die Option gewählt, erscheint im Login ein zusätzlicher Schritt zum Registrieren eines Benutzers.",

View File

@ -1021,7 +1021,7 @@
"DESCRIPTION": "These settings extend and overwrite your instance settings."
},
"LIST": {
"GENERAL": "General",
"LANGUAGES": "Languages",
"LOGIN": "Login Behavior and Security",
"LOCKOUT": "Lockout",
"COMPLEXITY": "Password complexity",
@ -1050,22 +1050,32 @@
}
},
"SETTING": {
"DEFAULTLANGUAGE": "Default Language",
"LANGUAGE": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
"LANGUAGES": {
"TITLE": "Language Settings",
"DEFAULT": "Default Language",
"ALLOWED": "Allowed Languages",
"NOT_ALLOWED": "Not Allowed Languages",
"ALLOW_ALL": "Allow All",
"DISALLOW_ALL": "Disallow All",
"SETASDEFAULT": "Set as Default Language",
"DEFAULT_SAVED": "Default Language saved",
"ALLOWED_SAVED": "Allowed Languages saved",
"OPTIONS": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
}
},
"SMTP": {
"TITLE": "SMTP Settings",
@ -1378,7 +1388,8 @@
"SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN": "SMTP Sender Address matches Instance Domain",
"ALLOWUSERNAMEPASSWORD": "Username Password allowed",
"ALLOWEXTERNALIDP": "External IDP allowed",
"ALLOWREGISTER": "Register allowed",
"ALLOWREGISTERUSERS": "Register users allowed",
"ALLOWREGISTERORGS": "Register organizations allowed",
"ALLOWUSERNAMEPASSWORD_DESC": "The conventional login with user name and password is allowed.",
"ALLOWEXTERNALIDP_DESC": "The login is allowed for the underlying identity providers",
"ALLOWREGISTER_DESC": "If the option is selected, an additional step for registering a user appears in the login.",

View File

@ -1021,7 +1021,7 @@
"DESCRIPTION": "Estas configuraciones amplían y sobrescriben tus configuraciones de instancia."
},
"LIST": {
"GENERAL": "General",
"LANGUAGES": "Idiomas",
"LOGIN": "Comportamiento del inicio de sesión y de la seguridad",
"LOCKOUT": "Bloqueo",
"COMPLEXITY": "Complejidad de contraseña",
@ -1050,22 +1050,32 @@
}
},
"SETTING": {
"DEFAULTLANGUAGE": "Idioma por defecto",
"LANGUAGE": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
"LANGUAGES": {
"TITLE": "Ajustes de idioma",
"DEFAULT": "Idioma predeterminado",
"ALLOWED": "Idiomas permitidos",
"NOT_ALLOWED": "Idiomas no permitidos",
"ALLOW_ALL": "Permitir todos los idiomas",
"DISALLOW_ALL": "No permitir ningún idioma",
"SETASDEFAULT": "Establecer como idioma predeterminado",
"DEFAULT_SAVED": "Idioma predeterminado guardado",
"ALLOWED_SAVED": "Idiomas permitidos guardados",
"OPTIONS": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
}
},
"SMTP": {
"TITLE": "Ajustes SMTP",
@ -1378,7 +1388,8 @@
"SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN": "La dirección del remitente SMTP coincide con el dominio de la instancia",
"ALLOWUSERNAMEPASSWORD": "Nombre de usuario y contraseña permitido",
"ALLOWEXTERNALIDP": "Permitido IDP externo",
"ALLOWREGISTER": "Registro permitido",
"ALLOWREGISTERUSERS": "Registro de usuarios permitido",
"ALLOWREGISTERORGS": "Registro de organizaciones permitido",
"ALLOWUSERNAMEPASSWORD_DESC": "El inicio de sesión convencional con nombre de usuario y contraseña está permitido.",
"ALLOWEXTERNALIDP_DESC": "El inicio de sesión está permitido para los proveedores de identidad subyacentes",
"ALLOWREGISTER_DESC": "Si esta opción es seleccionada, aparece un paso adicional durante el inicio de sesión para registrar un usuario.",

View File

@ -1020,7 +1020,7 @@
"DESCRIPTION": "Ces paramètres étendent et remplacent les paramètres de votre instance."
},
"LIST": {
"GENERAL": "Général",
"LANGUAGES": "Langues",
"LOGIN": "Comportement de connexion et sécurité",
"LOCKOUT": "Verrouillage",
"COMPLEXITY": "Complexité du mot de passe",
@ -1049,22 +1049,32 @@
}
},
"SETTING": {
"DEFAULTLANGUAGE": "Langue par défaut",
"LANGUAGE": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
"LANGUAGES": {
"TITLE": "Paramètres de langue",
"DEFAULT": "Langue par défaut",
"ALLOWED": "Langues autorisées",
"NOT_ALLOWED": "Langues non autorisées",
"ALLOW_ALL": "Autoriser toutes les langues",
"DISALLOW_ALL": "Interdire toutes les langues",
"SETASDEFAULT": "Définir comme langue par défaut",
"DEFAULT_SAVED": "Langue par défaut enregistrée",
"ALLOWED_SAVED": "Langues autorisées enregistrées",
"OPTIONS": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
}
},
"SMTP": {
"TITLE": "Paramètres SMTP",
@ -1377,7 +1387,8 @@
"SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN": "L'adresse de l'expéditeur SMTP correspond au domaine de l'instance",
"ALLOWUSERNAMEPASSWORD": "Nom d'utilisateur Mot de passe autorisé",
"ALLOWEXTERNALIDP": "IDP externe autorisé",
"ALLOWREGISTER": "Enregistrement autorisé",
"ALLOWREGISTERUSERS": "Enregistrer les utilisateurs autorisés",
"ALLOWREGISTERORGS": "Enregistrer les organisations autorisées",
"ALLOWUSERNAMEPASSWORD_DESC": "La connexion classique avec nom d'utilisateur et mot de passe est autorisée.",
"ALLOWEXTERNALIDP_DESC": "La connexion est autorisée pour les fournisseurs d'identité sous-jacents",
"ALLOWREGISTER_DESC": "Si l'option est sélectionnée, une étape supplémentaire pour l'enregistrement d'un utilisateur apparaît dans la connexion.",

View File

@ -1020,7 +1020,7 @@
"DESCRIPTION": "Queste impostazioni si applicheranno alla organizzazione corrente."
},
"LIST": {
"GENERAL": "Generale",
"LANGUAGES": "Lingue",
"LOGIN": "Comportamento login e sicurezza",
"LOCKOUT": "Meccanismi di bloccaggio",
"COMPLEXITY": "Complessità della password",
@ -1049,22 +1049,32 @@
}
},
"SETTING": {
"DEFAULTLANGUAGE": "Lingua standard",
"LANGUAGE": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
"LANGUAGES": {
"TITLE": "Impostazioni della lingua",
"DEFAULT": "Lingua predefinita",
"ALLOWED": "Lingue consentite",
"NOT_ALLOWED": "Lingue non consentite",
"ALLOW_ALL": "Consenti tutte le lingue",
"DISALLOW_ALL": "Non consentire tutte le lingue",
"SETASDEFAULT": "Imposta come lingua predefinita",
"DEFAULT_SAVED": "Lingua predefinita salvata",
"ALLOWED_SAVED": "Lingue consentite salvate",
"OPTIONS": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
}
},
"SMTP": {
"TITLE": "Impostazioni SMTP",
@ -1377,7 +1387,8 @@
"SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN": "L'indirizzo mittente SMTP corrisponde al dominio dell'istanza",
"ALLOWUSERNAMEPASSWORD": "Autenticazione classica con password consentita",
"ALLOWEXTERNALIDP": "IDP esterno consentito",
"ALLOWREGISTER": "Registrazione consentita",
"ALLOWREGISTERUSERS": "Registrazione utenti consentita",
"ALLOWREGISTERORGS": "Registrazione organizzazioni consentita",
"ALLOWUSERNAMEPASSWORD_DESC": "Autenticazione classica con nome utente e password \u00e8 permessa.",
"ALLOWEXTERNALIDP_DESC": "Il login \u00e8 permesso per gli IDP sottostanti",
"ALLOWREGISTER_DESC": "Se l'opzione \u00e8 selezionata, nel login apparirà un passo aggiuntivo per la registrazione di un utente.",

View File

@ -1021,7 +1021,7 @@
"DESCRIPTION": "これらの設定は、インスタンス設定を拡張・上書きします。"
},
"LIST": {
"GENERAL": "全般",
"LANGUAGES": "一般設定",
"LOGIN": "ログイン動作とセキュリティ",
"LOCKOUT": "ロックアウト",
"COMPLEXITY": "パスワードの複雑さ",
@ -1050,22 +1050,32 @@
}
},
"SETTING": {
"DEFAULTLANGUAGE": "デフォルトの言語",
"LANGUAGE": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
"LANGUAGES": {
"TITLE": "言語設定",
"DEFAULT": "デフォルト言語",
"ALLOWED": "許可された言語",
"NOT_ALLOWED": "許可されていない言語",
"ALLOW_ALL": "すべての言語を許可する",
"DISALLOW_ALL": "すべての言語を許可しない",
"SETASDEFAULT": "デフォルト言語として設定する",
"DEFAULT_SAVED": "デフォルト言語が保存されました",
"ALLOWED_SAVED": "許可された言語が保存されました",
"OPTIONS": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
}
},
"SMTP": {
"TITLE": "SMTP設定",
@ -1373,7 +1383,8 @@
"SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN": "SMTP送信者アドレスはインスタンスドメインに一致しています",
"ALLOWUSERNAMEPASSWORD": "ユーザー名とパスワードを許可",
"ALLOWEXTERNALIDP": "外部IDPを許可",
"ALLOWREGISTER": "登録を許可",
"ALLOWREGISTERUSERS": "ユーザーの登録を許可",
"ALLOWREGISTERORGS": "組織の登録を許可",
"ALLOWUSERNAMEPASSWORD_DESC": "ユーザー名とパスワードを使用した従来のログインを許可します。",
"ALLOWEXTERNALIDP_DESC": "基礎となるIDプロバイダーにログインを許可します。",
"ALLOWREGISTER_DESC": "このオプションが選択されている場合、ユーザーを登録するための追加のステップがログインに表示されます。",

View File

@ -1022,7 +1022,7 @@
"DESCRIPTION": "Овие подесувања ги прошируваат и препишуваат подесувањата на вашата инстанца."
},
"LIST": {
"GENERAL": "Генерални",
"LANGUAGES": "Општо",
"LOGIN": "Правила и безбедност при најава",
"LOCKOUT": "Забрана на пристап",
"COMPLEXITY": "Сложеност на лозинката",
@ -1051,22 +1051,32 @@
}
},
"SETTING": {
"DEFAULTLANGUAGE": "Стандарден јазик",
"LANGUAGE": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
"LANGUAGES": {
"TITLE": "Подесувања за јазик",
"DEFAULT": "Стандарден јазик",
"ALLOWED": "Дозволени јазици",
"NOT_ALLOWED": "Не дозволени јазици",
"ALLOW_ALL": "Дозволи ги сите јазици",
"DISALLOW_ALL": "Забрани ги сите јазици",
"SETASDEFAULT": "Постави како стандарден јазик",
"DEFAULT_SAVED": "Стандардниот јазик е зачуван",
"ALLOWED_SAVED": "Дозволените јазици се зачувани",
"OPTIONS": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
}
},
"SMTP": {
"TITLE": "SMTP подесувања",
@ -1379,7 +1389,8 @@
"SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN": "SMTP адресата на испраќачот се поклопува со доменот на инстанцата",
"ALLOWUSERNAMEPASSWORD": "Дозволено корисничко име и лозинка",
"ALLOWEXTERNALIDP": "Дозволен надворешен IDP",
"ALLOWREGISTER": "Дозволена регистрација",
"ALLOWREGISTERUSERS": "Дозволена регистрација на корисници",
"ALLOWREGISTERORGS": "Дозволена регистрација на организации",
"ALLOWUSERNAMEPASSWORD_DESC": "Дозволена е конвенционална најава со корисничко име и лозинка.",
"ALLOWEXTERNALIDP_DESC": "Најавата е дозволена за поддржуваните IDPs",
"ALLOWREGISTER_DESC": "Доколку е избрана опцијата, се прикажува дополнителен чекор за регистрирање на корисник во најавата.",

View File

@ -1021,7 +1021,7 @@
"DESCRIPTION": "Deze instellingen breiden uw instantie instellingen uit en overschrijven deze."
},
"LIST": {
"GENERAL": "Algemeen",
"LANGUAGES": "Talen",
"LOGIN": "Login Gedrag en Beveiliging",
"LOCKOUT": "Lockout",
"COMPLEXITY": "Wachtwoord complexiteit",
@ -1050,22 +1050,32 @@
}
},
"SETTING": {
"DEFAULTLANGUAGE": "Standaard Taal",
"LANGUAGE": {
"de": "Deutsch",
"en": "Engels",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portugees",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
"LANGUAGES": {
"TITLE": "Taalinstellingen",
"DEFAULT": "Standaardtaal",
"ALLOWED": "Toegestane Talen",
"NOT_ALLOWED": "Niet Toegestane Talen",
"ALLOW_ALL": "Sta alle talen toe",
"DISALLOW_ALL": "Sta geen talen toe",
"SETASDEFAULT": "Stel in als standaardtaal",
"DEFAULT_SAVED": "Standaardtaal opgeslagen",
"ALLOWED_SAVED": "Toegestane talen opgeslagen",
"OPTIONS": {
"de": "Deutsch",
"en": "Engels",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portugees",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
}
},
"SMTP": {
"TITLE": "SMTP Instellingen",
@ -1378,7 +1388,8 @@
"SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN": "SMTP Afzender Adres komt overeen met Instantie Domein",
"ALLOWUSERNAMEPASSWORD": "Gebruikersnaam Wachtwoord toegestaan",
"ALLOWEXTERNALIDP": "Externe IDP toegestaan",
"ALLOWREGISTER": "Registratie toegestaan",
"ALLOWREGISTERUSERS": "Gebruikersregistratie toegestaan",
"ALLOWREGISTERORGS": "Organisatieregistratie toegestaan",
"ALLOWUSERNAMEPASSWORD_DESC": "De conventionele login met gebruikersnaam en wachtwoord is toegestaan.",
"ALLOWEXTERNALIDP_DESC": "De login is toegestaan voor de onderliggende identiteitsproviders",
"ALLOWREGISTER_DESC": "Als de optie is geselecteerd, verschijnt er een extra stap voor het registreren van een gebruiker in het login proces.",

View File

@ -1020,7 +1020,7 @@
"DESCRIPTION": "Te ustawienia rozszerzają i nadpisują ustawienia instancji."
},
"LIST": {
"GENERAL": "Ogólne",
"LANGUAGES": "Języki",
"LOGIN": "Zachowanie logowania i bezpieczeństwo",
"LOCKOUT": "Blokada",
"COMPLEXITY": "Złożoność hasła",
@ -1049,22 +1049,32 @@
}
},
"SETTING": {
"DEFAULTLANGUAGE": "Domyślny język",
"LANGUAGE": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
"LANGUAGES": {
"TITLE": "Ustawienia językowe",
"DEFAULT": "Domyślny język",
"ALLOWED": "Dozwolone języki",
"NOT_ALLOWED": "Niedozwolone języki",
"ALLOW_ALL": "Zezwól na wszystkie języki",
"DISALLOW_ALL": "Zabroń wszystkich języków",
"SETASDEFAULT": "Ustaw jako domyślny język",
"DEFAULT_SAVED": "Domyślny język zapisany",
"ALLOWED_SAVED": "Dozwolone języki zapisane",
"OPTIONS": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
}
},
"SMTP": {
"TITLE": "Ustawienia SMTP",
@ -1377,7 +1387,8 @@
"SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN": "Adres nadawcy SMTP pasuje do domeny instancji",
"ALLOWUSERNAMEPASSWORD": "Zezwól na użycie nazwy użytkownika i hasła",
"ALLOWEXTERNALIDP": "Zezwól na zewnętrznego dostawcę tożsamości",
"ALLOWREGISTER": "Zezwól na rejestrację",
"ALLOWREGISTERUSERS": "Zezwól na rejestrację użytkowników",
"ALLOWREGISTERORGS": "Zezwól na rejestrację organizacji",
"ALLOWUSERNAMEPASSWORD_DESC": "Zwykłe logowanie za pomocą nazwy użytkownika i hasła jest dozwolone.",
"ALLOWEXTERNALIDP_DESC": "Logowanie jest dozwolone dla dostawców tożsamości podstawowych",
"ALLOWREGISTER_DESC": "Jeśli ta opcja jest zaznaczona, pojawi się dodatkowy krok rejestracji użytkownika w procesie logowania.",

View File

@ -1022,7 +1022,7 @@
"DESCRIPTION": "Essas configurações estendem e sobrescrevem as configurações da sua instância."
},
"LIST": {
"GENERAL": "Geral",
"LANGUAGES": "Idiomas",
"LOGIN": "Comportamento de Login e Segurança",
"LOCKOUT": "Bloqueio",
"COMPLEXITY": "Complexidade de Senha",
@ -1051,22 +1051,32 @@
}
},
"SETTING": {
"DEFAULTLANGUAGE": "Idioma padrão",
"LANGUAGE": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
"LANGUAGES": {
"TITLE": "Configurações de Idioma",
"DEFAULT": "Idioma Padrão",
"ALLOWED": "Idiomas Permitidos",
"NOT_ALLOWED": "Idiomas Não Permitidos",
"ALLOW_ALL": "Permitir Todos os Idiomas",
"DISALLOW_ALL": "Não Permitir Todos os Idiomas",
"SETASDEFAULT": "Definir como Idioma Padrão",
"DEFAULT_SAVED": "Idioma Padrão salvo",
"ALLOWED_SAVED": "Idiomas Permitidos salvos",
"OPTIONS": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
}
},
"SMTP": {
"TITLE": "Configurações SMTP",
@ -1379,7 +1389,8 @@
"SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN": "O endereço do remetente do SMTP corresponde ao domínio da Instância",
"ALLOWUSERNAMEPASSWORD": "Permitir usuário e senha",
"ALLOWEXTERNALIDP": "Permitir provedor de ID externo",
"ALLOWREGISTER": "Permitir registro",
"ALLOWREGISTERUSERS": "Permitir registro de usuários",
"ALLOWREGISTERORGS": "Permitir registro de organizações",
"ALLOWUSERNAMEPASSWORD_DESC": "O login convencional com nome de usuário e senha é permitido.",
"ALLOWEXTERNALIDP_DESC": "O login é permitido para os provedores de identidade subjacentes",
"ALLOWREGISTER_DESC": "Se a opção estiver selecionada, uma etapa adicional para registrar um usuário aparecerá no login.",

View File

@ -1017,7 +1017,7 @@
"DESCRIPTION": "Эти настройки расширяют и перезаписывают настройки вашего экземпляра."
},
"LIST": {
"GENERAL": "Общие",
"LANGUAGES": "Языки",
"LOGIN": "Поведение при входе и безопасность",
"LOCKOUT": "Блокировка",
"COMPLEXITY": "Сложность пароля",
@ -1043,21 +1043,31 @@
}
},
"SETTING": {
"DEFAULTLANGUAGE": "Язык по умолчанию",
"LANGUAGE": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"ru": "Русский",
"nl": "Nederlands"
"LANGUAGES": {
"TITLE": "Настройки языка",
"DEFAULT": "Язык по умолчанию",
"ALLOWED": "Разрешенные языки",
"NOT_ALLOWED": "Неразрешенные языки",
"ALLOW_ALL": "Разрешить все языки",
"DISALLOW_ALL": "Запретить все языки",
"SETASDEFAULT": "Установить как язык по умолчанию",
"DEFAULT_SAVED": "Язык по умолчанию сохранен",
"ALLOWED_SAVED": "Разрешенные языки сохранены",
"OPTIONS": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"ru": "Русский",
"nl": "Nederlands"
}
},
"SMTP": {
"TITLE": "Настройки SMTP",
@ -1364,7 +1374,8 @@
"SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN": "Адрес отправителя SMTP соответствует домену экземпляра",
"ALLOWUSERNAMEPASSWORD": "Вход с паролем разрешен",
"ALLOWEXTERNALIDP": "Внешний поставщик разрешен",
"ALLOWREGISTER": "Регистрация разрешена",
"ALLOWREGISTERUSERS": "Регистрация пользователей разрешена",
"ALLOWREGISTERORGS": "Регистрация организаций разрешена",
"ALLOWUSERNAMEPASSWORD_DESC": "Разрешен обычный вход в систему с использованием имени пользователя и паролем.",
"ALLOWEXTERNALIDP_DESC": "Вход разрешен для базовых поставщиков удостоверений.",
"ALLOWREGISTER_DESC": "Если опция выбрана, в логине появляется дополнительный шаг для регистрации пользователя.",

View File

@ -1020,7 +1020,7 @@
"DESCRIPTION": "这些设置将扩展或覆盖您的实例设置。"
},
"LIST": {
"GENERAL": "通用",
"LANGUAGES": "语言",
"LOGIN": "登录行为和安全",
"LOCKOUT": "安全锁策略",
"COMPLEXITY": "密码复杂性",
@ -1049,22 +1049,32 @@
}
},
"SETTING": {
"DEFAULTLANGUAGE": "默认语言",
"LANGUAGE": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
"LANGUAGES": {
"TITLE": "语言设置",
"DEFAULT": "默认语言",
"ALLOWED": "允许的语言",
"NOT_ALLOWED": "不允许的语言",
"ALLOW_ALL": "允许所有语言",
"DISALLOW_ALL": "禁止所有语言",
"SETASDEFAULT": "设置为默认语言",
"DEFAULT_SAVED": "默认语言已保存",
"ALLOWED_SAVED": "允许的语言已保存",
"OPTIONS": {
"de": "Deutsch",
"en": "English",
"es": "Español",
"fr": "Français",
"it": "Italiano",
"ja": "日本語",
"pl": "Polski",
"zh": "简体中文",
"bg": "Български",
"pt": "Portuguese",
"mk": "Македонски",
"cs": "Čeština",
"ru": "Русский",
"nl": "Nederlands"
}
},
"SMTP": {
"TITLE": "SMTP 设置",
@ -1376,7 +1386,8 @@
"SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN": "SMTP 发件人地址与实例域名匹配",
"ALLOWUSERNAMEPASSWORD": "允许用户名密码",
"ALLOWEXTERNALIDP": "允许外部身份提供者",
"ALLOWREGISTER": "允许注册",
"ALLOWREGISTERUSERS": "允许注册用户",
"ALLOWREGISTERORGS": "允许注册组织",
"ALLOWUSERNAMEPASSWORD_DESC": "允许使用用户名和密码进行登录。",
"ALLOWEXTERNALIDP_DESC": "允许外部身份提供者进行登录",
"ALLOWREGISTER_DESC": "如果选择了该选项,登录中会出现一个用于注册用户的附加步骤。",

View File

@ -49,6 +49,17 @@ ZITADEL is available in the following languages
- Russian (ru)
- Dutch (nl)
A language is displayed based on your agent's language header. The default language is English.
A language is displayed based on your agent's language header.
If a users language header doesn't match any of the supported or [restricted](#restrict-languages) languages, the instances default language will be used.
If you need support for a specific language we highly encourage you to [contribute translation files](https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md) for the missing language.
## Restrict Languages
If you only want to enable a subset of the supported languages, you can configure the languages you'd like to allow using the [restrictions API](./restrictions.md).
The login UI and notification messages are only rendered in one of the allowed languages and fallback to the instances default language.
Also, the instances OIDC discovery endpoint will only list the allowed languages in the *ui_locales_supported* field.
All language settings are also configurable in the consoles *Languages* instance settings.
![Languages](/img/guides/console/languages.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB