fix(console): permission regex, account switcher null check, restrict app and member create access (#821)

* fix member table disable, gerneal regexp

* fix user session card, app disable

* memberships max count

* fix policy permissions

* permission check for member add dialog

* lint

* rm accounts log

* rm id regex
This commit is contained in:
Max Peintner
2020-10-09 11:37:05 +02:00
committed by GitHub
parent 8fe635d3fd
commit 010a5815f4
24 changed files with 134 additions and 120 deletions
@@ -43,7 +43,7 @@
<app-card title="{{ 'APP.OIDC.TITLE' | translate }}" *ngIf="app && app.oidcConfig">
<div card-actions *ngIf="app?.oidcConfig?.authMethodType !== OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE">
<button mat-stroked-button
<button [disabled]="!canWrite" mat-stroked-button
(click)="regenerateOIDCClientSecret()">{{'APP.OIDC.REGENERATESECRET' | translate}}</button>
</div>
@@ -109,7 +109,7 @@
<mat-form-field class="formfield full-width" appearance="outline">
<mat-label>{{ 'APP.OIDC.REDIRECT' | translate }}</mat-label>
<mat-chip-list #chipRedirectList>
<mat-chip-list [disabled]="!canWrite" #chipRedirectList>
<mat-chip class="chip" *ngFor="let redirect of redirectUrisList" selected
[matTooltip]="!redirect.startsWith('https://') ? ('APP.OIDC.UNSECUREREDIRECT' | translate): ''"
[color]="!redirect.startsWith('https://') ? 'warn': 'green'"
@@ -128,7 +128,7 @@
<mat-form-field class="formfield full-width" appearance="outline">
<mat-label>{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}</mat-label>
<mat-chip-list #chipPostRedirectList>
<mat-chip-list [disabled]="!canWrite" #chipPostRedirectList>
<mat-chip class="chip" *ngFor="let redirect of postLogoutRedirectUrisList" selected
(removed)="remove(redirect, RedirectType.POSTREDIRECT)"
[matTooltip]="!redirect.startsWith('https://') ? ('APP.OIDC.UNSECUREREDIRECT' | translate): ''"
@@ -148,7 +148,7 @@
</div>
<div class="btn-container">
<button class="submit-button" type="submit" color="primary" [disabled]="appForm.invalid"
<button class="submit-button" type="submit" color="primary" [disabled]="appForm.invalid || !canWrite"
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
</div>
</form>
@@ -86,11 +86,16 @@
}
.toggle {
outline: none;
align-self: flex-start;
margin-bottom: 1rem;
margin-right: 1rem;
border-radius: .5rem;
* {
outline: none;
}
i {
margin-right: 1rem;
}
@@ -1,13 +1,14 @@
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
import { Location } from '@angular/common';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { AbstractControl, FormBuilder, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms';
import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { MatButtonToggleChange } from '@angular/material/button-toggle';
import { MatChipInputEvent } from '@angular/material/chips';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Params } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { Subscription } from 'rxjs';
import { take } from 'rxjs/operators';
import {
Application,
AppState,
@@ -18,6 +19,7 @@ import {
OIDCResponseType,
ZitadelDocs,
} from 'src/app/proto/generated/management_pb';
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';
@@ -34,6 +36,7 @@ enum RedirectType {
styleUrls: ['./app-detail.component.scss'],
})
export class AppDetailComponent implements OnInit, OnDestroy {
public canWrite: boolean = false;
public errorMessage: string = '';
public removable: boolean = true;
public addOnBlur: boolean = true;
@@ -78,8 +81,8 @@ export class AppDetailComponent implements OnInit, OnDestroy {
public OIDCApplicationType: any = OIDCApplicationType;
public OIDCAuthMethodType: any = OIDCAuthMethodType;
public redirectControl: FormControl = new FormControl('');
public postRedirectControl: FormControl = new FormControl('');
public redirectControl: FormControl = new FormControl({ value: '', disabled: true });
public postRedirectControl: FormControl = new FormControl({ value: '', disabled: true });
constructor(
@@ -90,18 +93,19 @@ export class AppDetailComponent implements OnInit, OnDestroy {
private _location: Location,
private dialog: MatDialog,
private mgmtService: ManagementService,
private authService: GrpcAuthService,
) {
this.appNameForm = this.fb.group({
state: ['', []],
name: ['', [Validators.required]],
state: [{ value: '', disabled: true }, []],
name: [{ value: '', disabled: true }, [Validators.required]],
});
this.appForm = this.fb.group({
devMode: [false, []],
devMode: [{ value: false, disabled: true }, []],
clientId: [{ value: '', disabled: true }],
responseTypesList: [],
grantTypesList: [],
applicationType: [],
authMethodType: [],
responseTypesList: [{ value: [], disabled: true }],
grantTypesList: [{ value: [], disabled: true }],
applicationType: [{ value: '', disabled: true }],
authMethodType: [{ value: '', disabled: true }],
});
}
@@ -118,36 +122,35 @@ export class AppDetailComponent implements OnInit, OnDestroy {
this.mgmtService.GetIam().then(iam => {
this.isZitadel = iam.toObject().iamProjectId === this.projectId;
});
this.authService.isAllowed(['project.app.write$', 'project.app.write:' + id]).pipe(take(1)).subscribe((allowed) => {
this.canWrite = allowed;
this.mgmtService.GetApplicationById(projectid, id).then(app => {
this.app = app.toObject();
this.appNameForm.patchValue(this.app);
if (allowed) {
this.appNameForm.enable();
this.appForm.enable();
this.redirectControl.enable();
this.postRedirectControl.enable();
}
this.mgmtService.GetApplicationById(projectid, id).then(app => {
this.app = app.toObject();
this.appNameForm.patchValue(this.app);
if (this.app.state !== AppState.APPSTATE_ACTIVE) {
this.appNameForm.controls['name'].disable();
this.appForm.disable();
} else {
this.appNameForm.controls['name'].enable();
this.appForm.enable();
}
if (this.app.oidcConfig?.redirectUrisList) {
this.redirectUrisList = this.app.oidcConfig.redirectUrisList;
// this.redirectControl = new FormControl('', [nativeValidator as ValidatorFn]);
}
if (this.app.oidcConfig?.postLogoutRedirectUrisList) {
this.postLogoutRedirectUrisList = this.app.oidcConfig.postLogoutRedirectUrisList;
// this.postRedirectControl = new FormControl('', [nativeValidator as ValidatorFn]);
}
if (this.app.oidcConfig) {
this.appForm.patchValue(this.app.oidcConfig);
}
}).catch(error => {
console.error(error);
this.toast.showError(error);
this.errorMessage = error.message;
if (this.app.oidcConfig?.redirectUrisList) {
this.redirectUrisList = this.app.oidcConfig.redirectUrisList;
}
if (this.app.oidcConfig?.postLogoutRedirectUrisList) {
this.postLogoutRedirectUrisList = this.app.oidcConfig.postLogoutRedirectUrisList;
}
if (this.app.oidcConfig) {
this.appForm.patchValue(this.app.oidcConfig);
}
}).catch(error => {
console.error(error);
this.toast.showError(error);
this.errorMessage = error.message;
});
});
this.docs = (await this.mgmtService.GetZitadelDocs()).toObject();
}
@@ -160,20 +163,11 @@ export class AppDetailComponent implements OnInit, OnDestroy {
});
} else if (event.value === AppState.APPSTATE_INACTIVE) {
this.mgmtService.DeactivateApplication(this.projectId, this.app.id).then(() => {
this.toast.showInfo('APP.TOAST.REACTIVATED', true);
this.toast.showInfo('APP.TOAST.DEACTIVATED', true);
}).catch((error: any) => {
this.toast.showError(error);
});
}
if (event.value !== AppState.APPSTATE_ACTIVE) {
this.appNameForm.controls['name'].disable();
this.appForm.disable();
} else {
this.appNameForm.controls['name'].enable();
this.appForm.enable();
this.clientId?.disable();
}
}
public add(event: MatChipInputEvent, target: RedirectType): void {
@@ -18,8 +18,8 @@
<app-user-grants *ngIf="projectId && grantId" [context]="UserGrantContext.GRANTED_PROJECT"
[projectId]="projectId" [grantId]="grantId"
[displayedColumns]="['select','user', 'projectId', 'creationDate', 'changeDate', 'roleNamesList']"
[allowWrite]="['user.grant.write','user.grant.write:'+grantId] | hasRole | async"
[allowDelete]="['user.grant.delete','user.grant.delete:'+grantId] | hasRole | async"
[allowWrite]="['user.grant.write$','user.grant.write:'+grantId] | hasRole | async"
[allowDelete]="['user.grant.delete$','user.grant.delete:'+grantId] | hasRole | async"
refreshOnPreviousRoute="/grant-create/project/{{projectId}}/grant/{{grantId}}">
</app-user-grants>
</app-card>
@@ -40,7 +40,7 @@
[membersSubject]="membersSubject" title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}" (addClicked)="openAddMember()"
(showDetailClicked)="showDetail()" (refreshClicked)="loadMembers()"
[disabled]="(['project.grant.member.write', 'project.grant.member.write:'+ project.projectId]| hasRole | async) == false">
[disabled]="(['project.grant.member.write$', 'project.grant.member.write:'+ project.projectId]| hasRole | async) == false">
</app-contributors>
</mat-tab>
<mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="flex-col">
@@ -22,11 +22,11 @@
<span class="fill-space"></span>
<button mat-stroked-button color="warn"
[disabled]="isZitadel || (['project.write', 'project.write:'+ project.projectId]| hasRole | async) == false"
[disabled]="isZitadel || (['project.write$', 'project.write:'+ project.projectId]| hasRole | async) == false"
*ngIf="project?.state === ProjectState.PROJECTSTATE_ACTIVE"
(click)="changeState(ProjectState.PROJECTSTATE_INACTIVE)">{{'PROJECT.TABLE.DEACTIVATE' | translate}}</button>
<button mat-stroked-button color="warn"
[disabled]="isZitadel || (['project.write', 'project.write:'+ project.projectId]| hasRole | async) == false"
[disabled]="isZitadel || (['project.write$', 'project.write:'+ project.projectId]| hasRole | async) == false"
*ngIf="project?.state === ProjectState.PROJECTSTATE_INACTIVE"
(click)="changeState(ProjectState.PROJECTSTATE_ACTIVE)">{{'PROJECT.TABLE.ACTIVATE' | translate}}</button>
@@ -69,7 +69,7 @@
<app-card title="{{ 'PROJECT.GRANT.TITLE' | translate }}"
description="{{ 'PROJECT.GRANT.DESCRIPTION' | translate }}">
<app-project-grants refreshOnPreviousRoute="/projects/{{projectId}}/grants/create"
[disabled]="((['project.grant.write', 'project.grant.write:'+ project.projectId]| hasRole | async) && (['org.global.read']| hasRole | async))== false"
[disabled]="((['project.grant.write$', 'project.grant.write:'+ project.projectId]| hasRole | async))== false"
[projectId]="projectId">
</app-project-grants>
</app-card>
@@ -79,7 +79,7 @@
<app-card title="{{ 'PROJECT.ROLE.TITLE' | translate }}"
description="{{ 'PROJECT.ROLE.DESCRIPTION' | translate }}">
<app-project-roles
[disabled]="(['project.role.write', 'project.role.write:'+ project.projectId]| hasRole | async) == false"
[disabled]="(['project.role.write$', 'project.role.write:'+ project.projectId]| hasRole | async) == false"
[actionsVisible]="true" [projectId]="projectId">
</app-project-roles>
</app-card>
@@ -90,8 +90,8 @@
description="{{'GRANTS.PROJECT.DESCRIPTION' | translate }}">
<app-user-grants [context]="UserGrantContext.OWNED_PROJECT" [projectId]="projectId"
refreshOnPreviousRoute="/grant-create/project/{{projectId}}"
[allowWrite]="(['user.grant.write', 'user.grant.write:'+projectId] | hasRole) | async"
[allowDelete]="(['user.grant.delete','user.grant.delete:'+projectId] | hasRole) | async">
[allowWrite]="(['user.grant.write$', 'user.grant.write:'+projectId] | hasRole) | async"
[allowDelete]="(['user.grant.delete$','user.grant.delete:'+projectId] | hasRole) | async">
</app-user-grants>
</app-card>
</ng-template>
@@ -113,7 +113,7 @@
[membersSubject]="membersSubject" title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}" (addClicked)="openAddMember()"
(showDetailClicked)="showDetail()" (refreshClicked)="loadMembers()"
[disabled]="(['project.member.write', 'project.member.write:'+ project.projectId]| hasRole | async) == false">
[disabled]="(['project.member.write$', 'project.member.write:'+ project.projectId]| hasRole | async) == false">
</app-contributors>
</mat-tab>
<mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="flex-col">
@@ -21,13 +21,13 @@
<ng-container matColumnDef="select">
<th class="selection" mat-header-cell *matHeaderCellDef>
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
<mat-checkbox [disabled]="disabled" color="primary" (change)="$event ? masterToggle() : null"
[checked]="selection.hasValue() && isAllSelected()"
[indeterminate]="selection.hasValue() && !isAllSelected()">
</mat-checkbox>
</th>
<td class="selection" mat-cell *matCellDef="let row">
<mat-checkbox color="primary" (click)="$event.stopPropagation()"
<mat-checkbox [disabled]="disabled" color="primary" (click)="$event.stopPropagation()"
(change)="$event ? selection.toggle(row) : null" [checked]="selection.isSelected(row)">
</mat-checkbox>
</td>