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:
Max Peintner
2020-08-12 08:47:53 +02:00
committed by GitHub
co-authored by Fabi
parent 29831111ae
commit 65058ed17c
42 changed files with 757 additions and 255 deletions
@@ -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 { }