mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-13 17:35:12 +00:00
feat(console): domain verification, create org, regexp route guards, change org, iam member (#573)
* verification dialog, service * i18n, verification dialog * file saver * savefile * verify trigger * delete project, i18n * org create dialog * org-create-self * stylelint * fix signout redirect * rm unused dialog component, import * project i18n de * regexp roles * use regex to check permissions * border radius * change validation flow * update org member * iam member change * lint * rm unused css * Update console/src/assets/i18n/de.json Co-authored-by: Fabi <[email protected]> * Update console/src/assets/i18n/de.json Co-authored-by: Fabi <[email protected]> * change guard Co-authored-by: Fabi <[email protected]>
This commit is contained in:
@@ -8,159 +8,176 @@
|
||||
{{ createSteps }}</span>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="currentCreateStep == 1">
|
||||
<h1>{{'ORG.PAGES.ORGDETAIL_TITLE' | translate}}</h1>
|
||||
<form [formGroup]="orgForm" (ngSubmit)="next()">
|
||||
<div class="content">
|
||||
<mat-form-field class="formfield" appearance="outline">
|
||||
<mat-label>{{ 'ORG_DETAIL.DETAIL.NAME' | translate }}</mat-label>
|
||||
<input matInput formControlName="name" />
|
||||
</mat-form-field>
|
||||
<mat-form-field class="formfield" appearance="outline">
|
||||
<mat-label>{{ 'ORG_DETAIL.DETAIL.DOMAIN' | translate }}</mat-label>
|
||||
<input matInput formControlName="domain" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<ng-template appHasRole [appHasRole]="['iam.write']">
|
||||
<mat-slide-toggle class="example-margin" color="primary" (change)="changeSelf($event)" [(ngModel)]="forSelf">
|
||||
Use your personal account as organisation owner
|
||||
</mat-slide-toggle>
|
||||
|
||||
<div class="btn-container">
|
||||
<span class="fill-space"></span>
|
||||
<button [disabled]="orgForm.invalid" color="primary" mat-raised-button class="big-button"
|
||||
cdkFocusInitial type="submit">
|
||||
{{'CONTINUE' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<ng-container *ngIf="!forSelf">
|
||||
<ng-container *ngIf="currentCreateStep == 1">
|
||||
<h1>{{'ORG.PAGES.ORGDETAIL_TITLE' | translate}}</h1>
|
||||
<form [formGroup]="orgForm" (ngSubmit)="next()">
|
||||
<div class="content">
|
||||
<mat-form-field class="formfield" appearance="outline">
|
||||
<mat-label>{{ 'ORG_DETAIL.DETAIL.NAME' | translate }}</mat-label>
|
||||
<input matInput formControlName="name" />
|
||||
</mat-form-field>
|
||||
<mat-form-field class="formfield" appearance="outline">
|
||||
<mat-label>{{ 'ORG_DETAIL.DETAIL.DOMAIN' | translate }}</mat-label>
|
||||
<input matInput formControlName="domain" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<!-- <div *ngIf="name?.touched" @openClose>
|
||||
<p class="desc">{{ 'ORG.PAGES.ORGDOMAIN_VERIFICATION' | translate }}</p>
|
||||
<div class="btn-container">
|
||||
<span class="fill-space"></span>
|
||||
<button [disabled]="orgForm.invalid" color="primary" mat-raised-button class="big-button"
|
||||
cdkFocusInitial type="submit">
|
||||
{{'CONTINUE' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</ng-container>
|
||||
|
||||
<p>{{domain?.value}}/.well-known/caos-developer-domain-association.txt</p>
|
||||
<ng-container *ngIf="currentCreateStep == createSteps">
|
||||
<h1>{{'ORG.PAGES.ORGDETAILUSER_TITLE' | translate}}</h1>
|
||||
|
||||
<div class="btn-container">
|
||||
<button color="primary" type="submit"
|
||||
mat-stroked-button>{{ 'ORG.PAGES.DOWNLOAD_FILE' | translate }}</button>
|
||||
|
||||
<button color="primary" type="submit" mat-raised-button>{{ 'ACTIONS.VERIFY' | translate }}</button>
|
||||
</div>
|
||||
|
||||
<p class="desc">{{ 'ORG.PAGES.ORGDOMAIN_VERIFICATION_SKIP' | translate }}</p>
|
||||
</div> -->
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="currentCreateStep == createSteps">
|
||||
<h1>{{'ORG.PAGES.ORGDETAILUSER_TITLE' | translate}}</h1>
|
||||
|
||||
<div class="user">
|
||||
<form [formGroup]="userForm" class="form">
|
||||
<div class="content">
|
||||
<p class="section">{{ 'USER.CREATE.NAMEANDEMAILSECTION' | translate }}</p>
|
||||
<mat-form-field class="formfield" appearance="outline">
|
||||
<mat-label>{{ 'USER.PROFILE.USERNAME' | translate }}</mat-label>
|
||||
<input matInput formControlName="userName" required />
|
||||
<mat-error *ngIf="userName?.invalid && userName?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="formfield" appearance="outline">
|
||||
<mat-label>{{ 'USER.PROFILE.EMAIL' | translate }}</mat-label>
|
||||
<input matInput formControlName="email" required />
|
||||
<mat-error *ngIf="email?.invalid && email?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="formfield" appearance="outline">
|
||||
<mat-label>{{ 'USER.PROFILE.FIRSTNAME' | translate }}</mat-label>
|
||||
<input matInput formControlName="firstName" required />
|
||||
<mat-error *ngIf="firstName?.invalid && firstName?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="formfield" appearance="outline">
|
||||
<mat-label>{{ 'USER.PROFILE.LASTNAME' | translate }}</mat-label>
|
||||
<input matInput formControlName="lastName" required />
|
||||
<mat-error *ngIf="lastName?.invalid && lastName?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="formfield" appearance="outline">
|
||||
<mat-label>{{ 'USER.PROFILE.NICKNAME' | translate }}</mat-label>
|
||||
<input matInput formControlName="nickName" />
|
||||
<mat-error *ngIf="nickName?.invalid && nickName?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<p class="section">{{ 'USER.CREATE.GENDERLANGSECTION' | translate }}</p>
|
||||
|
||||
<mat-form-field class="formfield" appearance="outline">
|
||||
<mat-label>{{ 'USER.PROFILE.GENDER' | translate }}</mat-label>
|
||||
<mat-select formControlName="gender">
|
||||
<mat-option *ngFor="let gender of genders" [value]="gender">
|
||||
{{ 'GENDERS.'+gender | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="gender?.invalid && gender?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="formfield" appearance="outline">
|
||||
<mat-label>{{ 'USER.PROFILE.PREFERRED_LANGUAGE' | translate }}</mat-label>
|
||||
<mat-select formControlName="preferredLanguage">
|
||||
<mat-option *ngFor="let language of languages" [value]="language">
|
||||
{{ 'LANGUAGES.'+language | translate }}
|
||||
</mat-option>
|
||||
<mat-error *ngIf="preferredLanguage?.invalid && preferredLanguage?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-checkbox class="checkbox" [(ngModel)]="usePassword" [ngModelOptions]="{standalone: true}"
|
||||
(change)="initPwdValidators()">
|
||||
{{'ORG.PAGES.USEPASSWORD' | translate}}</mat-checkbox>
|
||||
|
||||
<ng-container *ngIf="usePassword && pwdForm">
|
||||
<p class="section">{{ 'USER.CREATE.PASSWORDSECTION' | translate }}</p>
|
||||
|
||||
<app-password-complexity-view class="complexity-view" [policy]="this.policy"
|
||||
[password]="password">
|
||||
</app-password-complexity-view>
|
||||
|
||||
<form [formGroup]="pwdForm" class="form">
|
||||
<mat-form-field class="formfield" *ngIf="password" appearance="outline">
|
||||
<mat-label>{{ 'USER.PASSWORD.NEW' | translate }}</mat-label>
|
||||
<input autocomplete="off" name="firstpassword" matInput formControlName="password"
|
||||
type="password" />
|
||||
|
||||
<mat-error *ngIf="password?.errors?.required">
|
||||
<div class="user">
|
||||
<form [formGroup]="userForm" class="form">
|
||||
<div class="content">
|
||||
<p class="section">{{ 'USER.CREATE.NAMEANDEMAILSECTION' | translate }}</p>
|
||||
<mat-form-field class="formfield" appearance="outline">
|
||||
<mat-label>{{ 'USER.PROFILE.USERNAME' | translate }}</mat-label>
|
||||
<input matInput formControlName="userName" required />
|
||||
<mat-error *ngIf="userName?.invalid && userName?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
|
||||
</mat-form-field>
|
||||
<mat-form-field class="formfield" *ngIf="confirmPassword" appearance="outline">
|
||||
<mat-label>{{ 'USER.PASSWORD.CONFIRM' | translate }}</mat-label>
|
||||
<input autocomplete="off" name="confirmPassword" matInput
|
||||
formControlName="confirmPassword" type="password" />
|
||||
|
||||
<mat-error *ngIf="confirmPassword?.errors?.required">
|
||||
<mat-form-field class="formfield" appearance="outline">
|
||||
<mat-label>{{ 'USER.PROFILE.EMAIL' | translate }}</mat-label>
|
||||
<input matInput formControlName="email" required />
|
||||
<mat-error *ngIf="email?.invalid && email?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
<mat-error *ngIf="confirmPassword?.errors?.notequal">
|
||||
{{ 'USER.PASSWORD.NOTEQUAL' | translate }}
|
||||
</mat-form-field>
|
||||
<mat-form-field class="formfield" appearance="outline">
|
||||
<mat-label>{{ 'USER.PROFILE.FIRSTNAME' | translate }}</mat-label>
|
||||
<input matInput formControlName="firstName" required />
|
||||
<mat-error *ngIf="firstName?.invalid && firstName?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</form>
|
||||
</ng-container>
|
||||
<mat-form-field class="formfield" appearance="outline">
|
||||
<mat-label>{{ 'USER.PROFILE.LASTNAME' | translate }}</mat-label>
|
||||
<input matInput formControlName="lastName" required />
|
||||
<mat-error *ngIf="lastName?.invalid && lastName?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="formfield" appearance="outline">
|
||||
<mat-label>{{ 'USER.PROFILE.NICKNAME' | translate }}</mat-label>
|
||||
<input matInput formControlName="nickName" />
|
||||
<mat-error *ngIf="nickName?.invalid && nickName?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<p class="section">{{ 'USER.CREATE.GENDERLANGSECTION' | translate }}</p>
|
||||
|
||||
<mat-form-field class="formfield" appearance="outline">
|
||||
<mat-label>{{ 'USER.PROFILE.GENDER' | translate }}</mat-label>
|
||||
<mat-select formControlName="gender">
|
||||
<mat-option *ngFor="let gender of genders" [value]="gender">
|
||||
{{ 'GENDERS.'+gender | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="gender?.invalid && gender?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="formfield" appearance="outline">
|
||||
<mat-label>{{ 'USER.PROFILE.PREFERRED_LANGUAGE' | translate }}</mat-label>
|
||||
<mat-select formControlName="preferredLanguage">
|
||||
<mat-option *ngFor="let language of languages" [value]="language">
|
||||
{{ 'LANGUAGES.'+language | translate }}
|
||||
</mat-option>
|
||||
<mat-error
|
||||
*ngIf="preferredLanguage?.invalid && preferredLanguage?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-checkbox class="checkbox" [(ngModel)]="usePassword"
|
||||
[ngModelOptions]="{standalone: true}" (change)="initPwdValidators()">
|
||||
{{'ORG.PAGES.USEPASSWORD' | translate}}</mat-checkbox>
|
||||
|
||||
<ng-container *ngIf="usePassword && pwdForm">
|
||||
<p class="section">{{ 'USER.CREATE.PASSWORDSECTION' | translate }}</p>
|
||||
|
||||
<app-password-complexity-view class="complexity-view" [policy]="this.policy"
|
||||
[password]="password">
|
||||
</app-password-complexity-view>
|
||||
|
||||
<form [formGroup]="pwdForm" class="form">
|
||||
<mat-form-field class="formfield" *ngIf="password" appearance="outline">
|
||||
<mat-label>{{ 'USER.PASSWORD.NEW' | translate }}</mat-label>
|
||||
<input autocomplete="off" name="firstpassword" matInput
|
||||
formControlName="password" type="password" />
|
||||
|
||||
<mat-error *ngIf="password?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
|
||||
</mat-form-field>
|
||||
<mat-form-field class="formfield" *ngIf="confirmPassword" appearance="outline">
|
||||
<mat-label>{{ 'USER.PASSWORD.CONFIRM' | translate }}</mat-label>
|
||||
<input autocomplete="off" name="confirmPassword" matInput
|
||||
formControlName="confirmPassword" type="password" />
|
||||
|
||||
<mat-error *ngIf="confirmPassword?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
<mat-error *ngIf="confirmPassword?.errors?.notequal">
|
||||
{{ 'USER.PASSWORD.NOTEQUAL' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</form>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="btn-container">
|
||||
<button color="primary" class="small-button" type="button" (click)="previous()"
|
||||
mat-stroked-button>{{ 'ACTIONS.BACK' | translate }}</button>
|
||||
<span class="fill-space"></span>
|
||||
<button color="primary" class="big-button" (click)="finish()"
|
||||
[disabled]="orgForm.invalid || userForm.invalid || ((usePassword && pwdForm) ? pwdForm?.invalid : false)"
|
||||
mat-raised-button>{{ 'ACTIONS.FINISH' | translate }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="btn-container">
|
||||
<button color="primary" class="small-button" type="button" (click)="previous()"
|
||||
mat-stroked-button>{{ 'ACTIONS.BACK' | translate }}</button>
|
||||
<span class="fill-space"></span>
|
||||
<button color="primary" class="big-button" (click)="finish()"
|
||||
[disabled]="orgForm.invalid || userForm.invalid || ((usePassword && pwdForm) ? pwdForm?.invalid : false)"
|
||||
mat-raised-button>{{ 'ACTIONS.FINISH' | translate }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
<ng-template appHasRole [appHasRole]="['org.create']">
|
||||
<div *ngIf="forSelf">
|
||||
<ng-container *ngIf="currentCreateStep == 1">
|
||||
<h1>{{'ORG.PAGES.ORGDETAIL_TITLE' | translate}}</h1>
|
||||
<form [formGroup]="orgForm" (ngSubmit)="createOrgForSelf()">
|
||||
<div class="content">
|
||||
<mat-form-field class="formfield" appearance="outline">
|
||||
<mat-label>{{ 'ORG_DETAIL.DETAIL.NAME' | translate }}</mat-label>
|
||||
<input matInput formControlName="name" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="btn-container">
|
||||
<span class="fill-space"></span>
|
||||
<button [disabled]="orgForm.invalid" color="primary" mat-raised-button class="big-button"
|
||||
cdkFocusInitial type="submit">
|
||||
{{'CREATE' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
</div>
|
||||
@@ -2,11 +2,14 @@ import { animate, style, transition, trigger } from '@angular/animations';
|
||||
import { Location } from '@angular/common';
|
||||
import { Component } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, ValidatorFn, Validators } from '@angular/forms';
|
||||
import { MatSlideToggleChange } from '@angular/material/slide-toggle';
|
||||
import { Router } from '@angular/router';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { lowerCaseValidator, numberValidator, symbolValidator, upperCaseValidator } from 'src/app/pages/validators';
|
||||
import { CreateOrgRequest, CreateUserRequest, Gender, OrgSetUpResponse } from 'src/app/proto/generated/admin_pb';
|
||||
import { PasswordComplexityPolicy } from 'src/app/proto/generated/auth_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
import { AuthService } from 'src/app/services/auth.service';
|
||||
import { OrgService } from 'src/app/services/org.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -56,6 +59,8 @@ export class OrgCreateComponent {
|
||||
|
||||
public policy!: PasswordComplexityPolicy.AsObject;
|
||||
public usePassword: boolean = false;
|
||||
|
||||
public forSelf: boolean = true;
|
||||
constructor(
|
||||
private router: Router,
|
||||
private toast: ToastService,
|
||||
@@ -63,8 +68,13 @@ export class OrgCreateComponent {
|
||||
private _location: Location,
|
||||
private fb: FormBuilder,
|
||||
private orgService: OrgService,
|
||||
private authService: AuthService,
|
||||
) {
|
||||
const validators: Validators[] = [];
|
||||
this.authService.isAllowed(['iam.write']).pipe(take(1)).subscribe((allowed) => {
|
||||
if (allowed) {
|
||||
this.forSelf = false;
|
||||
}
|
||||
});
|
||||
|
||||
this.orgForm = this.fb.group({
|
||||
name: ['', [Validators.required]],
|
||||
@@ -165,6 +175,36 @@ export class OrgCreateComponent {
|
||||
}
|
||||
}
|
||||
|
||||
public changeSelf(change: MatSlideToggleChange): void {
|
||||
console.log(change.checked);
|
||||
|
||||
if (change.checked) {
|
||||
this.createSteps = 1;
|
||||
|
||||
this.orgForm = this.fb.group({
|
||||
name: ['', [Validators.required]],
|
||||
});
|
||||
} else {
|
||||
this.createSteps = 2;
|
||||
|
||||
this.orgForm = this.fb.group({
|
||||
name: ['', [Validators.required]],
|
||||
domain: [''],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public createOrgForSelf(): void {
|
||||
console.log('create for self');
|
||||
if (this.name && this.name.value) {
|
||||
this.orgService.CreateOrg(this.name.value).then((org) => {
|
||||
this.router.navigate(['orgs', org.toObject().id]);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public get name(): AbstractControl | null {
|
||||
return this.orgForm.get('name');
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
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 { PasswordComplexityViewModule } from 'src/app/modules/password-complexity-view/password-complexity-view.module';
|
||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.module';
|
||||
|
||||
@@ -28,8 +30,10 @@ import { OrgCreateComponent } from './org-create.component';
|
||||
MatSelectModule,
|
||||
HasRolePipeModule,
|
||||
TranslateModule,
|
||||
HasRoleModule,
|
||||
MatCheckboxModule,
|
||||
PasswordComplexityViewModule,
|
||||
MatSlideToggleModule,
|
||||
],
|
||||
})
|
||||
export class OrgCreateModule { }
|
||||
|
||||
Reference in New Issue
Block a user