feat(console): set email verified on org creation, disable svg upload, password page optimizations (#4149)

* feat: set email verified on org creation

* catch svg files and throw error

* password changes

* passwordpage

* rm log

* it

* fr

* localhost env

* Update fr.json

Co-authored-by: Fabi <38692350+hifabienne@users.noreply.github.com>
Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
Max Peintner 2022-08-26 09:34:44 +02:00 committed by GitHub
parent e39d82c031
commit cbb5e90bac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 201 additions and 127 deletions

View File

@ -17,6 +17,15 @@ const routes: Routes = [
path: 'signedout',
loadChildren: () => import('./pages/signedout/signedout.module').then((m) => m.SignedoutModule),
},
{
path: 'orgs/create',
component: OrgCreateComponent,
canActivate: [AuthGuard, RoleGuard],
data: {
roles: ['(org.create)?(iam.write)?'],
},
loadChildren: () => import('./pages/org-create/org-create.module').then((m) => m.OrgCreateModule),
},
{
path: 'orgs',
loadChildren: () => import('./pages/org-list/org-list.module').then((m) => m.OrgListModule),
@ -52,15 +61,6 @@ const routes: Routes = [
roles: ['iam.read', 'iam.write'],
},
},
{
path: 'org/create',
component: OrgCreateComponent,
canActivate: [AuthGuard, RoleGuard],
data: {
roles: ['(org.create)?(iam.write)?'],
},
loadChildren: () => import('./pages/org-create/org-create.module').then((m) => m.OrgCreateModule),
},
{
path: 'org',
loadChildren: () => import('./pages/orgs/org.module').then((m) => m.OrgModule),

View File

@ -28,7 +28,7 @@
<a
class="nav-item"
[routerLinkActiveOptions]="{ exact: true }"
[routerLinkActiveOptions]="{ exact: false }"
[routerLinkActive]="['active']"
[routerLink]="['/orgs']"
>

View File

@ -56,7 +56,7 @@
</ng-template>
<ng-template cnslHasRole [hasRole]="['org.create', 'iam.write']">
<button mat-button [routerLink]="['/org/create']" (click)="closedCard.emit()">
<button mat-button [routerLink]="['/orgs/create']" (click)="closedCard.emit()">
<mat-icon class="avatar">add</mat-icon>
{{ 'MENU.NEWORG' | translate }}
</button>

View File

@ -8,10 +8,10 @@
</cnsl-filter-org>
<ng-template actions cnslHasRole [hasRole]="['org.create', 'iam.write']">
<a [routerLink]="['/org', 'create']" color="primary" mat-raised-button class="cnsl-action-button">
<a [routerLink]="['/orgs', 'create']" color="primary" mat-raised-button class="cnsl-action-button">
<mat-icon class="icon">add</mat-icon>
<span>{{ 'ACTIONS.NEW' | translate }}</span>
<cnsl-action-keys (actionTriggered)="gotoRouterLink(['/org', 'create'])"> </cnsl-action-keys>
<cnsl-action-keys (actionTriggered)="gotoRouterLink(['/orgs', 'create'])"> </cnsl-action-keys>
</a>
</ng-template>

View File

@ -112,7 +112,9 @@ export class PrivateLabelingPolicyComponent implements OnInit, OnDestroy {
const file = filelist.item(0);
if (file) {
if (file.size > MAX_ALLOWED_SIZE) {
this.toast.showInfo('POLICY.PRIVATELABELING.MAXSIZEEXCEEDED', true);
this.toast.showError('POLICY.PRIVATELABELING.MAXSIZEEXCEEDED', false, true);
} else if (file.type === 'image/svg+xml') {
this.toast.showError('POLICY.PRIVATELABELING.NOSVGSUPPORTED', false, true);
} else {
const formData = new FormData();
formData.append('file', file);

View File

@ -115,14 +115,23 @@
</mat-select>
</cnsl-form-field>
<div class="email-is-verified">
<mat-checkbox class="block-checkbox" formControlName="isVerified">
{{ 'USER.LOGINMETHODS.EMAIL.ISVERIFIED' | translate }}
</mat-checkbox>
<mat-checkbox
class="checkbox"
class="block-checkbox"
[(ngModel)]="usePassword"
[ngModelOptions]="{ standalone: true }"
(change)="initPwdValidators()"
>
{{ 'ORG.PAGES.USEPASSWORD' | translate }}</mat-checkbox
>
<cnsl-info-section class="full-width desc">
<span>{{ 'USER.CREATE.INITMAILDESCRIPTION' | translate }}</span>
</cnsl-info-section>
</div>
<ng-container *ngIf="usePassword && pwdForm">
<p class="section cnsl-secondary-text">{{ 'USER.CREATE.PASSWORDSECTION' | translate }}</p>

View File

@ -58,6 +58,16 @@ h1 {
flex: 1 0 33%;
margin: 0 0.5rem;
}
.email-is-verified {
flex-basis: 100%;
margin: 1.5rem 0.5rem 0 0.5rem;
.block-checkbox {
display: block;
margin: 0.25rem 0;
}
}
}
.pwd-form {
@ -109,11 +119,6 @@ h1 {
}
}
.checkbox {
flex-basis: 100%;
margin: 0.5rem;
}
.complexity-view {
width: 100%;
margin: 0 0.5rem;

View File

@ -10,6 +10,7 @@ import { SetUpOrgRequest } from 'src/app/proto/generated/zitadel/admin_pb';
import { PasswordComplexityPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
import { Gender } from 'src/app/proto/generated/zitadel/user_pb';
import { AdminService } from 'src/app/services/admin.service';
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service';
@ -69,7 +70,16 @@ export class OrgCreateComponent {
private fb: UntypedFormBuilder,
private mgmtService: ManagementService,
private authService: GrpcAuthService,
private breadcrumbService: BreadcrumbService,
) {
const instanceBread = new Breadcrumb({
type: BreadcrumbType.INSTANCE,
name: 'Instance',
routerLink: ['/instance'],
});
breadcrumbService.setBreadcrumb([instanceBread]);
this.authService
.isAllowed(['iam.write'])
.pipe(take(1))
@ -96,7 +106,9 @@ export class OrgCreateComponent {
createOrgRequest.setDomain(this.domain?.value);
const humanRequest: SetUpOrgRequest.Human = new SetUpOrgRequest.Human();
humanRequest.setEmail(new SetUpOrgRequest.Human.Email().setEmail(this.email?.value));
humanRequest.setEmail(
new SetUpOrgRequest.Human.Email().setEmail(this.email?.value).setIsEmailVerified(this.isVerified?.value),
);
humanRequest.setUserName(this.userName?.value);
const profile: SetUpOrgRequest.Human.Profile = new SetUpOrgRequest.Human.Profile();
@ -143,6 +155,7 @@ export class OrgCreateComponent {
firstName: ['', [Validators.required]],
lastName: ['', [Validators.required]],
email: ['', [Validators.required]],
isVerified: [false, []],
gender: [''],
nickName: [''],
preferredLanguage: [''],
@ -243,6 +256,10 @@ export class OrgCreateComponent {
return this.userForm.get('email');
}
public get isVerified(): AbstractControl | null {
return this.userForm.get('isVerified');
}
public get nickName(): AbstractControl | null {
return this.userForm.get('nickName');
}

View File

@ -9,6 +9,7 @@ import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { CreateLayoutModule } from 'src/app/modules/create-layout/create-layout.module';
import { InfoSectionModule } from 'src/app/modules/info-section/info-section.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { PasswordComplexityViewModule } from 'src/app/modules/password-complexity-view/password-complexity-view.module';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
@ -23,6 +24,7 @@ import { OrgCreateComponent } from './org-create.component';
CommonModule,
FormsModule,
ReactiveFormsModule,
InfoSectionModule,
InputModule,
MatButtonModule,
MatIconModule,

View File

@ -1,15 +1,14 @@
<cnsl-detail-layout [hasBackButton]="true" title="{{ 'USER.PASSWORD.TITLE' | translate }}"
description="{{ 'USER.PASSWORD.DESCRIPTION' | translate }}">
<cnsl-detail-layout [hasBackButton]="true" title="{{ 'USER.PASSWORD.TITLE' | translate }}">
<p class="password-info cnsl-secondary-text" sub>{{ 'USER.PASSWORD.DESCRIPTION' | translate }}</p>
<ng-container *ngIf="userId; else authUser">
<div class="validation" *ngIf="this.policy">
<cnsl-password-complexity-view [policy]="this.policy" [password]="password">
</cnsl-password-complexity-view>
</div>
<form *ngIf="passwordForm" autocomplete="new-password" [formGroup]="passwordForm"
(ngSubmit)="setInitialPassword(userId)">
<div class="content center">
<form
*ngIf="passwordForm"
autocomplete="new-password"
[formGroup]="passwordForm"
(ngSubmit)="setInitialPassword(userId)"
>
<div class="password-content">
<div class="side-by-side">
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'USER.PASSWORD.NEW' | translate }}</cnsl-label>
<input cnslInput autocomplete="off" name="password" formControlName="password" type="password" />
@ -26,16 +25,21 @@
</span>
</cnsl-form-field>
</div>
<div class="btn-container">
<button class="submit-button" [disabled]="passwordForm.invalid" mat-raised-button color="primary">{{
'USER.PASSWORD.SET' | translate }}</button>
<div class="validation" *ngIf="this.policy">
<cnsl-password-complexity-view [policy]="this.policy" [password]="password"> </cnsl-password-complexity-view>
</div>
</div>
<button class="submit-button" [disabled]="passwordForm.invalid" mat-raised-button color="primary">
{{ 'USER.PASSWORD.SET' | translate }}
</button>
</form>
</ng-container>
<ng-template #authUser>
<form *ngIf="passwordForm" [formGroup]="passwordForm" (ngSubmit)="setPassword()">
<div class="content">
<div class="password-content">
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'USER.PASSWORD.OLD' | translate }}</cnsl-label>
<input cnslInput autocomplete="off" name="password" type="password" formControlName="currentPassword" />
@ -45,10 +49,10 @@
</cnsl-form-field>
<div class="validation between" *ngIf="this.policy">
<cnsl-password-complexity-view [policy]="this.policy" [password]="newPassword">
</cnsl-password-complexity-view>
<cnsl-password-complexity-view [policy]="this.policy" [password]="newPassword"> </cnsl-password-complexity-view>
</div>
<div class="side-by-side">
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'USER.PASSWORD.NEW' | translate }}</cnsl-label>
<input cnslInput autocomplete="off" name="new password" type="password" formControlName="newPassword" />
@ -59,15 +63,22 @@
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'USER.PASSWORD.CONFIRM' | translate }}</cnsl-label>
<input cnslInput autocomplete="off" name="password repeating" type="password"
formControlName="confirmPassword" />
<input
cnslInput
autocomplete="off"
name="password repeating"
type="password"
formControlName="confirmPassword"
/>
<span cnslError *ngIf="confirmPassword?.errors?.notequal">
{{ 'USER.PASSWORD.NOTEQUAL' | translate }}
</span>
</cnsl-form-field>
</div>
<button class="submit-button" [disabled]="passwordForm.invalid" mat-raised-button color="primary">{{
'USER.PASSWORD.RESET' | translate }}</button>
</div>
<button class="submit-button" [disabled]="passwordForm.invalid" mat-raised-button color="primary">
{{ 'USER.PASSWORD.RESET' | translate }}
</button>
</form>
</ng-template>
</cnsl-detail-layout>

View File

@ -1,17 +1,34 @@
.content {
.password-info {
margin: -1rem 0 0 0;
font-size: 14px;
}
.password-content {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: 0 -0.5rem;
max-width: 40rem;
.formfield {
margin: 0 0.5rem;
.side-by-side {
display: flex;
flex-wrap: wrap;
flex-direction: row;
margin: 0 -0.5rem;
width: 100%;
max-width: 400px;
@media only screen and (max-width: 500px) {
flex-direction: column;
}
&.center {
align-items: center;
.formfield {
flex: 1;
margin: 0 0.5rem;
}
}
.formfield {
max-width: 400px;
width: 100%;
}
}
@ -22,7 +39,7 @@
}
.submit-button {
margin-top: 100px;
margin-top: 2rem;
display: block;
padding: 0.5rem 4rem;
}

View File

@ -33,7 +33,7 @@ export class ToastService {
}
}
public showError(error: any | string, isGrpc: boolean = true): void {
public showError(error: any | string, isGrpc: boolean = true, i18nKey: boolean = false): void {
if (isGrpc) {
const { message, code, metadata } = error;
if (code !== 16) {
@ -44,6 +44,13 @@ export class ToastService {
this.showMessage(decodeURI(message), value, false);
});
}
} else if (i18nKey) {
this.translate
.get(error)
.pipe(take(1))
.subscribe((value) => {
this.showMessage(value, '', false);
});
} else {
this.showMessage(error as string, '', false);
}

View File

@ -482,7 +482,7 @@
"SET": "Passwort neu setzen",
"RESENDNOTIFICATION": "Email zum Zurücksetzen senden",
"REQUIRED": "Bitte prüfe, dass alle notwendigen Felder ausgefüllt sind.",
"MINLENGTHERROR": "Das Passwort muss mindestens {{value}} Zeichen lang sein.",
"MINLENGTHERROR": "Muss mindestens {{value}} Zeichen lang sein.",
"NOTEQUAL": "Die Passwörter stimmen nicht überein."
},
"ID": "ID",
@ -542,10 +542,10 @@
"REQUIRED": "Das Eingabefeld ist leer.",
"MINLENGTH": "Das Passwort muss mindestens {{requiredLength}} Zeichen lang sein.",
"NOEMAIL": "Benutzername darf keine E-Mail-Adresse sein.",
"UPPERCASEMISSING": "Passwort muss einen Grossbuchstaben beinhalten.",
"LOWERCASEMISSING": "Passwort muss einen Kleinbuchstaben beinhalten.",
"SYMBOLERROR": "Das Passwort muss ein Symbol/Satzzeichen beinhalten.",
"NUMBERERROR": "Das Passwort muss eine Ziffer beinhalten."
"UPPERCASEMISSING": "Muss einen Grossbuchstaben beinhalten.",
"LOWERCASEMISSING": "Muss einen Kleinbuchstaben beinhalten.",
"SYMBOLERROR": "Muss ein Symbol/Satzzeichen beinhalten.",
"NUMBERERROR": "Muss eine Ziffer beinhalten."
},
"STATE": {
"0": "Unbekannt",
@ -978,9 +978,9 @@
"PWD_COMPLEXITY": {
"TITLE": "Passwortkomplexität",
"DESCRIPTION": "Stellt sicher, dass alle festgelegten Passwörter einem bestimmten Muster entsprechen.",
"SYMBOLANDNUMBERERROR": "Das Passwort muss ein Symbol/Satzzeichen und eine Ziffer beinhalten.",
"SYMBOLERROR": "Das Passwort muss ein Symbol/Satzzeichen beinhalten.",
"NUMBERERROR": "Das Passwort muss eine Ziffer beinhalten.",
"SYMBOLANDNUMBERERROR": "Muss ein Symbol/Satzzeichen und eine Ziffer beinhalten.",
"SYMBOLERROR": "Muss ein Symbol/Satzzeichen beinhalten.",
"NUMBERERROR": "Muss eine Ziffer beinhalten.",
"PATTERNERROR": "Das Passwort erfüllt nicht die vorgeschriebene Richtlinie."
},
"PRIVATELABELING": {
@ -1005,6 +1005,7 @@
"MAXSIZE": "Die maximale Grösse von Uploads ist mit 524kB begrenzt",
"EMAILNOSVG": "Das SVG Dateiformat wird nicht in emails unterstützt. Laden Sie deshalb ihr Logo im PNG oder einem anderen unterstützten Format hoch.",
"MAXSIZEEXCEEDED": "Maximale Grösse von 524kB überschritten",
"NOSVGSUPPORTED": "SVG werden nicht unterstützt!",
"FONTINLOGINONLY": "Die Schriftart wird momentan nur im Login interface angezeigt.",
"VIEWS": {
"PREVIEW": "Vorschau",

View File

@ -482,7 +482,7 @@
"SET": "Set New Password",
"RESENDNOTIFICATION": "Send Password Reset Link",
"REQUIRED": "Some required fields are missing.",
"MINLENGTHERROR": "The password has to be at least {{value}} characters long.",
"MINLENGTHERROR": "Has to be at least {{value}} characters long.",
"NOTEQUAL": "The passwords provided do not match."
},
"ID": "ID",
@ -542,10 +542,10 @@
"REQUIRED": "The input field is empty.",
"MINLENGTH": "The password has to be at least {{requiredLength}} characters long.",
"NOEMAIL": "The user name cannot be an e-mail address.",
"UPPERCASEMISSING": "The password must include an uppercase character.",
"LOWERCASEMISSING": "The password must include a lowercase character.",
"SYMBOLERROR": "The password must include a symbol or punctuation mark.",
"NUMBERERROR": "The password must include a digit."
"UPPERCASEMISSING": "Must include an uppercase character.",
"LOWERCASEMISSING": "Must include a lowercase character.",
"SYMBOLERROR": "Must include a symbol or punctuation mark.",
"NUMBERERROR": "Must include a digit."
},
"STATE": {
"0": "Unknown",
@ -978,9 +978,9 @@
"PWD_COMPLEXITY": {
"TITLE": "Password Complexity",
"DESCRIPTION": "Ensures that all set passwords correspond to a specific pattern",
"SYMBOLANDNUMBERERROR": "The password must consist of a digit and a symbol/punctuation mark.",
"SYMBOLERROR": "The password must include a symbol/punctuation mark.",
"NUMBERERROR": "The password must include a digit.",
"SYMBOLANDNUMBERERROR": "Must consist of a digit and a symbol/punctuation mark.",
"SYMBOLERROR": "Must include a symbol/punctuation mark.",
"NUMBERERROR": "Must include a digit.",
"PATTERNERROR": "The password does not meet the required pattern."
},
"PRIVATELABELING": {
@ -1005,6 +1005,7 @@
"MAXSIZE": "The maximum size is limited to 524kB",
"EMAILNOSVG": "The SVG file format is not supported in emails. Therefore upload your logo in PNG or other supported format.",
"MAXSIZEEXCEEDED": "Maximum size of 524kB exceeded.",
"NOSVGSUPPORTED": "SVG are not supported!",
"FONTINLOGINONLY": "The font is currently only displayed in the login interface.",
"VIEWS": {
"PREVIEW": "Preview",

View File

@ -482,7 +482,7 @@
"SET": "Définir un nouveau mot de passe",
"RESENDNOTIFICATION": "Envoyer le lien de réinitialisation du mot de passe",
"REQUIRED": "Certains champs obligatoires sont manquants.",
"MINLENGTHERROR": "Le mot de passe doit comporter au moins{{value}} caractères.",
"MINLENGTHERROR": "Doit comporter au moins {{value}} caractères.",
"NOTEQUAL": "Les mots de passe fournis ne correspondent pas."
},
"ID": "ID",
@ -542,10 +542,10 @@
"REQUIRED": "Le champ de saisie est vide.",
"MINLENGTH": "Le mot de passe doit comporter au moins{{length}} caractères.",
"NOEMAIL": "Le nom d'utilisateur ne peut pas être une adresse électronique.",
"UPPERCASEMISSING": "Le mot de passe doit comporter un caractère majuscule.",
"LOWERCASEMISSING": "Le mot de passe doit inclure un caractère minuscule.",
"SYMBOLERROR": "Le mot de passe doit inclure un symbole ou un signe de ponctuation.",
"NUMBERERROR": "Le mot de passe doit contenir un chiffre."
"UPPERCASEMISSING": "Doit inclure un caractère majuscule.",
"LOWERCASEMISSING": "Doit inclure un caractère minuscule.",
"SYMBOLERROR": "Doit inclure un symbole ou un signe de ponctuation.",
"NUMBERERROR": "Doit inclure un chiffre."
},
"STATE": {
"0": "Inconnu",
@ -978,9 +978,9 @@
"PWD_COMPLEXITY": {
"TITLE": "Complexité des mots de passe",
"DESCRIPTION": "Assure que tous les mots de passe définis correspondent à un modèle spécifique.",
"SYMBOLANDNUMBERERROR": "Le mot de passe doit être composé d'un chiffre et d'un symbole/une marque de ponctuation.",
"SYMBOLERROR": "Le mot de passe doit comprendre un symbole ou un signe de ponctuation.",
"NUMBERERROR": "Le mot de passe doit comprendre un chiffre.",
"SYMBOLANDNUMBERERROR": "Doit être composé d'un chiffre et d'un symbole/signe de ponctuation.",
"SYMBOLERROR": "Doit inclure un symbole/un signe de ponctuation.",
"NUMBERERROR": "Doit inclure un chiffre.",
"PATTERNERROR": "Le mot de passe ne correspond pas au modèle requis."
},
"PRIVATELABELING": {
@ -1005,6 +1005,7 @@
"MAXSIZE": "La taille maximale est limitée à 524 Ko.",
"EMAILNOSVG": "Le format de fichier SVG n'est pas supporté dans les emails. Téléchargez donc votre logo en PNG ou dans un autre format pris en charge.",
"MAXSIZEEXCEEDED": "La taille maximale de 524kB est dépassée.",
"NOSVGSUPPORTED": "SVG n'est pas pris en charge",
"FONTINLOGINONLY": "La police n'est actuellement affichée que dans l'interface de connexion.",
"VIEWS": {
"PREVIEW": "Aperçu",

View File

@ -482,7 +482,7 @@
"SET": "Imposta nuova password",
"RESENDNOTIFICATION": "Invia email per la reimpostazione",
"REQUIRED": "Mancano alcuni campi obbligatori.",
"MINLENGTHERROR": "La password deve essere lunga almeno {{valore}} caratteri.",
"MINLENGTHERROR": "Deve essere lunga almeno {{valore}} caratteri.",
"NOTEQUAL": "Le password fornite non corrispondono."
},
"ID": "ID",
@ -540,12 +540,12 @@
"INVALIDPATTERN": "La password non soddisfa le regole definite.",
"NOTANEMAIL": "Il valore dato non \u00e8 un indirizzo e-mail",
"REQUIRED": "Il campo di input \u00e8 vuoto.",
"MINLENGTH": "La password deve essere lunga almeno {{requiredLength}} caratteri.",
"MINLENGTH": "Deve essere lunga almeno {{requiredLength}} caratteri.",
"NOEMAIL": "Il nome utente non pu\u00f2 essere un indirizzo e-mail.",
"UPPERCASEMISSING": "La password deve includere un carattere maiuscolo.",
"LOWERCASEMISSING": "La password deve includere un carattere minuscolo.",
"SYMBOLERROR": "La password deve includere un simbolo o un segno di punteggiatura.",
"NUMBERERROR": "La password deve includere una cifra."
"UPPERCASEMISSING": "Deve includere un carattere maiuscolo.",
"LOWERCASEMISSING": "Deve includere un carattere minuscolo.",
"SYMBOLERROR": "Deve includere un simbolo o un segno di punteggiatura.",
"NUMBERERROR": "Deve includere una cifra."
},
"STATE": {
"0": "Sconosciuto",
@ -978,9 +978,9 @@
"PWD_COMPLEXITY": {
"TITLE": "Complessit\u00e0 della password",
"DESCRIPTION": "Assicura che tutte le password impostate corrispondano a un modello specifico",
"SYMBOLANDNUMBERERROR": "La password deve essere composta da una cifra e un simbolo/segno di interpunzione.",
"SYMBOLERROR": "La password deve includere un simbolo/segno di punteggiatura.",
"NUMBERERROR": "La password deve includere una cifra.",
"SYMBOLANDNUMBERERROR": "Deve essere composta da una cifra e un simbolo/segno di interpunzione.",
"SYMBOLERROR": "Deve includere un simbolo/segno di punteggiatura.",
"NUMBERERROR": "Deve includere una cifra.",
"PATTERNERROR": "La password non corrisponde al modello richiesto."
},
"PRIVATELABELING": {
@ -1005,6 +1005,7 @@
"MAXSIZE": "La dimensione massima \u00e8 limitata a 524kB",
"EMAILNOSVG": "Il formato di file SVG non \u00e8 supportato nelle email. Perci\u00f2 carica il tuo logo in PNG o in un altro formato supportato.",
"MAXSIZEEXCEEDED": "Dimensione massima di 524kB superata.",
"NOSVGSUPPORTED": "SVG non sono supportati",
"FONTINLOGINONLY": "Il carattere \u00e8 attualmente visualizzato solo nell'interfaccia di accesso.",
"VIEWS": {
"PREVIEW": "Anteprima",

View File

@ -13,7 +13,7 @@ When planning your applications, investing time in researching your apps archite
This guide introduces you to the grouping and structuring of ZITADEL projects which forms the base for all projects. This can be used as a quick start to the [B2B scenario](./b2b), which is merely focused on planning considerations if you are having projects with multiple organizations.
The journey of this guide starts with creating an Organization, the outermost layer of ZITADEL within your instance, as it is the vessel for projects, roles, applications and users.
Creation can be done from [ZITADEL Console](https://{your_domain}.zitadel.cloud/ui/console/org/create). You can choose your current account for the organization owner or create a new one.
Creation can be done from [ZITADEL Console](https://{your_domain}.zitadel.cloud/ui/console/orgs/create). You can choose your current account for the organization owner or create a new one.
Depending on your Software Development Life Cycle (SDLC) you can create multiple organizations or projects to keep your applications environments seperated.