From 40d7dba574cad2ecd8525563a39322415bdcad7c Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Fri, 20 May 2022 11:23:16 +0200 Subject: [PATCH] fix(console-v2): settings permission restriction, u2f naming, asset error handling (#3658) * fix permission on nav * restrict settings access * fido table * u2f i18n, permission * factor, image fallback Co-authored-by: Livio Amstutz --- console/src/app/app.component.ts | 5 + .../app/modules/avatar/avatar.component.html | 35 ++-- .../app/modules/avatar/avatar.component.ts | 4 + .../domain-policy.component.html | 79 ++++++++- .../general-settings.component.html | 16 +- .../general-settings.module.ts | 2 + .../dialog-add-type.component.html | 29 ++++ .../dialog-add-type.component.scss | 0 .../dialog-add-type.component.ts | 4 +- .../factor-table.component.html} | 0 .../factor-table.component.scss} | 0 .../factor-table.component.spec.ts | 24 +++ .../factor-table.component.ts} | 153 +++++++++-------- .../login-policy/login-policy.component.html | 154 ++++++++++++++++-- .../login-policy/login-policy.component.ts | 38 ++++- .../login-policy/login-policy.module.ts | 6 +- .../dialog-add-type.component.html | 20 --- .../mfa-table/mfa-table.component.spec.ts | 25 --- .../login-texts/login-texts.component.html | 5 + .../login-texts/login-texts.component.ts | 4 + .../message-texts.component.html | 4 + .../message-texts/message-texts.component.ts | 6 +- .../notification-settings.component.html | 4 +- .../preview/preview.component.scss | 2 +- .../private-labeling-policy.component.html | 37 ++++- .../private-labeling-policy.module.ts | 2 + .../src/app/modules/settings-list/settings.ts | 55 ++++++- .../modules/sidenav/sidenav.component.html | 5 + .../app/modules/sidenav/sidenav.component.ts | 6 +- .../src/app/modules/sidenav/sidenav.module.ts | 3 +- .../auth-user-detail.component.html | 1 + .../auth-user-detail.component.ts | 14 +- .../detail-form/detail-form.component.ts | 6 +- .../profile-picture.component.ts | 2 +- console/src/app/services/asset.service.ts | 5 +- console/src/app/services/grpc-auth.service.ts | 13 ++ console/src/assets/i18n/de.json | 26 +-- console/src/assets/i18n/en.json | 28 ++-- console/src/assets/i18n/it.json | 30 ++-- .../assets/mdi/usb-flash-drive-outline.svg | 1 + console/src/component-themes.scss | 2 +- 41 files changed, 633 insertions(+), 222 deletions(-) create mode 100644 console/src/app/modules/policies/login-policy/factor-table/dialog-add-type/dialog-add-type.component.html rename console/src/app/modules/policies/login-policy/{mfa-table => factor-table}/dialog-add-type/dialog-add-type.component.scss (100%) rename console/src/app/modules/policies/login-policy/{mfa-table => factor-table}/dialog-add-type/dialog-add-type.component.ts (94%) rename console/src/app/modules/policies/login-policy/{mfa-table/mfa-table.component.html => factor-table/factor-table.component.html} (100%) rename console/src/app/modules/policies/login-policy/{mfa-table/mfa-table.component.scss => factor-table/factor-table.component.scss} (100%) create mode 100644 console/src/app/modules/policies/login-policy/factor-table/factor-table.component.spec.ts rename console/src/app/modules/policies/login-policy/{mfa-table/mfa-table.component.ts => factor-table/factor-table.component.ts} (65%) delete mode 100644 console/src/app/modules/policies/login-policy/mfa-table/dialog-add-type/dialog-add-type.component.html delete mode 100644 console/src/app/modules/policies/login-policy/mfa-table/mfa-table.component.spec.ts create mode 100644 console/src/assets/mdi/usb-flash-drive-outline.svg diff --git a/console/src/app/app.component.ts b/console/src/app/app.component.ts index bfdb5bf2ce..0bf7d498db 100644 --- a/console/src/app/app.component.ts +++ b/console/src/app/app.component.ts @@ -116,6 +116,11 @@ export class AppComponent implements OnInit, OnDestroy { this.domSanitizer.bypassSecurityTrustResourceUrl('assets/mdi/lightbulb-off-outline.svg'), ); + this.matIconRegistry.addSvgIcon( + 'usb', + this.domSanitizer.bypassSecurityTrustResourceUrl('assets/mdi/usb-flash-drive-outline.svg'), + ); + this.matIconRegistry.addSvgIcon('mdi_radar', this.domSanitizer.bypassSecurityTrustResourceUrl('assets/mdi/radar.svg')); this.matIconRegistry.addSvgIcon( diff --git a/console/src/app/modules/avatar/avatar.component.html b/console/src/app/modules/avatar/avatar.component.html index 6225de4e52..7be0728c83 100644 --- a/console/src/app/modules/avatar/avatar.component.html +++ b/console/src/app/modules/avatar/avatar.component.html @@ -1,12 +1,23 @@ -
- - - - - {{credentials}} - - -
\ No newline at end of file +
+ + + + + {{ credentials }} + + +
diff --git a/console/src/app/modules/avatar/avatar.component.ts b/console/src/app/modules/avatar/avatar.component.ts index 125013b85b..417218a458 100644 --- a/console/src/app/modules/avatar/avatar.component.ts +++ b/console/src/app/modules/avatar/avatar.component.ts @@ -52,4 +52,8 @@ export class AvatarComponent implements OnInit { const toGen = this.forColor || this.name || ''; return getColorHash(toGen); } + + public errorHandler(event: any) { + (event.target as HTMLImageElement).style.display = 'none'; + } } diff --git a/console/src/app/modules/policies/domain-policy/domain-policy.component.html b/console/src/app/modules/policies/domain-policy/domain-policy.component.html index 14256905bd..11edde75b9 100644 --- a/console/src/app/modules/policies/domain-policy/domain-policy.component.html +++ b/console/src/app/modules/policies/domain-policy/domain-policy.component.html @@ -12,22 +12,63 @@ color="warn" (click)="removePolicy()" mat-stroked-button + [disabled]=" + ([ + serviceType === PolicyComponentServiceType.ADMIN + ? 'iam.policy.write' + : serviceType === PolicyComponentServiceType.MGMT + ? 'policy.write' + : '' + ] + | hasRole + | async) === false + " > {{ 'POLICY.RESET' | translate }} -
- + {{ 'POLICY.DATA.USERLOGINMUSTBEDOMAIN' | translate }}
-
- + {{ 'POLICY.DATA.VALIDATEORGDOMAINS' | translate }}
@@ -38,15 +79,41 @@ name="hasNumber" ngDefaultControl [(ngModel)]="domainData.smtpSenderAddressMatchesInstanceDomain" + [disabled]=" + ([ + serviceType === PolicyComponentServiceType.ADMIN + ? 'iam.policy.write' + : serviceType === PolicyComponentServiceType.MGMT + ? 'policy.write' + : '' + ] + | hasRole + | async) === false + " > {{ 'POLICY.DATA.SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN' | translate }}
-
-
diff --git a/console/src/app/modules/policies/general-settings/general-settings.component.html b/console/src/app/modules/policies/general-settings/general-settings.component.html index 229c1593c0..a465cf3466 100644 --- a/console/src/app/modules/policies/general-settings/general-settings.component.html +++ b/console/src/app/modules/policies/general-settings/general-settings.component.html @@ -1,17 +1,27 @@ +

{{ 'SETTING.DEFAULTLANGUAGE' | translate }}

+
-

{{ 'SETTING.DEFAULTLANGUAGE' | translate }}

+ {{ 'SETTING.DEFAULTLANGUAGE' | translate }} - + {{ lang }} - {{ 'SETTING.LANGUAGE.' + lang | translate }} +
-
diff --git a/console/src/app/modules/policies/general-settings/general-settings.module.ts b/console/src/app/modules/policies/general-settings/general-settings.module.ts index cc59a79450..98f0e4cb53 100644 --- a/console/src/app/modules/policies/general-settings/general-settings.module.ts +++ b/console/src/app/modules/policies/general-settings/general-settings.module.ts @@ -5,6 +5,7 @@ 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'; @@ -20,6 +21,7 @@ import { GeneralSettingsComponent } from './general-settings.component'; FormFieldModule, MatProgressSpinnerModule, MatSelectModule, + HasRolePipeModule, TranslateModule, ], exports: [GeneralSettingsComponent], diff --git a/console/src/app/modules/policies/login-policy/factor-table/dialog-add-type/dialog-add-type.component.html b/console/src/app/modules/policies/login-policy/factor-table/dialog-add-type/dialog-add-type.component.html new file mode 100644 index 0000000000..ba67725551 --- /dev/null +++ b/console/src/app/modules/policies/login-policy/factor-table/dialog-add-type/dialog-add-type.component.html @@ -0,0 +1,29 @@ +

+ {{ data.title | translate }} +

+
+

{{ data.desc | translate }}

+ + + {{ 'MFA.TYPE' | translate }} + + + {{ + (data.componentType === LoginMethodComponentType.SecondFactor + ? 'MFA.SECONDFACTORTYPES.' + : LoginMethodComponentType.MultiFactor + ? 'MFA.MULTIFACTORTYPES.' + : '') + mfa | translate + }} + + + +
+
+ + +
diff --git a/console/src/app/modules/policies/login-policy/mfa-table/dialog-add-type/dialog-add-type.component.scss b/console/src/app/modules/policies/login-policy/factor-table/dialog-add-type/dialog-add-type.component.scss similarity index 100% rename from console/src/app/modules/policies/login-policy/mfa-table/dialog-add-type/dialog-add-type.component.scss rename to console/src/app/modules/policies/login-policy/factor-table/dialog-add-type/dialog-add-type.component.scss diff --git a/console/src/app/modules/policies/login-policy/mfa-table/dialog-add-type/dialog-add-type.component.ts b/console/src/app/modules/policies/login-policy/factor-table/dialog-add-type/dialog-add-type.component.ts similarity index 94% rename from console/src/app/modules/policies/login-policy/mfa-table/dialog-add-type/dialog-add-type.component.ts rename to console/src/app/modules/policies/login-policy/factor-table/dialog-add-type/dialog-add-type.component.ts index 47da98af6e..5bc8a324cb 100644 --- a/console/src/app/modules/policies/login-policy/mfa-table/dialog-add-type/dialog-add-type.component.ts +++ b/console/src/app/modules/policies/login-policy/factor-table/dialog-add-type/dialog-add-type.component.ts @@ -16,8 +16,8 @@ export class DialogAddTypeComponent { public LoginMethodComponentType: any = LoginMethodComponentType; public availableMfaTypes: Array = []; public newMfaType!: MultiFactorType | SecondFactorType; - constructor(public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: any) { + + constructor(public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: any) { this.availableMfaTypes = data.types; } diff --git a/console/src/app/modules/policies/login-policy/mfa-table/mfa-table.component.html b/console/src/app/modules/policies/login-policy/factor-table/factor-table.component.html similarity index 100% rename from console/src/app/modules/policies/login-policy/mfa-table/mfa-table.component.html rename to console/src/app/modules/policies/login-policy/factor-table/factor-table.component.html diff --git a/console/src/app/modules/policies/login-policy/mfa-table/mfa-table.component.scss b/console/src/app/modules/policies/login-policy/factor-table/factor-table.component.scss similarity index 100% rename from console/src/app/modules/policies/login-policy/mfa-table/mfa-table.component.scss rename to console/src/app/modules/policies/login-policy/factor-table/factor-table.component.scss diff --git a/console/src/app/modules/policies/login-policy/factor-table/factor-table.component.spec.ts b/console/src/app/modules/policies/login-policy/factor-table/factor-table.component.spec.ts new file mode 100644 index 0000000000..7eebd02231 --- /dev/null +++ b/console/src/app/modules/policies/login-policy/factor-table/factor-table.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { FactorTableComponent } from './factor-table.component'; + +describe('FactorTableComponent', () => { + let component: FactorTableComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [FactorTableComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(FactorTableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/console/src/app/modules/policies/login-policy/mfa-table/mfa-table.component.ts b/console/src/app/modules/policies/login-policy/factor-table/factor-table.component.ts similarity index 65% rename from console/src/app/modules/policies/login-policy/mfa-table/mfa-table.component.ts rename to console/src/app/modules/policies/login-policy/factor-table/factor-table.component.ts index 91557701aa..989768ffdb 100644 --- a/console/src/app/modules/policies/login-policy/mfa-table/mfa-table.component.ts +++ b/console/src/app/modules/policies/login-policy/factor-table/factor-table.component.ts @@ -6,16 +6,16 @@ import { BehaviorSubject, Observable } from 'rxjs'; import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum'; import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component'; import { - AddMultiFactorToLoginPolicyRequest as AdminAddMultiFactorToLoginPolicyRequest, - AddSecondFactorToLoginPolicyRequest as AdminAddSecondFactorToLoginPolicyRequest, - RemoveMultiFactorFromLoginPolicyRequest as AdminRemoveMultiFactorFromLoginPolicyRequest, - RemoveSecondFactorFromLoginPolicyRequest as AdminRemoveSecondFactorFromLoginPolicyRequest, + AddMultiFactorToLoginPolicyRequest as AdminAddMultiFactorToLoginPolicyRequest, + AddSecondFactorToLoginPolicyRequest as AdminAddSecondFactorToLoginPolicyRequest, + RemoveMultiFactorFromLoginPolicyRequest as AdminRemoveMultiFactorFromLoginPolicyRequest, + RemoveSecondFactorFromLoginPolicyRequest as AdminRemoveSecondFactorFromLoginPolicyRequest, } from 'src/app/proto/generated/zitadel/admin_pb'; import { - AddMultiFactorToLoginPolicyRequest as MgmtAddMultiFactorToLoginPolicyRequest, - AddSecondFactorToLoginPolicyRequest as MgmtAddSecondFactorToLoginPolicyRequest, - RemoveMultiFactorFromLoginPolicyRequest as MgmtRemoveMultiFactorFromLoginPolicyRequest, - RemoveSecondFactorFromLoginPolicyRequest as MgmtRemoveSecondFactorFromLoginPolicyRequest, + AddMultiFactorToLoginPolicyRequest as MgmtAddMultiFactorToLoginPolicyRequest, + AddSecondFactorToLoginPolicyRequest as MgmtAddSecondFactorToLoginPolicyRequest, + RemoveMultiFactorFromLoginPolicyRequest as MgmtRemoveMultiFactorFromLoginPolicyRequest, + RemoveSecondFactorFromLoginPolicyRequest as MgmtRemoveSecondFactorFromLoginPolicyRequest, } from 'src/app/proto/generated/zitadel/management_pb'; import { MultiFactorType, SecondFactorType } from 'src/app/proto/generated/zitadel/policy_pb'; import { AdminService } from 'src/app/services/admin.service'; @@ -30,11 +30,11 @@ export enum LoginMethodComponentType { } @Component({ - selector: 'cnsl-mfa-table', - templateUrl: './mfa-table.component.html', - styleUrls: ['./mfa-table.component.scss'], + selector: 'cnsl-factor-table', + templateUrl: './factor-table.component.html', + styleUrls: ['./factor-table.component.scss'], }) -export class MfaTableComponent implements OnInit { +export class FactorTableComponent implements OnInit { public LoginMethodComponentType: any = LoginMethodComponentType; @Input() componentType!: LoginMethodComponentType; @Input() public serviceType!: PolicyComponentServiceType; @@ -48,7 +48,7 @@ export class MfaTableComponent implements OnInit { public PolicyComponentServiceType: any = PolicyComponentServiceType; - constructor(public translate: TranslateService, private toast: ToastService, private dialog: MatDialog) { } + constructor(public translate: TranslateService, private toast: ToastService, private dialog: MatDialog) {} public ngOnInit(): void { this.getData(); @@ -65,7 +65,7 @@ export class MfaTableComponent implements OnInit { width: '400px', }); - dialogRef.afterClosed().subscribe(resp => { + dialogRef.afterClosed().subscribe((resp) => { if (resp) { if (this.serviceType === PolicyComponentServiceType.MGMT) { if (this.componentType === LoginMethodComponentType.MultiFactor) { @@ -105,7 +105,6 @@ export class MfaTableComponent implements OnInit { } public addMfa(): void { - let selection: any[] = []; if (this.componentType === LoginMethodComponentType.MultiFactor) { @@ -114,8 +113,8 @@ export class MfaTableComponent implements OnInit { selection = [SecondFactorType.SECOND_FACTOR_TYPE_U2F, SecondFactorType.SECOND_FACTOR_TYPE_OTP]; } - this.mfas.forEach(mfa => { - const index = selection.findIndex(sel => sel === mfa); + this.mfas.forEach((mfa) => { + const index = selection.findIndex((sel) => sel === mfa); if (index > -1) { selection.splice(index, 1); } @@ -137,37 +136,49 @@ export class MfaTableComponent implements OnInit { if (this.componentType === LoginMethodComponentType.MultiFactor) { const req = new MgmtAddMultiFactorToLoginPolicyRequest(); req.setType(mfaType as MultiFactorType); - (this.service as ManagementService).addMultiFactorToLoginPolicy(req).then(() => { - this.refreshPageAfterTimout(2000); - }).catch(error => { - this.toast.showError(error); - }); + (this.service as ManagementService) + .addMultiFactorToLoginPolicy(req) + .then(() => { + this.refreshPageAfterTimout(2000); + }) + .catch((error) => { + this.toast.showError(error); + }); } else if (this.componentType === LoginMethodComponentType.SecondFactor) { const req = new MgmtAddSecondFactorToLoginPolicyRequest(); req.setType(mfaType as SecondFactorType); - (this.service as ManagementService).addSecondFactorToLoginPolicy(req).then(() => { - this.refreshPageAfterTimout(2000); - }).catch(error => { - this.toast.showError(error); - }); + (this.service as ManagementService) + .addSecondFactorToLoginPolicy(req) + .then(() => { + this.refreshPageAfterTimout(2000); + }) + .catch((error) => { + this.toast.showError(error); + }); } } else if (this.serviceType === PolicyComponentServiceType.ADMIN) { if (this.componentType === LoginMethodComponentType.MultiFactor) { const req = new AdminAddMultiFactorToLoginPolicyRequest(); req.setType(mfaType as MultiFactorType); - (this.service as AdminService).addMultiFactorToLoginPolicy(req).then(() => { - this.refreshPageAfterTimout(2000); - }).catch(error => { - this.toast.showError(error); - }); + (this.service as AdminService) + .addMultiFactorToLoginPolicy(req) + .then(() => { + this.refreshPageAfterTimout(2000); + }) + .catch((error) => { + this.toast.showError(error); + }); } else if (this.componentType === LoginMethodComponentType.SecondFactor) { const req = new AdminAddSecondFactorToLoginPolicyRequest(); req.setType(mfaType as SecondFactorType); - (this.service as AdminService).addSecondFactorToLoginPolicy(req).then(() => { - this.refreshPageAfterTimout(2000); - }).catch(error => { - this.toast.showError(error); - }); + (this.service as AdminService) + .addSecondFactorToLoginPolicy(req) + .then(() => { + this.refreshPageAfterTimout(2000); + }) + .catch((error) => { + this.toast.showError(error); + }); } } } @@ -179,39 +190,51 @@ export class MfaTableComponent implements OnInit { if (this.serviceType === PolicyComponentServiceType.MGMT) { if (this.componentType === LoginMethodComponentType.MultiFactor) { - (this.service as ManagementService).listLoginPolicyMultiFactors().then(resp => { - this.mfas = resp.resultList; - this.loadingSubject.next(false); - }).catch(error => { - this.toast.showError(error); - this.loadingSubject.next(false); - }); + (this.service as ManagementService) + .listLoginPolicyMultiFactors() + .then((resp) => { + this.mfas = resp.resultList; + this.loadingSubject.next(false); + }) + .catch((error) => { + this.toast.showError(error); + this.loadingSubject.next(false); + }); } else if (this.componentType === LoginMethodComponentType.SecondFactor) { - (this.service as ManagementService).listLoginPolicySecondFactors().then(resp => { - this.mfas = resp.resultList; - this.loadingSubject.next(false); - }).catch(error => { - this.toast.showError(error); - this.loadingSubject.next(false); - }); + (this.service as ManagementService) + .listLoginPolicySecondFactors() + .then((resp) => { + this.mfas = resp.resultList; + this.loadingSubject.next(false); + }) + .catch((error) => { + this.toast.showError(error); + this.loadingSubject.next(false); + }); } } else if (this.serviceType === PolicyComponentServiceType.ADMIN) { if (this.componentType === LoginMethodComponentType.MultiFactor) { - (this.service as AdminService).listLoginPolicyMultiFactors().then(resp => { - this.mfas = resp.resultList; - this.loadingSubject.next(false); - }).catch(error => { - this.toast.showError(error); - this.loadingSubject.next(false); - }); + (this.service as AdminService) + .listLoginPolicyMultiFactors() + .then((resp) => { + this.mfas = resp.resultList; + this.loadingSubject.next(false); + }) + .catch((error) => { + this.toast.showError(error); + this.loadingSubject.next(false); + }); } else if (this.componentType === LoginMethodComponentType.SecondFactor) { - (this.service as AdminService).listLoginPolicySecondFactors().then(resp => { - this.mfas = resp.resultList; - this.loadingSubject.next(false); - }).catch(error => { - this.toast.showError(error); - this.loadingSubject.next(false); - }); + (this.service as AdminService) + .listLoginPolicySecondFactors() + .then((resp) => { + this.mfas = resp.resultList; + this.loadingSubject.next(false); + }) + .catch((error) => { + this.toast.showError(error); + this.loadingSubject.next(false); + }); } } } diff --git a/console/src/app/modules/policies/login-policy/login-policy.component.html b/console/src/app/modules/policies/login-policy/login-policy.component.html index d717aa68d1..6b76509464 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.component.html +++ b/console/src/app/modules/policies/login-policy/login-policy.component.html @@ -29,7 +29,20 @@ - - +
@@ -64,12 +77,29 @@

{{ 'MFA.LIST.SECONDFACTORDESCRIPTION' | translate }}

- - +
@@ -149,6 +179,17 @@ matTooltip="{{ 'POLICY.DATA.FORCEMFA_DESC' | translate }}" ngDefaultControl [(ngModel)]="loginData.allowUsernamePassword" + [disabled]=" + ([ + serviceType === PolicyComponentServiceType.ADMIN + ? 'iam.policy.write' + : serviceType === PolicyComponentServiceType.MGMT + ? 'policy.write' + : '' + ] + | hasRole + | async) === false + " > {{ 'POLICY.DATA.ALLOWUSERNAMEPASSWORD' | translate }} @@ -158,7 +199,23 @@ --> @@ -214,7 +329,24 @@
diff --git a/console/src/app/modules/policies/login-policy/login-policy.component.ts b/console/src/app/modules/policies/login-policy/login-policy.component.ts index 449ed21cb1..998a8b8dd6 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.component.ts +++ b/console/src/app/modules/policies/login-policy/login-policy.component.ts @@ -1,6 +1,7 @@ import { Component, Injector, Input, OnInit, Type } from '@angular/core'; import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Duration } from 'google-protobuf/google/protobuf/duration_pb'; +import { take } from 'rxjs'; import { GetLoginPolicyResponse as AdminGetLoginPolicyResponse, UpdateLoginPolicyRequest, @@ -12,12 +13,13 @@ import { } from 'src/app/proto/generated/zitadel/management_pb'; import { LoginPolicy, PasswordlessType } from 'src/app/proto/generated/zitadel/policy_pb'; import { AdminService } from 'src/app/services/admin.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'; import { InfoSectionType } from '../../info-section/info-section.component'; import { PolicyComponentServiceType } from '../policy-component-types.enum'; -import { LoginMethodComponentType } from './mfa-table/mfa-table.component'; +import { LoginMethodComponentType } from './factor-table/factor-table.component'; @Component({ selector: 'cnsl-login-policy', @@ -40,21 +42,24 @@ export class LoginPolicyComponent implements OnInit { public InfoSectionType: any = InfoSectionType; public PasswordlessType: any = PasswordlessType; public lifetimeForm!: FormGroup; - constructor(private toast: ToastService, private injector: Injector, private fb: FormBuilder) { + constructor( + private toast: ToastService, + private injector: Injector, + private fb: FormBuilder, + private authService: GrpcAuthService, + ) { this.lifetimeForm = this.fb.group({ - passwordCheckLifetime: [240, [Validators.required]], - externalLoginCheckLifetime: [12, [Validators.required]], - mfaInitSkipLifetime: [720, [Validators.required]], - secondFactorCheckLifetime: [12, [Validators.required]], - multiFactorCheckLifetime: [12, [Validators.required]], + passwordCheckLifetime: [{ disabled: true, value: 240 }, [Validators.required]], + externalLoginCheckLifetime: [{ disabled: true, value: 12 }, [Validators.required]], + mfaInitSkipLifetime: [{ disabled: true, value: 720 }, [Validators.required]], + secondFactorCheckLifetime: [{ disabled: true, value: 12 }, [Validators.required]], + multiFactorCheckLifetime: [{ disabled: true, value: 12 }, [Validators.required]], }); } private fetchData(): void { this.getData() .then((resp) => { - console.log(resp); - if (resp.policy) { this.loginData = resp.policy; this.loading = false; @@ -107,6 +112,21 @@ export class LoginPolicyComponent implements OnInit { break; } this.fetchData(); + this.authService + .isAllowed( + this.serviceType === PolicyComponentServiceType.ADMIN + ? ['iam.policy.write'] + : this.serviceType === PolicyComponentServiceType.MGMT + ? ['policy.write'] + : [], + ) + .pipe(take(1)) + .subscribe((allowed) => { + console.log(allowed); + if (allowed) { + this.lifetimeForm.enable(); + } + }); } private async getData(): Promise { diff --git a/console/src/app/modules/policies/login-policy/login-policy.module.ts b/console/src/app/modules/policies/login-policy/login-policy.module.ts index 3363fd1f6f..679d4850d7 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.module.ts +++ b/console/src/app/modules/policies/login-policy/login-policy.module.ts @@ -18,13 +18,13 @@ import { InputModule } from 'src/app/modules/input/input.module'; import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module'; import { InfoSectionModule } from '../../info-section/info-section.module'; +import { DialogAddTypeComponent } from './factor-table/dialog-add-type/dialog-add-type.component'; +import { FactorTableComponent } from './factor-table/factor-table.component'; import { LoginPolicyRoutingModule } from './login-policy-routing.module'; import { LoginPolicyComponent } from './login-policy.component'; -import { DialogAddTypeComponent } from './mfa-table/dialog-add-type/dialog-add-type.component'; -import { MfaTableComponent } from './mfa-table/mfa-table.component'; @NgModule({ - declarations: [LoginPolicyComponent, MfaTableComponent, DialogAddTypeComponent], + declarations: [LoginPolicyComponent, FactorTableComponent, DialogAddTypeComponent], imports: [ LoginPolicyRoutingModule, CommonModule, diff --git a/console/src/app/modules/policies/login-policy/mfa-table/dialog-add-type/dialog-add-type.component.html b/console/src/app/modules/policies/login-policy/mfa-table/dialog-add-type/dialog-add-type.component.html deleted file mode 100644 index d51a743512..0000000000 --- a/console/src/app/modules/policies/login-policy/mfa-table/dialog-add-type/dialog-add-type.component.html +++ /dev/null @@ -1,20 +0,0 @@ -

{{data.title | translate}}

-
-

{{data.desc | translate}}

- - - {{'MFA.TYPE' | translate}} - - - {{(data.componentType === LoginMethodComponentType.SecondFactor ? 'MFA.SECONDFACTORTYPES.': - LoginMethodComponentType.MultiFactor ? 'MFA.MULTIFACTORTYPES.': '')+mfa | translate}} - - - -
-
- - -
\ No newline at end of file diff --git a/console/src/app/modules/policies/login-policy/mfa-table/mfa-table.component.spec.ts b/console/src/app/modules/policies/login-policy/mfa-table/mfa-table.component.spec.ts deleted file mode 100644 index 24992ccaff..0000000000 --- a/console/src/app/modules/policies/login-policy/mfa-table/mfa-table.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; - -import { MfaTableComponent } from './mfa-table.component'; - -describe('MfaTableComponent', () => { - let component: MfaTableComponent; - let fixture: ComponentFixture; - - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [MfaTableComponent], - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(MfaTableComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/console/src/app/modules/policies/login-texts/login-texts.component.html b/console/src/app/modules/policies/login-texts/login-texts.component.html index 027d8e6df0..50dfb40796 100644 --- a/console/src/app/modules/policies/login-texts/login-texts.component.html +++ b/console/src/app/modules/policies/login-texts/login-texts.component.html @@ -1,5 +1,10 @@

{{ 'POLICY.LOGIN_TEXTS.TITLE' | translate }}

{{ 'POLICY.LOGIN_TEXTS.DESCRIPTION' | translate }}

+ +
+ +
+

{{ 'POLICY.LOGIN_TEXTS.NEWERVERSIONEXISTS' | translate }}

diff --git a/console/src/app/modules/policies/login-texts/login-texts.component.ts b/console/src/app/modules/policies/login-texts/login-texts.component.ts index da363da730..ca47feb89f 100644 --- a/console/src/app/modules/policies/login-texts/login-texts.component.ts +++ b/console/src/app/modules/policies/login-texts/login-texts.component.ts @@ -93,6 +93,7 @@ const REQUESTMAP = { styleUrls: ['./login-texts.component.scss'], }) export class LoginTextsComponent implements OnInit, OnDestroy { + public loading: boolean = false; public currentPolicyChangeDate!: Timestamp.AsObject | undefined; public newerPolicyChangeDate!: Timestamp.AsObject | undefined; @@ -208,6 +209,7 @@ export class LoginTextsComponent implements OnInit, OnDestroy { } public async loadData(): Promise { + this.loading = true; const reqDefaultInit = REQUESTMAP[this.serviceType].getDefault; reqDefaultInit.setLanguage(this.locale); this.getDefaultInitMessageTextMap$ = from(this.getDefaultValues(reqDefaultInit)).pipe(map((m) => m[this.currentSubMap])); @@ -215,12 +217,14 @@ export class LoginTextsComponent implements OnInit, OnDestroy { const reqCustomInit = REQUESTMAP[this.serviceType].get.setLanguage(this.locale); return this.getCurrentValues(reqCustomInit) .then((policy) => { + this.loading = false; if (policy) { this.totalCustomPolicy = policy; this.getCustomInitMessageTextMap$.next(policy[this.currentSubMap]); } }) .catch((error) => { + this.loading = false; this.toast.showError(error); }); } diff --git a/console/src/app/modules/policies/message-texts/message-texts.component.html b/console/src/app/modules/policies/message-texts/message-texts.component.html index 58e35c8d8b..675406b7da 100644 --- a/console/src/app/modules/policies/message-texts/message-texts.component.html +++ b/console/src/app/modules/policies/message-texts/message-texts.component.html @@ -1,6 +1,10 @@

{{ 'POLICY.MESSAGE_TEXTS.TITLE' | translate }}

{{ 'POLICY.MESSAGE_TEXTS.DESCRIPTION' | translate }}

+
+ +
+
{{ 'POLICY.MESSAGE_TEXTS.TYPE' | translate }} diff --git a/console/src/app/modules/policies/message-texts/message-texts.component.ts b/console/src/app/modules/policies/message-texts/message-texts.component.ts index 8228ee3ee0..08afb7e191 100644 --- a/console/src/app/modules/policies/message-texts/message-texts.component.ts +++ b/console/src/app/modules/policies/message-texts/message-texts.component.ts @@ -37,7 +37,6 @@ import { MessageCustomText } from 'src/app/proto/generated/zitadel/text_pb'; import { AdminService } from 'src/app/services/admin.service'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; import { ManagementService } from 'src/app/services/mgmt.service'; -import { StorageService } from 'src/app/services/storage.service'; import { ToastService } from 'src/app/services/toast.service'; import { InfoSectionType } from '../../info-section/info-section.component'; @@ -275,6 +274,7 @@ const REQUESTMAP = { styleUrls: ['./message-texts.component.scss'], }) export class MessageTextsComponent implements OnInit, OnDestroy { + public loading: boolean = false; public getDefaultInitMessageTextMap$: Observable<{ [key: string]: string }> = of({}); public getCustomInitMessageTextMap$: BehaviorSubject<{ [key: string]: string | boolean }> = new BehaviorSubject({}); // boolean because of isDefault @@ -400,7 +400,6 @@ export class MessageTextsComponent implements OnInit, OnDestroy { private toast: ToastService, private injector: Injector, private dialog: MatDialog, - private storageService: StorageService, ) {} ngOnInit(): void { @@ -493,11 +492,14 @@ export class MessageTextsComponent implements OnInit, OnDestroy { } const reqCustomInit = REQUESTMAP[this.serviceType][type].get.setLanguage(this.locale); + this.loading = true; return this.getCurrentValues(type, reqCustomInit) ?.then((data) => { + this.loading = false; this.getCustomInitMessageTextMap$.next(data); }) .catch((error) => { + this.loading = false; this.toast.showError(error); }); } diff --git a/console/src/app/modules/policies/notification-settings/notification-settings.component.html b/console/src/app/modules/policies/notification-settings/notification-settings.component.html index a56addc273..9fbfeb9936 100644 --- a/console/src/app/modules/policies/notification-settings/notification-settings.component.html +++ b/console/src/app/modules/policies/notification-settings/notification-settings.component.html @@ -1,9 +1,9 @@ +

{{ 'SETTING.SMTP.TITLE' | translate }}

+
-

{{ 'SETTING.SMTP.TITLE' | translate }}

- {{ 'POLICY.PRIVATELABELING.ACTIVATEPREVIEW' | translate }} @@ -391,7 +402,18 @@ class="toggle" color="primary" ngDefaultControl - [disabled]="view === View.CURRENT" + [disabled]=" + view === View.CURRENT || + ([ + serviceType === PolicyComponentServiceType.ADMIN + ? 'iam.policy.write' + : serviceType === PolicyComponentServiceType.MGMT + ? 'policy.write' + : '' + ] + | hasRole + | async) === false + " [(ngModel)]="view === View.CURRENT ? data.hideLoginNameSuffix : previewData.hideLoginNameSuffix" (change)="savePolicy()" > @@ -402,9 +424,20 @@ class="toggle" color="primary" ngDefaultControl - [disabled]="view === View.CURRENT" [(ngModel)]="view === View.CURRENT ? data.disableWatermark : previewData.disableWatermark" (change)="savePolicy()" + [disabled]=" + view === View.CURRENT || + ([ + serviceType === PolicyComponentServiceType.ADMIN + ? 'iam.policy.write' + : serviceType === PolicyComponentServiceType.MGMT + ? 'policy.write' + : '' + ] + | hasRole + | async) === false + " > {{ 'POLICY.DATA.DISABLEWATERMARK' | translate }} diff --git a/console/src/app/modules/policies/private-labeling-policy/private-labeling-policy.module.ts b/console/src/app/modules/policies/private-labeling-policy/private-labeling-policy.module.ts index eea320d516..a2d0f9390b 100644 --- a/console/src/app/modules/policies/private-labeling-policy/private-labeling-policy.module.ts +++ b/console/src/app/modules/policies/private-labeling-policy/private-labeling-policy.module.ts @@ -12,6 +12,7 @@ import { MatTooltipModule } from '@angular/material/tooltip'; import { TranslateModule } from '@ngx-translate/core'; import { ColorChromeModule } from 'ngx-color/chrome'; import { HasRoleModule } from 'src/app/directives/has-role/has-role.module'; +import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module'; import { DropzoneModule } from '../../../directives/dropzone/dropzone.module'; import { CardModule } from '../../card/card.module'; @@ -42,6 +43,7 @@ import { PrivateLabelingPolicyComponent } from './private-labeling-policy.compon TranslateModule, DetailLayoutModule, DropzoneModule, + HasRolePipeModule, MatProgressSpinnerModule, MatExpansionModule, InfoSectionModule, diff --git a/console/src/app/modules/settings-list/settings.ts b/console/src/app/modules/settings-list/settings.ts index 8b9edda78e..b67e106946 100644 --- a/console/src/app/modules/settings-list/settings.ts +++ b/console/src/app/modules/settings-list/settings.ts @@ -1,72 +1,125 @@ +import { PolicyComponentServiceType } from '../policies/policy-component-types.enum'; import { SidenavSetting } from '../sidenav/sidenav.component'; export const GENERAL: SidenavSetting = { id: 'general', i18nKey: 'SETTINGS.LIST.GENERAL', + requiredRoles: { + [PolicyComponentServiceType.ADMIN]: ['iam.policy.read'], + }, }; export const OIDC: SidenavSetting = { id: 'oidc', i18nKey: 'SETTINGS.LIST.OIDC', + requiredRoles: { + [PolicyComponentServiceType.ADMIN]: ['iam.policy.read'], + }, }; export const SECRETS: SidenavSetting = { id: 'secrets', i18nKey: 'SETTINGS.LIST.SECRETS', + requiredRoles: { + [PolicyComponentServiceType.ADMIN]: ['iam.policy.read'], + }, }; export const LOGIN: SidenavSetting = { id: 'login', i18nKey: 'SETTINGS.LIST.LOGIN', groupI18nKey: 'SETTINGS.GROUPS.LOGIN', + requiredRoles: { + [PolicyComponentServiceType.MGMT]: ['policy.read'], + [PolicyComponentServiceType.ADMIN]: ['iam.policy.read'], + }, }; export const DOMAIN: SidenavSetting = { id: 'domain', i18nKey: 'SETTINGS.LIST.DOMAIN', groupI18nKey: 'SETTINGS.GROUPS.DOMAIN', + requiredRoles: { + [PolicyComponentServiceType.MGMT]: ['policy.read'], + [PolicyComponentServiceType.ADMIN]: ['iam.policy.read'], + }, }; export const LOCKOUT: SidenavSetting = { id: 'lockout', i18nKey: 'SETTINGS.LIST.LOCKOUT', groupI18nKey: 'SETTINGS.GROUPS.LOGIN', + requiredRoles: { + [PolicyComponentServiceType.MGMT]: ['policy.read'], + [PolicyComponentServiceType.ADMIN]: ['iam.policy.read'], + }, }; export const COMPLEXITY: SidenavSetting = { id: 'complexity', i18nKey: 'SETTINGS.LIST.COMPLEXITY', groupI18nKey: 'SETTINGS.GROUPS.LOGIN', + requiredRoles: { + [PolicyComponentServiceType.MGMT]: ['policy.read'], + [PolicyComponentServiceType.ADMIN]: ['iam.policy.read'], + }, }; -export const IDP: SidenavSetting = { id: 'idp', i18nKey: 'SETTINGS.LIST.IDP', groupI18nKey: 'SETTINGS.GROUPS.LOGIN' }; +export const IDP: SidenavSetting = { + id: 'idp', + i18nKey: 'SETTINGS.LIST.IDP', + groupI18nKey: 'SETTINGS.GROUPS.LOGIN', + requiredRoles: { + [PolicyComponentServiceType.MGMT]: ['policy.read'], + [PolicyComponentServiceType.ADMIN]: ['iam.policy.read'], + }, +}; export const NOTIFICATIONS: SidenavSetting = { id: 'notifications', i18nKey: 'SETTINGS.LIST.NOTIFICATIONS', groupI18nKey: 'SETTINGS.GROUPS.NOTIFICATIONS', + requiredRoles: { + [PolicyComponentServiceType.ADMIN]: ['iam.policy.read'], + }, }; export const MESSAGETEXTS: SidenavSetting = { id: 'messagetexts', i18nKey: 'SETTINGS.LIST.MESSAGETEXTS', groupI18nKey: 'SETTINGS.GROUPS.APPEARANCE', + requiredRoles: { + [PolicyComponentServiceType.MGMT]: ['policy.read'], + [PolicyComponentServiceType.ADMIN]: ['iam.policy.read'], + }, }; export const LOGINTEXTS: SidenavSetting = { id: 'logintexts', i18nKey: 'SETTINGS.LIST.LOGINTEXTS', groupI18nKey: 'SETTINGS.GROUPS.APPEARANCE', + requiredRoles: { + [PolicyComponentServiceType.MGMT]: ['policy.read'], + [PolicyComponentServiceType.ADMIN]: ['iam.policy.read'], + }, }; export const PRIVACYPOLICY: SidenavSetting = { id: 'privacypolicy', i18nKey: 'SETTINGS.LIST.PRIVACYPOLICY', groupI18nKey: 'SETTINGS.GROUPS.OTHER', + requiredRoles: { + [PolicyComponentServiceType.MGMT]: ['policy.read'], + [PolicyComponentServiceType.ADMIN]: ['iam.policy.read'], + }, }; export const BRANDING: SidenavSetting = { id: 'branding', i18nKey: 'SETTINGS.LIST.BRANDING', groupI18nKey: 'SETTINGS.GROUPS.APPEARANCE', + requiredRoles: { + [PolicyComponentServiceType.MGMT]: ['policy.read'], + [PolicyComponentServiceType.ADMIN]: ['iam.policy.read'], + }, }; diff --git a/console/src/app/modules/sidenav/sidenav.component.html b/console/src/app/modules/sidenav/sidenav.component.html index bc1309041f..d7338c1e7c 100644 --- a/console/src/app/modules/sidenav/sidenav.component.html +++ b/console/src/app/modules/sidenav/sidenav.component.html @@ -28,6 +28,11 @@