mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-01 20:02:15 +00:00
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 <[email protected]> Co-authored-by: Livio Spring <[email protected]>
This commit is contained in:
co-authored by
Fabi
Livio Spring
parent
e39d82c031
commit
cbb5e90bac
@@ -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),
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
<a
|
||||
class="nav-item"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
[routerLinkActiveOptions]="{ exact: false }"
|
||||
[routerLinkActive]="['active']"
|
||||
[routerLink]="['/orgs']"
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
+10
-8
@@ -4,15 +4,15 @@ import { MatDialog } from '@angular/material/dialog';
|
||||
import { Subject, Subscription } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import {
|
||||
GetLabelPolicyResponse as AdminGetLabelPolicyResponse,
|
||||
GetPreviewLabelPolicyResponse as AdminGetPreviewLabelPolicyResponse,
|
||||
UpdateLabelPolicyRequest,
|
||||
GetLabelPolicyResponse as AdminGetLabelPolicyResponse,
|
||||
GetPreviewLabelPolicyResponse as AdminGetPreviewLabelPolicyResponse,
|
||||
UpdateLabelPolicyRequest,
|
||||
} from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import {
|
||||
AddCustomLabelPolicyRequest,
|
||||
GetLabelPolicyResponse as MgmtGetLabelPolicyResponse,
|
||||
GetPreviewLabelPolicyResponse as MgmtGetPreviewLabelPolicyResponse,
|
||||
UpdateCustomLabelPolicyRequest,
|
||||
AddCustomLabelPolicyRequest,
|
||||
GetLabelPolicyResponse as MgmtGetLabelPolicyResponse,
|
||||
GetPreviewLabelPolicyResponse as MgmtGetPreviewLabelPolicyResponse,
|
||||
UpdateCustomLabelPolicyRequest,
|
||||
} from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { Org } from 'src/app/proto/generated/zitadel/org_pb';
|
||||
import { LabelPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
|
||||
@@ -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);
|
||||
|
||||
@@ -115,14 +115,23 @@
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
|
||||
<mat-checkbox
|
||||
class="checkbox"
|
||||
[(ngModel)]="usePassword"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
(change)="initPwdValidators()"
|
||||
>
|
||||
{{ 'ORG.PAGES.USEPASSWORD' | translate }}</mat-checkbox
|
||||
>
|
||||
<div class="email-is-verified">
|
||||
<mat-checkbox class="block-checkbox" formControlName="isVerified">
|
||||
{{ 'USER.LOGINMETHODS.EMAIL.ISVERIFIED' | translate }}
|
||||
</mat-checkbox>
|
||||
|
||||
<mat-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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,41 +1,45 @@
|
||||
<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="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" />
|
||||
|
||||
<form *ngIf="passwordForm" autocomplete="new-password" [formGroup]="passwordForm"
|
||||
(ngSubmit)="setInitialPassword(userId)">
|
||||
<div class="content center">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'USER.PASSWORD.NEW' | translate }}</cnsl-label>
|
||||
<input cnslInput autocomplete="off" name="password" formControlName="password" type="password" />
|
||||
<span cnslError *ngIf="password?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</span>
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'USER.PASSWORD.CONFIRM' | translate }}</cnsl-label>
|
||||
<input cnslInput autocomplete="off" name="passwordRepeat" formControlName="confirmPassword" type="password" />
|
||||
<span cnslError *ngIf="confirmPassword?.errors?.notequal">
|
||||
{{ 'USER.PASSWORD.NOTEQUAL' | translate }}
|
||||
</span>
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
|
||||
<span cnslError *ngIf="password?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</span>
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'USER.PASSWORD.CONFIRM' | translate }}</cnsl-label>
|
||||
<input cnslInput autocomplete="off" name="passwordRepeat" formControlName="confirmPassword" type="password" />
|
||||
<span cnslError *ngIf="confirmPassword?.errors?.notequal">
|
||||
{{ 'USER.PASSWORD.NOTEQUAL' | translate }}
|
||||
</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,29 +49,36 @@
|
||||
</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>
|
||||
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'USER.PASSWORD.NEW' | translate }}</cnsl-label>
|
||||
<input cnslInput autocomplete="off" name="new password" type="password" formControlName="newPassword" />
|
||||
<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" />
|
||||
|
||||
<span cnslError *ngIf="newPassword?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</span>
|
||||
</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" />
|
||||
<span cnslError *ngIf="confirmPassword?.errors?.notequal">
|
||||
{{ 'USER.PASSWORD.NOTEQUAL' | translate }}
|
||||
</span>
|
||||
</cnsl-form-field>
|
||||
<span cnslError *ngIf="newPassword?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</span>
|
||||
</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"
|
||||
/>
|
||||
<span cnslError *ngIf="confirmPassword?.errors?.notequal">
|
||||
{{ 'USER.PASSWORD.NOTEQUAL' | translate }}
|
||||
</span>
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<button class="submit-button" [disabled]="passwordForm.invalid" mat-raised-button color="primary">{{
|
||||
'USER.PASSWORD.RESET' | translate }}</button>
|
||||
<button class="submit-button" [disabled]="passwordForm.invalid" mat-raised-button color="primary">
|
||||
{{ 'USER.PASSWORD.RESET' | translate }}
|
||||
</button>
|
||||
</form>
|
||||
</ng-template>
|
||||
</cnsl-detail-layout>
|
||||
</cnsl-detail-layout>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
.formfield {
|
||||
flex: 1;
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.center {
|
||||
align-items: center;
|
||||
.formfield {
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +39,7 @@
|
||||
}
|
||||
|
||||
.submit-button {
|
||||
margin-top: 100px;
|
||||
margin-top: 2rem;
|
||||
display: block;
|
||||
padding: 0.5rem 4rem;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user