feat(console): JWT IDP, cleanup login policy, update deps (#2438)

* idp cleanup

* lint

* jwtidp service, create, detail, assets

* idp detail, info row

* detail actions

* delete idp, fix state change

* lint

* cli core

* cdk material

* chore(deps-dev): bump karma-jasmine-html-reporter in /console (#2446)

Bumps [karma-jasmine-html-reporter](https://github.com/dfederm/karma-jasmine-html-reporter) from 1.6.0 to 1.7.0.
- [Release notes](https://github.com/dfederm/karma-jasmine-html-reporter/releases)
- [Commits](https://github.com/dfederm/karma-jasmine-html-reporter/compare/v1.6.0...v1.7.0)

---
updated-dependencies:
- dependency-name: karma-jasmine-html-reporter
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* update deps

* lock

* disable actions, user grant link to user, add granted org desc

* lint

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
Max Peintner
2021-10-21 08:29:13 +02:00
committed by GitHub
parent dbec8e164d
commit 1c20ea5a50
67 changed files with 2571 additions and 1947 deletions

View File

@@ -82,7 +82,7 @@ export class GrantedProjectGridComponent implements OnChanges {
private async getPrefixedItem(key: string): Promise<string | null> {
const org = this.storage.getItem<Org.AsObject>(StorageKey.organization, StorageLocation.session) as Org.AsObject;
return localStorage.getItem(`${org.id}:${key}`);
return localStorage.getItem(`${org?.id}:${key}`);
}
private async setPrefixedItem(key: string, value: any): Promise<void> {

View File

@@ -1,148 +1,153 @@
<app-meta-layout>
<div class="max-width-container">
<div class="head" *ngIf="project?.id">
<a [routerLink]="[ '/projects' ]" mat-icon-button>
<mat-icon class="icon">arrow_back</mat-icon>
</a>
<h1>{{ 'PROJECT.PAGES.TITLE' | translate }} {{project?.name}}</h1>
<div class="max-width-container">
<div class="head" *ngIf="project?.id">
<a [routerLink]="[ '/projects' ]" mat-icon-button>
<mat-icon class="icon">arrow_back</mat-icon>
</a>
<h1>{{ 'PROJECT.PAGES.TITLE' | translate }} {{project?.name}}</h1>
<span class="fill-space"></span>
<span class="fill-space"></span>
<ng-template appHasRole [appHasRole]="['project.write:'+projectId, 'project.write']">
<button class="actions-trigger" mat-raised-button color="primary" [matMenuTriggerFor]="actions">
<span>{{'ACTIONS.ACTIONS' | translate}}</span>
<mat-icon class="icon">keyboard_arrow_down</mat-icon>
</button>
<mat-menu #actions="matMenu" xPosition="before">
<button mat-menu-item (click)="openNameDialog()"
aria-label="Edit project name" *ngIf="isZitadel === false">
{{'ACTIONS.RENAME' | translate}}
</button>
<ng-template appHasRole [appHasRole]="['project.write:'+projectId, 'project.write']">
<button class="actions-trigger" mat-raised-button color="primary" [matMenuTriggerFor]="actions">
<span>{{'ACTIONS.ACTIONS' | translate}}</span>
<mat-icon class="icon">keyboard_arrow_down</mat-icon>
</button>
<mat-menu #actions="matMenu" xPosition="before">
<button mat-menu-item (click)="openNameDialog()" aria-label="Edit project name" *ngIf="isZitadel === false">
{{'ACTIONS.RENAME' | translate}}
</button>
<button mat-menu-item
[disabled]="isZitadel || (['project.write$', 'project.write:'+ project.id]| hasRole | async) == false"
*ngIf="project?.state === ProjectState.PROJECT_STATE_ACTIVE"
(click)="changeState(ProjectState.PROJECT_STATE_INACTIVE)">
{{'PROJECT.TABLE.DEACTIVATE' | translate}}
</button>
<button mat-menu-item
[disabled]="isZitadel || (['project.write$', 'project.write:'+ project.id]| hasRole | async) == false"
*ngIf="project?.state === ProjectState.PROJECT_STATE_ACTIVE"
(click)="changeState(ProjectState.PROJECT_STATE_INACTIVE)">
{{'PROJECT.TABLE.DEACTIVATE' | translate}}
</button>
<button mat-menu-item
[disabled]="isZitadel || (['project.write$', 'project.write:'+ project.id]| hasRole | async) == false"
*ngIf="project?.state === ProjectState.PROJECT_STATE_INACTIVE"
(click)="changeState(ProjectState.PROJECT_STATE_ACTIVE)">
{{'PROJECT.TABLE.ACTIVATE' | translate}}
</button>
<button mat-menu-item
[disabled]="isZitadel || (['project.write$', 'project.write:'+ project.id]| hasRole | async) == false"
*ngIf="project?.state === ProjectState.PROJECT_STATE_INACTIVE"
(click)="changeState(ProjectState.PROJECT_STATE_ACTIVE)">
{{'PROJECT.TABLE.ACTIVATE' | translate}}
</button>
<ng-template appHasRole [appHasRole]="['project.delete$', 'project.delete:'+projectId]">
<button mat-menu-item matTooltip="{{'ACTIONS.DELETE' | translate}}"
(click)="deleteProject()" aria-label="Edit project name" *ngIf="isZitadel === false">
<span [style.color]="'var(--warn)'">{{'PROJECT.PAGES.DELETE' | translate}}</span>
</button>
</ng-template>
</mat-menu>
</ng-template>
<ng-template appHasRole [appHasRole]="['project.delete$', 'project.delete:'+projectId]">
<button mat-menu-item matTooltip="{{'ACTIONS.DELETE' | translate}}" (click)="deleteProject()"
aria-label="Edit project name" *ngIf="isZitadel === false">
<span [style.color]="'var(--warn)'">{{'PROJECT.PAGES.DELETE' | translate}}</span>
</button>
</ng-template>
</mat-menu>
</ng-template>
<div class="full-width">
<p class="desc">{{ 'PROJECT.PAGES.DESCRIPTION' | translate }}</p>
<p *ngIf="isZitadel" class="zitadel-warning">{{'PROJECT.PAGES.ZITADELPROJECT' | translate}}</p>
</div>
</div>
<ng-container *ngIf="project">
<div class="privatelabel-info">
<h2 class="setting-title">{{'PROJECT.PAGES.PRIVATELABEL.TITLE' | translate}}</h2>
<p class="setting-desc">
<span>{{'PROJECT.PAGES.PRIVATELABEL.'+project.privateLabelingSetting+'.TITLE' | translate}}</span>
<button (click)="openPrivateLabelingDialog()" mat-icon-button><i class="las la-edit"></i></button>
</p>
</div>
<ng-template appHasRole [appHasRole]="['project.app.read:' + project.id, 'project.app.read']">
<app-application-grid *ngIf="grid" [disabled]="isZitadel" (changeView)="grid = false"
[projectId]="projectId"></app-application-grid>
<app-card *ngIf="!grid" title="{{ 'PROJECT.APP.TITLE' | translate }}">
<div class="card-actions" card-actions>
<button mat-icon-button (click)="grid = true">
<i matTooltip="show grid view" class="las la-th-large"></i>
</button>
</div>
<app-applications [disabled]="isZitadel" [projectId]="projectId"></app-applications>
</app-card>
</ng-template>
<ng-container *ngIf="isZitadel == false">
<ng-template appHasRole [appHasRole]="['project.grant.read:' + project.id, 'project.grant.read']">
<app-card title="{{ 'PROJECT.GRANT.TITLE' | translate }}"
description="{{ 'PROJECT.GRANT.DESCRIPTION' | translate }}">
<app-project-grants
[refreshOnPreviousRoutes]="['/projects/'+projectId+'/grants/create','/projects/'+projectId+'/roles/create']"
[disabled]="((['project.grant.write$', 'project.grant.write:'+ project.id]| hasRole | async))== false"
[projectId]="projectId">
</app-project-grants>
</app-card>
</ng-template>
<ng-template appHasRole [appHasRole]="['project.role.read:' + project.id, 'project.role.read']">
<app-card id="roles" title="{{ 'PROJECT.ROLE.TITLE' | translate }}"
description="{{ 'PROJECT.ROLE.DESCRIPTION' | translate }}">
<p>{{'PROJECT.ROLE.OPTIONS' | translate}}</p>
<mat-checkbox [(ngModel)]="project.projectRoleAssertion" (change)="saveProject()"
color="primary">
{{'PROJECT.ROLE.ASSERTION' | translate}}</mat-checkbox>
<p class="desc">{{'PROJECT.ROLE.ASSERTION_DESCRIPTION' | translate}}</p>
<mat-checkbox [(ngModel)]="project.projectRoleCheck" (change)="saveProject()" color="primary">
{{'PROJECT.ROLE.CHECK' | translate}}</mat-checkbox>
<p class="desc">{{'PROJECT.ROLE.CHECK_DESCRIPTION' | translate}}</p>
<mat-checkbox [(ngModel)]="project.hasProjectCheck" (change)="saveProject()" color="primary">
{{'PROJECT.HAS_PROJECT' | translate}}</mat-checkbox>
<p class="desc">{{'PROJECT.HAS_PROJECT_DESCRIPTION' | translate}}</p>
<div class="divider"></div>
<app-project-roles
[disabled]="(['project.role.write$', 'project.role.write:'+ project.id]| hasRole | async) == false"
[actionsVisible]="true" [projectId]="projectId">
</app-project-roles>
</app-card>
</ng-template>
<ng-template appHasRole [appHasRole]="['user.grant.read']">
<app-card *ngIf="project?.id" title="{{ 'GRANTS.PROJECT.TITLE' | translate }}"
description="{{'GRANTS.PROJECT.DESCRIPTION' | translate }}">
<app-user-grants [context]="UserGrantContext.OWNED_PROJECT" [projectId]="projectId"
[refreshOnPreviousRoutes]="['/grant-create/project/'+projectId]"
[disableWrite]="((['user.grant.write$', 'user.grant.write:'+projectId] | hasRole) | async) == false"
[disableDelete]="((['user.grant.delete$','user.grant.delete:'+projectId] | hasRole) | async) == false">
</app-user-grants>
</app-card>
</ng-template>
</ng-container>
</ng-container>
<div class="full-width">
<p class="desc">{{ 'PROJECT.PAGES.DESCRIPTION' | translate }}</p>
<p *ngIf="isZitadel" class="zitadel-warning">{{'PROJECT.PAGES.ZITADELPROJECT' | translate}}</p>
</div>
</div>
<div class="side" metainfo>
<div class="meta-details">
<div class="meta-row">
<span class="first">{{'RESOURCEID' | translate}}:</span>
<span *ngIf="projectId" class="second">{{ projectId }}</span>
</div>
<div class="meta-row">
<span class="first">{{'PROJECT.STATE.TITLE' | translate}}:</span>
<span *ngIf="project && project.state !== undefined" class="state"
[ngClass]="{'active': project.state === ProjectState.PROJECT_STATE_ACTIVE, 'inactive': project.state === ProjectState.PROJECT_STATE_INACTIVE}">{{'PROJECT.STATE.'+project.state
| translate}}</span>
</div>
</div>
<mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true">
<mat-tab label="Details">
<app-contributors *ngIf="project" [loading]="loading$ | async" [totalResult]="totalMemberResult"
[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.id]| hasRole | async) == false">
</app-contributors>
</mat-tab>
<mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="meta-flex-col">
<app-changes *ngIf="project" [changeType]="ChangeType.PROJECT" [id]="project.id"></app-changes>
</mat-tab>
</mat-tab-group>
<ng-container *ngIf="project">
<div class="privatelabel-info">
<h2 class="setting-title">{{'PROJECT.PAGES.PRIVATELABEL.TITLE' | translate}}</h2>
<p class="setting-desc">
<span>{{'PROJECT.PAGES.PRIVATELABEL.'+project.privateLabelingSetting+'.TITLE' | translate}}</span>
<button [disabled]="((['project.write$', 'project.write:'+ project.id]| hasRole | async))== false"
(click)="openPrivateLabelingDialog()" mat-icon-button><i class="las la-edit"></i></button>
</p>
</div>
<ng-template appHasRole [appHasRole]="['project.app.read:' + project.id, 'project.app.read']">
<app-application-grid *ngIf="grid" [disabled]="isZitadel" (changeView)="grid = false" [projectId]="projectId">
</app-application-grid>
<app-card *ngIf="!grid" title="{{ 'PROJECT.APP.TITLE' | translate }}">
<div class="card-actions" card-actions>
<button mat-icon-button (click)="grid = true">
<i matTooltip="show grid view" class="las la-th-large"></i>
</button>
</div>
<app-applications [disabled]="isZitadel" [projectId]="projectId"></app-applications>
</app-card>
</ng-template>
<ng-container *ngIf="isZitadel == false">
<ng-template appHasRole [appHasRole]="['project.grant.read:' + project.id, 'project.grant.read']">
<app-card title="{{ 'PROJECT.GRANT.TITLE' | translate }}"
description="{{ 'PROJECT.GRANT.DESCRIPTION' | translate }}">
<app-project-grants
[refreshOnPreviousRoutes]="['/projects/'+projectId+'/grants/create','/projects/'+projectId+'/roles/create']"
[disabled]="((['project.grant.write$', 'project.grant.write:'+ project.id]| hasRole | async))== false"
[projectId]="projectId">
</app-project-grants>
</app-card>
</ng-template>
<ng-template appHasRole [appHasRole]="['project.role.read:' + project.id, 'project.role.read']">
<app-card id="roles" title="{{ 'PROJECT.ROLE.TITLE' | translate }}"
description="{{ 'PROJECT.ROLE.DESCRIPTION' | translate }}">
<p>{{'PROJECT.ROLE.OPTIONS' | translate}}</p>
<mat-checkbox [(ngModel)]="project.projectRoleAssertion"
[disabled]="((['project.write$', 'project.write:'+ project.id]| hasRole | async))== false"
(change)="saveProject()" color="primary">
{{'PROJECT.ROLE.ASSERTION' | translate}}</mat-checkbox>
<p class="desc">{{'PROJECT.ROLE.ASSERTION_DESCRIPTION' | translate}}</p>
<mat-checkbox [(ngModel)]="project.projectRoleCheck"
[disabled]="((['project.write$', 'project.write:'+ project.id]| hasRole | async))== false"
(change)="saveProject()" color="primary">
{{'PROJECT.ROLE.CHECK' | translate}}</mat-checkbox>
<p class="desc">{{'PROJECT.ROLE.CHECK_DESCRIPTION' | translate}}</p>
<mat-checkbox [(ngModel)]="project.hasProjectCheck"
[disabled]="((['project.write$', 'project.write:'+ project.id]| hasRole | async))== false"
(change)="saveProject()" color="primary">
{{'PROJECT.HAS_PROJECT' | translate}}</mat-checkbox>
<p class="desc">{{'PROJECT.HAS_PROJECT_DESCRIPTION' | translate}}</p>
<div class="divider"></div>
<app-project-roles
[disabled]="(['project.role.write$', 'project.role.write:'+ project.id]| hasRole | async) == false"
[actionsVisible]="true" [projectId]="projectId">
</app-project-roles>
</app-card>
</ng-template>
<ng-template appHasRole [appHasRole]="['user.grant.read']">
<app-card *ngIf="project?.id" title="{{ 'GRANTS.PROJECT.TITLE' | translate }}"
description="{{'GRANTS.PROJECT.DESCRIPTION' | translate }}">
<app-user-grants [context]="UserGrantContext.OWNED_PROJECT" [projectId]="projectId"
[refreshOnPreviousRoutes]="['/grant-create/project/'+projectId]"
[disableWrite]="((['user.grant.write$', 'user.grant.write:'+projectId] | hasRole) | async) == false"
[disableDelete]="((['user.grant.delete$','user.grant.delete:'+projectId] | hasRole) | async) == false">
</app-user-grants>
</app-card>
</ng-template>
</ng-container>
</ng-container>
</div>
<div class="side" metainfo>
<div class="meta-details">
<div class="meta-row">
<span class="first">{{'RESOURCEID' | translate}}:</span>
<span *ngIf="projectId" class="second">{{ projectId }}</span>
</div>
<div class="meta-row">
<span class="first">{{'PROJECT.STATE.TITLE' | translate}}:</span>
<span *ngIf="project && project.state !== undefined" class="state"
[ngClass]="{'active': project.state === ProjectState.PROJECT_STATE_ACTIVE, 'inactive': project.state === ProjectState.PROJECT_STATE_INACTIVE}">{{'PROJECT.STATE.'+project.state
| translate}}</span>
</div>
</div>
</app-meta-layout>
<mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true">
<mat-tab label="Details">
<app-contributors *ngIf="project" [loading]="loading$ | async" [totalResult]="totalMemberResult"
[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.id]| hasRole | async) == false">
</app-contributors>
</mat-tab>
<mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="meta-flex-col">
<app-changes *ngIf="project" [changeType]="ChangeType.PROJECT" [id]="project.id"></app-changes>
</mat-tab>
</mat-tab-group>
</div>
</app-meta-layout>

View File

@@ -106,7 +106,7 @@ export class OwnedProjectGridComponent implements OnChanges {
private async getPrefixedItem(key: string): Promise<string | null> {
const org = this.storage.getItem<Org.AsObject>(StorageKey.organization, StorageLocation.session) as Org.AsObject;
return localStorage.getItem(`${org.id}:${key}`);
return localStorage.getItem(`${org?.id}:${key}`);
}
private async setPrefixedItem(key: string, value: any): Promise<void> {

View File

@@ -1,57 +1,64 @@
<app-detail-layout [backRouterLink]="[ '/projects', projectid]" title="{{ 'PROJECT.GRANT.DETAIL.TITLE' | translate }}"
description="{{ 'PROJECT.GRANT.DETAIL.DESC' | translate }}">
<div class="master-row">
<div class="left-col">
<div class="row">
<span class="first">{{'PROJECT.GRANT.DETAIL.PROJECTNAME' | translate}}</span>
<span class="fill-space"></span>
<span>{{grant?.projectName}}</span>
</div>
<div class="row">
<span class="first">{{'PROJECT.GRANT.DETAIL.RESOURCEOWNER' | translate}}</span>
<span class="fill-space"></span>
<span>{{grant?.details?.resourceOwner}}</span>
</div>
</div>
description="{{ 'PROJECT.GRANT.DETAIL.DESC' | translate }}">
<div class="master-row">
<div class="left-col">
<div class="row">
<span class="first">{{'PROJECT.GRANT.DETAIL.PROJECTNAME' | translate}}</span>
<span class="fill-space"></span>
<div>
<button mat-stroked-button color="warn" *ngIf="grant?.state === ProjectGrantState.PROJECTGRANTSTATE_ACTIVE"
(click)="changeState(ProjectGrantState.PROJECTGRANTSTATE_INACTIVE)">{{'USER.PAGES.DEACTIVATE' | translate}}</button>
<button mat-stroked-button color="warn"
*ngIf="grant?.state === ProjectGrantState.PROJECTGRANTSTATE_INACTIVE"
(click)="changeState(ProjectGrantState.PROJECTGRANTSTATE_ACTIVE)">{{'USER.PAGES.REACTIVATE' | translate}}</button>
</div>
<span>{{grant?.projectName}}</span>
</div>
<div class="row">
<span class="first">{{'PROJECT.GRANT.DETAIL.RESOURCEOWNER' | translate}}</span>
<span class="fill-space"></span>
<span>{{grant?.projectOwnerName}}</span>
</div>
<div class="row">
<span class="first">{{'PROJECT.GRANT.DETAIL.GRANTEDORG' | translate}}</span>
<span class="fill-space"></span>
<span>{{grant?.grantedOrgName}}</span>
</div>
</div>
<cnsl-form-field class="formfield" appearance="outline" *ngIf="grant && grant.grantedRoleKeysList">
<cnsl-label>{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}</cnsl-label>
<mat-select [(ngModel)]="grant.grantedRoleKeysList" multiple (selectionChange)="updateRoles($event)">
<mat-option *ngFor="let role of projectRoleOptions" [value]="role.key">
{{role.key}}
</mat-option>
</mat-select>
</cnsl-form-field>
<span class="fill-space"></span>
<div class="divider"></div>
<div>
<button mat-stroked-button color="warn" *ngIf="grant?.state === ProjectGrantState.PROJECTGRANTSTATE_ACTIVE"
(click)="changeState(ProjectGrantState.PROJECTGRANTSTATE_INACTIVE)">{{'USER.PAGES.DEACTIVATE' |
translate}}</button>
<button mat-stroked-button color="warn" *ngIf="grant?.state === ProjectGrantState.PROJECTGRANTSTATE_INACTIVE"
(click)="changeState(ProjectGrantState.PROJECTGRANTSTATE_ACTIVE)">{{'USER.PAGES.REACTIVATE' |
translate}}</button>
</div>
</div>
<h1 class="h1">{{ 'PROJECT.GRANT.DETAIL.MEMBERTITLE' | translate }}</h1>
<p class="desc">{{ 'PROJECT.GRANT.DETAIL.MEMBERDESC' | translate }}</p>
<cnsl-form-field class="formfield" appearance="outline" *ngIf="grant && grant.grantedRoleKeysList">
<cnsl-label>{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}</cnsl-label>
<mat-select [(ngModel)]="grant.grantedRoleKeysList" multiple (selectionChange)="updateRoles($event)">
<mat-option *ngFor="let role of projectRoleOptions" [value]="role.key">
{{role.key}}
</mat-option>
</mat-select>
</cnsl-form-field>
<app-members-table *ngIf="grant" style="width: 100%;" [dataSource]="dataSource" [canWrite]="['project.grant.member.write','project.grant.member.write:' + grant.grantId] | hasRole | async"
[memberRoleOptions]="memberRoleOptions" (updateRoles)="updateMemberRoles($event.member, $event.change)"
[factoryLoadFunc]="changePageFactory" (changedSelection)="selection = $event" [refreshTrigger]="changePage">
<button selectactions (click)="removeProjectMemberSelection()"
[disabled]="(['project.grant.member.delete','project.grant.member.delete:' + grant.grantId] | hasRole | async) == false"
matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="del-button" color="warn" mat-raised-button>
<i class="las la-trash"></i>
{{'ACTIONS.SELECTIONDELETE' | translate}}
</button>
<a writeactions color="primary"
[disabled]="(['project.grant.member.write','project.grant.member.write:' + grant.grantId] | hasRole | async) == false"
(click)="openAddMember()" color="primary" mat-raised-button>
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
</a>
</app-members-table>
<div class="divider"></div>
<h1 class="h1">{{ 'PROJECT.GRANT.DETAIL.MEMBERTITLE' | translate }}</h1>
<p class="desc">{{ 'PROJECT.GRANT.DETAIL.MEMBERDESC' | translate }}</p>
<app-members-table *ngIf="grant" style="width: 100%;" [dataSource]="dataSource"
[canWrite]="['project.grant.member.write','project.grant.member.write:' + grant.grantId] | hasRole | async"
[memberRoleOptions]="memberRoleOptions" (updateRoles)="updateMemberRoles($event.member, $event.change)"
[factoryLoadFunc]="changePageFactory" (changedSelection)="selection = $event" [refreshTrigger]="changePage">
<button selectactions (click)="removeProjectMemberSelection()"
[disabled]="(['project.grant.member.delete','project.grant.member.delete:' + grant.grantId] | hasRole | async) == false"
matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="del-button" color="warn" mat-raised-button>
<i class="las la-trash"></i>
{{'ACTIONS.SELECTIONDELETE' | translate}}
</button>
<a writeactions color="primary"
[disabled]="(['project.grant.member.write','project.grant.member.write:' + grant.grantId] | hasRole | async) == false"
(click)="openAddMember()" color="primary" mat-raised-button>
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
</a>
</app-members-table>
</app-detail-layout>

View File

@@ -1,135 +1,136 @@
<div class="max-width-container" *ngIf="error">{{error}}</div>
<app-meta-layout *ngIf="user && (['user.write$','user.write:' + user.id] | hasRole) as canWrite$">
<div class="max-width-container">
<div class="head">
<a (click)="navigateBack()" mat-icon-button>
<mat-icon class="icon">arrow_back</mat-icon>
</a>
<div class="head-row">
<h1>{{user.human ? user.human?.profile?.displayName : user.machine?.name}}</h1>
<p *ngIf="user?.preferredLoginName">{{user?.preferredLoginName}}</p>
</div>
<span class="fill-space"></span>
<ng-template appHasRole [appHasRole]="['user.write$', 'user.write:'+user?.id]">
<button class="actions-trigger" mat-raised-button color="primary" [matMenuTriggerFor]="actions">
<span>{{'ACTIONS.ACTIONS' | translate}}</span>
<mat-icon class="icon">keyboard_arrow_down</mat-icon>
</button>
<mat-menu #actions="matMenu" xPosition="before">
<button mat-menu-item color="warn"
*ngIf="user?.state === UserState.USER_STATE_LOCKED"
(click)="unlockUser()">{{'USER.PAGES.UNLOCK' |
translate}}</button>
<button mat-menu-item
*ngIf="user?.state !== UserState.USER_STATE_INACTIVE"
(click)="changeState(UserState.USER_STATE_INACTIVE)">{{'USER.PAGES.DEACTIVATE' |
translate}}</button>
<button mat-menu-item
*ngIf="user?.state == UserState.USER_STATE_INACTIVE"
(click)="changeState(UserState.USER_STATE_ACTIVE)">{{'USER.PAGES.REACTIVATE' | translate}}</button>
<ng-template appHasRole [appHasRole]="['user.delete$', 'user.delete:'+user?.id]">
<button mat-menu-item matTooltip="{{'USER.PAGES.DELETE' | translate}}"
(click)="deleteUser()"><span [style.color]="'var(--warn)'">{{'USER.PAGES.DELETE' | translate}}</span></button>
</ng-template>
</mat-menu>
</ng-template>
</div>
<mat-progress-bar *ngIf="loading" color="primary" mode="indeterminate"></mat-progress-bar>
<cnsl-info-section class="locked" *ngIf="user?.state === UserState.USER_STATE_LOCKED" type="WARN">{{'USER.PAGES.LOCKEDDESCRIPTION' | translate}}</cnsl-info-section>
<span *ngIf="!loading && !user">{{ 'USER.PAGES.NOUSER' | translate }}</span>
<cnsl-info-row *ngIf="user" [user]="user"></cnsl-info-row>
<ng-template appHasRole [appHasRole]="['user.read$', 'user.read:'+user?.id]">
<app-card *ngIf="user.human" title="{{ 'USER.PROFILE.TITLE' | translate }}">
<app-detail-form [preferredLoginName]="user.preferredLoginName" [disabled]="(canWrite$ | async) == false" [genders]="genders" [languages]="languages"
[username]="user.userName" [user]="user.human" (submitData)="saveProfile($event)">
</app-detail-form>
</app-card>
<app-card *ngIf="user.human" title="{{ 'USER.LOGINMETHODS.TITLE' | translate }}"
description="{{ 'USER.LOGINMETHODS.DESCRIPTION' | translate }}">
<button card-actions class="icon-button" mat-icon-button (click)="refreshUser()" matTooltip="{{'ACTIONS.REFRESH' | translate}}">
<mat-icon class="icon">refresh</mat-icon>
</button>
<app-contact disablePhoneCode="true"
[canWrite]="(['user.write:' + user?.id, 'user.write$'] | hasRole | async)" *ngIf="user?.human"
[human]="user.human" (editType)="openEditDialog($event)" (deletedPhone)="deletePhone()"
(resendEmailVerification)="resendEmailVerification()"
(resendPhoneVerification)="resendPhoneVerification()">
<button pwdAction [disabled]="(canWrite$ | async) == false" (click)="sendSetPasswordNotification()"
mat-stroked-button color="primary" *ngIf="user.state === UserState.USER_STATE_INITIAL">{{
'USER.PASSWORD.RESENDNOTIFICATION' | translate }}</button>
<button emailAction class="resendemail" *ngIf="user.state == UserState.USER_STATE_INITIAL"
mat-stroked-button color="primary" (click)="resendInitEmail()">{{'USER.RESENDINITIALEMAIL' |
translate}}</button>
</app-contact>
</app-card>
<app-external-idps *ngIf="user && user.human && user.id" [userId]="user.id" [service]="mgmtUserService"></app-external-idps>
<app-card *ngIf="user.machine" title="{{ 'USER.MACHINE.TITLE' | translate }}">
<app-detail-form-machine [disabled]="(canWrite$ | async) == false" [username]="user.userName"
[user]="user.machine" (submitData)="saveMachine($event)">
</app-detail-form-machine>
</app-card>
<app-card *ngIf="user.machine && user.id" title="{{ 'USER.MACHINE.KEYSTITLE' | translate }}"
description="{{ 'USER.MACHINE.KEYSDESC' | translate }}">
<app-machine-keys [userId]="user.id"></app-machine-keys>
</app-card>
</ng-template>
<app-passwordless *ngIf="user && user.human" [user]="user"></app-passwordless>
<app-user-mfa *ngIf="user && user.human" [user]="user"></app-user-mfa>
<app-card *ngIf="user?.id" title="{{ 'GRANTS.USER.TITLE' | translate }}"
description="{{'GRANTS.USER.DESCRIPTION' | translate }}">
<app-user-grants [userId]="user.id" [context]="USERGRANTCONTEXT"
[displayedColumns]="['select', 'projectId', 'dates', 'roleNamesList']"
[disableWrite]="((['user.grant.write$'] | hasRole) | async) == false"
[disableDelete]="((['user.grant.delete$'] | hasRole) | async) == false">
</app-user-grants>
</app-card>
<ng-template appHasFeature [appHasFeature]="['metadata.user']">
<cnsl-metadata *ngIf="user" [userId]="user.id"></cnsl-metadata>
</ng-template>
</div>
<div *ngIf="user" class="side" metainfo>
<div class="meta-details">
<div class="meta-row">
<span class="first">{{'RESOURCEID' | translate}}:</span>
<span *ngIf="user?.id" class="second">{{ user.id }}</span>
</div>
<div class="meta-row" *ngIf="user?.preferredLoginName">
<span class="first">{{'USER.PREFERRED_LOGINNAME' | translate}}</span>
<span class="second"><span style="display: block;">{{user.preferredLoginName}}</span></span>
</div>
<div class="meta-row">
<span class="first">{{'USER.PAGES.STATE' | translate}}</span>
<span *ngIf="user && user.state !== undefined" class="state"
[ngClass]="{'active': user.state === UserState.USER_STATE_ACTIVE, 'inactive': user.state === UserState.USER_STATE_INACTIVE}">{{'USER.DATA.STATE'+user.state
| translate}}</span>
</div>
<div class="max-width-container">
<div class="head">
<a (click)="navigateBack()" mat-icon-button>
<mat-icon class="icon">arrow_back</mat-icon>
</a>
<div class="head-row">
<h1>{{user.human ? user.human?.profile?.displayName : user.machine?.name}}</h1>
<p *ngIf="user?.preferredLoginName">{{user?.preferredLoginName}}</p>
</div>
<mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true">
<mat-tab label="Details">
<div class="side-padding">
<ng-template appHasRole [appHasRole]="['user.membership.read']">
<app-memberships [user]="user" [disabled]="(canWrite$ | async) == false"></app-memberships>
</ng-template>
</div>
</mat-tab>
<mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="meta-flex-col">
<app-changes class="changes" [refresh]="refreshChanges$" [changeType]="ChangeType.USER" [id]="user.id">
</app-changes>
</mat-tab>
</mat-tab-group>
<span class="fill-space"></span>
<ng-template appHasRole [appHasRole]="['user.write$', 'user.write:'+user?.id]">
<button class="actions-trigger" mat-raised-button color="primary" [matMenuTriggerFor]="actions">
<span>{{'ACTIONS.ACTIONS' | translate}}</span>
<mat-icon class="icon">keyboard_arrow_down</mat-icon>
</button>
<mat-menu #actions="matMenu" xPosition="before">
<button mat-menu-item color="warn" *ngIf="user?.state === UserState.USER_STATE_LOCKED"
(click)="unlockUser()">{{'USER.PAGES.UNLOCK' |
translate}}</button>
<button mat-menu-item *ngIf="user?.state !== UserState.USER_STATE_INACTIVE"
(click)="changeState(UserState.USER_STATE_INACTIVE)">{{'USER.PAGES.DEACTIVATE' |
translate}}</button>
<button mat-menu-item *ngIf="user?.state == UserState.USER_STATE_INACTIVE"
(click)="changeState(UserState.USER_STATE_ACTIVE)">{{'USER.PAGES.REACTIVATE' | translate}}</button>
<ng-template appHasRole [appHasRole]="['user.delete$', 'user.delete:'+user?.id]">
<button mat-menu-item matTooltip="{{'USER.PAGES.DELETE' | translate}}" (click)="deleteUser()"><span
[style.color]="'var(--warn)'">{{'USER.PAGES.DELETE' | translate}}</span></button>
</ng-template>
</mat-menu>
</ng-template>
</div>
<mat-progress-bar *ngIf="loading" color="primary" mode="indeterminate"></mat-progress-bar>
<cnsl-info-section class="locked" *ngIf="user?.state === UserState.USER_STATE_LOCKED" type="WARN">
{{'USER.PAGES.LOCKEDDESCRIPTION' | translate}}</cnsl-info-section>
<span *ngIf="!loading && !user">{{ 'USER.PAGES.NOUSER' | translate }}</span>
<cnsl-info-row *ngIf="user" [user]="user"></cnsl-info-row>
<ng-template appHasRole [appHasRole]="['user.read$', 'user.read:'+user?.id]">
<app-card *ngIf="user.human" title="{{ 'USER.PROFILE.TITLE' | translate }}">
<app-detail-form [preferredLoginName]="user.preferredLoginName" [disabled]="(canWrite$ | async) == false"
[genders]="genders" [languages]="languages" [username]="user.userName" [user]="user.human"
(submitData)="saveProfile($event)">
</app-detail-form>
</app-card>
<app-card *ngIf="user.human" title="{{ 'USER.LOGINMETHODS.TITLE' | translate }}"
description="{{ 'USER.LOGINMETHODS.DESCRIPTION' | translate }}">
<button card-actions class="icon-button" mat-icon-button (click)="refreshUser()"
matTooltip="{{'ACTIONS.REFRESH' | translate}}">
<mat-icon class="icon">refresh</mat-icon>
</button>
<app-contact disablePhoneCode="true" [canWrite]="(['user.write:' + user?.id, 'user.write$'] | hasRole | async)"
*ngIf="user?.human" [human]="user.human" (editType)="openEditDialog($event)" (deletedPhone)="deletePhone()"
(resendEmailVerification)="resendEmailVerification()" (resendPhoneVerification)="resendPhoneVerification()">
<button pwdAction [disabled]="(canWrite$ | async) == false" (click)="sendSetPasswordNotification()"
mat-stroked-button color="primary" *ngIf="user.state === UserState.USER_STATE_INITIAL">{{
'USER.PASSWORD.RESENDNOTIFICATION' | translate }}</button>
<button emailAction class="resendemail" *ngIf="user.state == UserState.USER_STATE_INITIAL" mat-stroked-button
color="primary" (click)="resendInitEmail()">{{'USER.RESENDINITIALEMAIL' |
translate}}</button>
</app-contact>
</app-card>
<app-external-idps *ngIf="user && user.human && user.id" [userId]="user.id" [service]="mgmtUserService">
</app-external-idps>
<app-card *ngIf="user.machine" title="{{ 'USER.MACHINE.TITLE' | translate }}">
<app-detail-form-machine [disabled]="(canWrite$ | async) == false" [username]="user.userName"
[user]="user.machine" (submitData)="saveMachine($event)">
</app-detail-form-machine>
</app-card>
<app-card *ngIf="user.machine && user.id" title="{{ 'USER.MACHINE.KEYSTITLE' | translate }}"
description="{{ 'USER.MACHINE.KEYSDESC' | translate }}">
<app-machine-keys [userId]="user.id"></app-machine-keys>
</app-card>
</ng-template>
<app-passwordless *ngIf="user && user.human" [user]="user"></app-passwordless>
<app-user-mfa *ngIf="user && user.human" [user]="user"></app-user-mfa>
<app-card *ngIf="user?.id" title="{{ 'GRANTS.USER.TITLE' | translate }}"
description="{{'GRANTS.USER.DESCRIPTION' | translate }}">
<app-user-grants [userId]="user.id" [context]="USERGRANTCONTEXT"
[displayedColumns]="['select', 'projectId', 'dates', 'roleNamesList']"
[disableWrite]="((['user.grant.write$'] | hasRole) | async) == false"
[disableDelete]="((['user.grant.delete$'] | hasRole) | async) == false">
</app-user-grants>
</app-card>
<ng-template appHasFeature [appHasFeature]="['metadata.user']">
<cnsl-metadata *ngIf="user" [userId]="user.id"></cnsl-metadata>
</ng-template>
</div>
<div *ngIf="user" class="side" metainfo>
<div class="meta-details">
<div class="meta-row">
<span class="first">{{'RESOURCEID' | translate}}:</span>
<span *ngIf="user?.id" class="second">{{ user.id }}</span>
</div>
<div class="meta-row" *ngIf="user?.preferredLoginName">
<span class="first">{{'USER.PREFERRED_LOGINNAME' | translate}}</span>
<span class="second"><span style="display: block;">{{user.preferredLoginName}}</span></span>
</div>
<div class="meta-row">
<span class="first">{{'USER.PAGES.STATE' | translate}}</span>
<span *ngIf="user && user.state !== undefined" class="state"
[ngClass]="{'active': user.state === UserState.USER_STATE_ACTIVE, 'inactive': user.state === UserState.USER_STATE_INACTIVE}">{{'USER.DATA.STATE'+user.state
| translate}}</span>
</div>
</div>
<mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true">
<mat-tab label="Details">
<div class="side-padding">
<ng-template appHasRole [appHasRole]="['user.membership.read']">
<app-memberships [user]="user" [disabled]="(canWrite$ | async) == false"></app-memberships>
</ng-template>
</div>
</mat-tab>
<mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="meta-flex-col">
<app-changes class="changes" [refresh]="refreshChanges$" [changeType]="ChangeType.USER" [id]="user.id">
</app-changes>
</mat-tab>
</mat-tab-group>
</div>
</app-meta-layout>

View File

@@ -37,6 +37,8 @@ export class UserDetailComponent implements OnInit {
public EditDialogType: any = EditDialogType;
public refreshChanges$: EventEmitter<void> = new EventEmitter();
public error: string = '';
constructor(
public translate: TranslateService,
private route: ActivatedRoute,
@@ -56,7 +58,8 @@ export class UserDetailComponent implements OnInit {
this.user = resp.user;
}
}).catch(err => {
console.error(err);
this.error = err.message ?? '';
this.toast.showError(err);
});
this.mgmtUserService.listUserMetadata(id, 0, 100, []).then(resp => {