fix(console): partial authconfig prompt, regex permissions (#658)

* fix partial authconfig prompt, domain c perm

* membership read check

* contributor refresh trigger, observe org write

* user permissions, project deactivate

* allow user grants for project.write

* fix: create project

* project create, roles, grants, members

* fix adminurl

* disable user

* disable user actions

* project grant members delete, p g user grant

* lint

* projectgrantcreate fix

Co-authored-by: Fabiennne <fabienne.gerschwiler@gmail.com>
This commit is contained in:
Max Peintner 2020-09-01 13:24:34 +02:00 committed by GitHub
parent bd7e3731c5
commit 0dd49f7430
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 127 additions and 83 deletions

View File

@ -33,8 +33,6 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
@Input() allowCreate: boolean = false; @Input() allowCreate: boolean = false;
@Input() allowDelete: boolean = false; @Input() allowDelete: boolean = false;
@Input() public disabled: boolean = false;
@Input() userId: string = ''; @Input() userId: string = '';
@Input() projectId: string = ''; @Input() projectId: string = '';
@ -85,8 +83,6 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
this.routerLink = ['/grant-create']; this.routerLink = ['/grant-create'];
} }
console.log(this.routerLink);
this.dataSource.loadGrants(this.context, 0, 25, { this.dataSource.loadGrants(this.context, 0, 25, {
projectId: this.projectId, projectId: this.projectId,
grantId: this.grantId, grantId: this.grantId,

View File

@ -65,7 +65,7 @@
<mat-form-field class="form-field" appearance="outline"> <mat-form-field class="form-field" appearance="outline">
<mat-label>{{ 'ROLESLABEL' | translate }}</mat-label> <mat-label>{{ 'ROLESLABEL' | translate }}</mat-label>
<mat-select [(ngModel)]="member.rolesList" multiple <mat-select [(ngModel)]="member.rolesList" multiple
[disabled]="(['org.member.write'] | hasRole | async) == false" [disabled]="(['iam.member.write'] | hasRole | async) == false"
(selectionChange)="updateRoles(member, $event)"> (selectionChange)="updateRoles(member, $event)">
<mat-option *ngFor="let role of memberRoleOptions" [value]="role"> <mat-option *ngFor="let role of memberRoleOptions" [value]="role">
{{ role }} {{ role }}

View File

@ -3,7 +3,7 @@
<app-refresh-table (refreshed)="refreshPage()" [dataSize]="dataSource?.totalResult" <app-refresh-table (refreshed)="refreshPage()" [dataSize]="dataSource?.totalResult"
[timestamp]="dataSource?.viewTimestamp" [selection]="selection" [loading]="dataSource?.loading$ | async"> [timestamp]="dataSource?.viewTimestamp" [selection]="selection" [loading]="dataSource?.loading$ | async">
<ng-template appHasRole actions [appHasRole]="['org.member.delete:'+org?.id,'org.member.delete']"> <ng-template appHasRole actions [appHasRole]="['org.member.delete:'+org?.id,'org.member.delete']">
<button (click)="removeProjectMemberSelection()" matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" <button (click)="removeOrgMemberSelection()" matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}"
class="icon-button" mat-icon-button *ngIf="selection.hasValue()" color="warn"> class="icon-button" mat-icon-button *ngIf="selection.hasValue()" color="warn">
<i class="las la-trash"></i> <i class="las la-trash"></i>
</button> </button>

View File

@ -75,7 +75,7 @@ export class OrgMembersComponent implements AfterViewInit {
); );
} }
public removeProjectMemberSelection(): void { public removeOrgMemberSelection(): void {
Promise.all(this.selection.selected.map(member => { Promise.all(this.selection.selected.map(member => {
return this.mgmtService.RemoveMyOrgMember(member.userId).then(() => { return this.mgmtService.RemoveMyOrgMember(member.userId).then(() => {
this.toast.showInfo('ORG.TOAST.MEMBERREMOVED', true); this.toast.showInfo('ORG.TOAST.MEMBERREMOVED', true);

View File

@ -18,8 +18,8 @@
description="{{'GRANTS.PROJECT.DESCRIPTION' | translate }}"> description="{{'GRANTS.PROJECT.DESCRIPTION' | translate }}">
<app-user-grants [context]="userGrantContext" [projectId]="projectId" [grantId]="grantId" <app-user-grants [context]="userGrantContext" [projectId]="projectId" [grantId]="grantId"
[displayedColumns]="['select','user', 'projectId', 'creationDate', 'changeDate', 'roleNamesList']" [displayedColumns]="['select','user', 'projectId', 'creationDate', 'changeDate', 'roleNamesList']"
[allowCreate]="['project.grant.user.grant.write'] | hasRole | async" [allowCreate]="['user.grant.write','user.grant.write:'+grantId] | hasRole | async"
[allowDelete]="['project.grant.user.grant.delete'] | hasRole | async"> [allowDelete]="['user.grant.delete','user.grant.delete:'+grantId] | hasRole | async">
</app-user-grants> </app-user-grants>
</app-card> </app-card>
</ng-template> </ng-template>
@ -35,10 +35,11 @@
<mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true"> <mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true">
<mat-tab label="Details"> <mat-tab label="Details">
<app-contributors [totalResult]="totalMemberResult" [loading]="loading$ | async" <app-contributors *ngIf="project" [totalResult]="totalMemberResult" [loading]="loading$ | async"
[membersSubject]="membersSubject" title="{{ 'PROJECT.MEMBER.TITLE' | translate }}" [membersSubject]="membersSubject" title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}" (addClicked)="openAddMember()" description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}" (addClicked)="openAddMember()"
(showDetailClicked)="showDetail()" (refreshClicked)="loadMembers()" [disabled]="false"> (showDetailClicked)="showDetail()" (refreshClicked)="loadMembers()"
[disabled]="(['project.grant.member.write', 'project.grant.member.write:'+ project.projectId]| hasRole | async) == false">
</app-contributors> </app-contributors>
</mat-tab> </mat-tab>
<mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="flex-col"> <mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="flex-col">

View File

@ -22,11 +22,11 @@
<span class="fill-space"></span> <span class="fill-space"></span>
<button mat-stroked-button color="warn" <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" class="state-button" *ngIf="project?.state === ProjectState.PROJECTSTATE_ACTIVE" class="state-button"
(click)="changeState(ProjectState.PROJECTSTATE_INACTIVE)">{{'PROJECT.TABLE.DEACTIVATE' | translate}}</button> (click)="changeState(ProjectState.PROJECTSTATE_INACTIVE)">{{'PROJECT.TABLE.DEACTIVATE' | translate}}</button>
<button mat-stroked-button color="warn" <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" class="state-button" *ngIf="project?.state === ProjectState.PROJECTSTATE_INACTIVE" class="state-button"
(click)="changeState(ProjectState.PROJECTSTATE_ACTIVE)">{{'PROJECT.TABLE.ACTIVATE' | translate}}</button> (click)="changeState(ProjectState.PROJECTSTATE_ACTIVE)">{{'PROJECT.TABLE.ACTIVATE' | translate}}</button>
@ -51,17 +51,15 @@
<ng-container *ngIf="project"> <ng-container *ngIf="project">
<ng-template appHasRole [appHasRole]="['project.app.read:' + project.projectId, 'project.app.read']"> <ng-template appHasRole [appHasRole]="['project.app.read:' + project.projectId, 'project.app.read']">
<app-application-grid *ngIf="grid" <app-application-grid *ngIf="grid" [disabled]="isZitadel" (changeView)="grid = false"
[disabled]="project?.state !== ProjectState.PROJECTSTATE_ACTIVE || isZitadel" [projectId]="projectId"></app-application-grid>
(changeView)="grid = false" [projectId]="projectId"></app-application-grid>
<app-card *ngIf="!grid" title="{{ 'PROJECT.APP.TITLE' | translate }}"> <app-card *ngIf="!grid" title="{{ 'PROJECT.APP.TITLE' | translate }}">
<div class="card-actions" card-actions> <div class="card-actions" card-actions>
<button mat-icon-button (click)="grid = true"> <button mat-icon-button (click)="grid = true">
<i matTooltip="show grid view" class="las la-th-large"></i> <i matTooltip="show grid view" class="las la-th-large"></i>
</button> </button>
</div> </div>
<app-applications [disabled]="project?.state !== ProjectState.PROJECTSTATE_ACTIVE || isZitadel" <app-applications [disabled]="isZitadel" [projectId]="projectId"></app-applications>
[projectId]="projectId"></app-applications>
</app-card> </app-card>
</ng-template> </ng-template>
@ -70,7 +68,8 @@
[appHasRole]="['project.grant.read:' + project.projectId, 'project.grant.read']"> [appHasRole]="['project.grant.read:' + project.projectId, 'project.grant.read']">
<app-card title="{{ 'PROJECT.GRANT.TITLE' | translate }}" <app-card title="{{ 'PROJECT.GRANT.TITLE' | translate }}"
description="{{ 'PROJECT.GRANT.DESCRIPTION' | translate }}"> description="{{ 'PROJECT.GRANT.DESCRIPTION' | translate }}">
<app-project-grants [disabled]="project?.state !== ProjectState.PROJECTSTATE_ACTIVE" <app-project-grants
[disabled]="((['project.grant.write', 'project.grant.write:'+ project.projectId]| hasRole | async) && (['org.global.read']| hasRole | async))== false"
[projectId]="projectId"> [projectId]="projectId">
</app-project-grants> </app-project-grants>
</app-card> </app-card>
@ -79,7 +78,8 @@
<ng-template appHasRole [appHasRole]="['project.role.read:' + project.projectId, 'project.role.read']"> <ng-template appHasRole [appHasRole]="['project.role.read:' + project.projectId, 'project.role.read']">
<app-card title="{{ 'PROJECT.ROLE.TITLE' | translate }}" <app-card title="{{ 'PROJECT.ROLE.TITLE' | translate }}"
description="{{ 'PROJECT.ROLE.DESCRIPTION' | translate }}"> description="{{ 'PROJECT.ROLE.DESCRIPTION' | translate }}">
<app-project-roles [disabled]="project?.state !== ProjectState.PROJECTSTATE_ACTIVE" <app-project-roles
[disabled]="(['project.role.write', 'project.role.write:'+ project.projectId]| hasRole | async) == false"
[actionsVisible]="true" [projectId]="projectId"> [actionsVisible]="true" [projectId]="projectId">
</app-project-roles> </app-project-roles>
</app-card> </app-card>
@ -89,9 +89,8 @@
<app-card *ngIf="project?.projectId" title="{{ 'GRANTS.PROJECT.TITLE' | translate }}" <app-card *ngIf="project?.projectId" title="{{ 'GRANTS.PROJECT.TITLE' | translate }}"
description="{{'GRANTS.PROJECT.DESCRIPTION' | translate }}"> description="{{'GRANTS.PROJECT.DESCRIPTION' | translate }}">
<app-user-grants [context]="userGrantContext" [projectId]="projectId" <app-user-grants [context]="userGrantContext" [projectId]="projectId"
[disabled]="project?.state !== ProjectState.PROJECTSTATE_ACTIVE || (['project.write', 'project.write'+ project.projectId]| hasRole | async) == false" [allowCreate]="(['user.grant.write', 'user.grant.write:'+projectId] | hasRole) | async"
[allowCreate]="(['user.grant.write'] | hasRole) | async" [allowDelete]="(['user.grant.delete','user.grant.delete:'+projectId] | hasRole) | async">
[allowDelete]="(['user.grant.delete'] | hasRole) | async">
</app-user-grants> </app-user-grants>
</app-card> </app-card>
</ng-template> </ng-template>
@ -109,10 +108,11 @@
<mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true"> <mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true">
<mat-tab label="Details"> <mat-tab label="Details">
<app-contributors [loading]="loading$ | async" [totalResult]="totalMemberResult" <app-contributors *ngIf="project" [loading]="loading$ | async" [totalResult]="totalMemberResult"
[membersSubject]="membersSubject" title="{{ 'PROJECT.MEMBER.TITLE' | translate }}" [membersSubject]="membersSubject" title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}" (addClicked)="openAddMember()" description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}" (addClicked)="openAddMember()"
(showDetailClicked)="showDetail()" (refreshClicked)="loadMembers()" [disabled]="false"> (showDetailClicked)="showDetail()" (refreshClicked)="loadMembers()"
[disabled]="(['project.member.write', 'project.member.write:'+ project.projectId]| hasRole | async) == false">
</app-contributors> </app-contributors>
</mat-tab> </mat-tab>
<mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="flex-col"> <mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="flex-col">

View File

@ -61,7 +61,7 @@
<p class="n-items" *ngIf="!loading && items.length === 0">{{'PROJECT.PAGES.NOITEMS' | translate}}</p> <p class="n-items" *ngIf="!loading && items.length === 0">{{'PROJECT.PAGES.NOITEMS' | translate}}</p>
<ng-template appHasRole [appHasRole]="['project.write']"> <ng-template appHasRole [appHasRole]="['project.create']">
<div class="add-project-button card" (click)="addItem()"> <div class="add-project-button card" (click)="addItem()">
<mat-icon class="icon">add</mat-icon> <mat-icon class="icon">add</mat-icon>
<span>{{'PROJECT.PAGES.ADDNEW' | translate}}</span> <span>{{'PROJECT.PAGES.ADDNEW' | translate}}</span>

View File

@ -11,7 +11,7 @@
<app-refresh-table (refreshed)="refreshPage()" [dataSize]="totalResult" [timestamp]="viewTimestamp" <app-refresh-table (refreshed)="refreshPage()" [dataSize]="totalResult" [timestamp]="viewTimestamp"
[selection]="selection" [loading]="loading$ | async"> [selection]="selection" [loading]="loading$ | async">
<ng-template actions appHasRole [appHasRole]="['project.write']"> <ng-template actions appHasRole [appHasRole]="['project.create']">
<a class="add-button" [routerLink]="[ '/projects', 'create']" color="primary" mat-raised-button> <a class="add-button" [routerLink]="[ '/projects', 'create']" color="primary" mat-raised-button>
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }} <mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
</a> </a>
@ -70,4 +70,4 @@
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator> [pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator>
</div> </div>
</app-refresh-table> </app-refresh-table>
</div> </div>

View File

@ -40,7 +40,7 @@
<h1 class="h1">{{ 'PROJECT.GRANT.DETAIL.MEMBERTITLE' | translate }}</h1> <h1 class="h1">{{ 'PROJECT.GRANT.DETAIL.MEMBERTITLE' | translate }}</h1>
<p class="desc">{{ 'PROJECT.GRANT.DETAIL.MEMBERDESC' | translate }}</p> <p class="desc">{{ 'PROJECT.GRANT.DETAIL.MEMBERDESC' | translate }}</p>
<app-project-grant-members *ngIf="this.projectid && this.grantid" [disabled]="isZitadel" [projectId]="projectid" <app-project-grant-members *ngIf="this.projectid && this.grantid" [projectId]="projectid" [grantId]="grantid"
[grantId]="grantid" [type]="projectType"> [type]="projectType">
</app-project-grant-members> </app-project-grant-members>
</app-detail-layout> </app-detail-layout>

View File

@ -38,10 +38,6 @@ export class ProjectGrantDetailComponent {
this.projectid = params.projectid; this.projectid = params.projectid;
this.grantid = params.grantid; this.grantid = params.grantid;
this.mgmtService.GetIam().then(iam => {
this.isZitadel = iam.toObject().iamProjectId === this.projectid;
});
this.getRoleOptions(params.projectid); this.getRoleOptions(params.projectid);
this.mgmtService.ProjectGrantByID(this.grantid, this.projectid).then((grant) => { this.mgmtService.ProjectGrantByID(this.grantid, this.projectid).then((grant) => {

View File

@ -15,6 +15,7 @@ import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module'; import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module'; import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.module';
import { ProjectGrantDetailRoutingModule } from './project-grant-detail-routing.module'; import { ProjectGrantDetailRoutingModule } from './project-grant-detail-routing.module';
import { ProjectGrantDetailComponent } from './project-grant-detail.component'; import { ProjectGrantDetailComponent } from './project-grant-detail.component';
@ -43,6 +44,7 @@ import { ProjectGrantMembersModule } from './project-grant-members/project-grant
TranslateModule, TranslateModule,
MatSelectModule, MatSelectModule,
DetailLayoutModule, DetailLayoutModule,
HasRolePipeModule,
], ],
}) })
export class ProjectGrantDetailModule { } export class ProjectGrantDetailModule { }

View File

@ -1,11 +1,14 @@
<app-refresh-table (refreshed)="refreshPage()" [dataSize]="dataSource.totalResult" <app-refresh-table (refreshed)="refreshPage()" [dataSize]="dataSource.totalResult"
[timestamp]="dataSource?.viewTimestamp" [selection]="selection" [loading]="dataSource.loading$ | async"> [timestamp]="dataSource?.viewTimestamp" [selection]="selection" [loading]="dataSource.loading$ | async">
<button (click)="removeProjectMemberSelection()" matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" <button (click)="removeProjectMemberSelection()"
class="icon-button" color="warn" mat-icon-button *ngIf="selection.hasValue()"> [disabled]="(['project.grant.member.delete','project.grant.member.delete:' + grantId] | hasRole | async) == false"
matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="icon-button" color="warn" mat-icon-button
*ngIf="selection.hasValue()">
<i class="las la-trash"></i> <i class="las la-trash"></i>
</button> </button>
<a color="primary" [disabled]="disabled" class="add-button" (click)="openAddMember()" color="primary" <a color="primary"
mat-raised-button> [disabled]="(['project.grant.member.write','project.grant.member.write:' + grantId] | hasRole | async) == false"
class="add-button" (click)="openAddMember()" color="primary" mat-raised-button>
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }} <mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
</a> </a>
@ -59,7 +62,8 @@
<td class="pointer" mat-cell *matCellDef="let member"> <td class="pointer" mat-cell *matCellDef="let member">
<mat-form-field class="form-field" appearance="outline" *ngIf="projectId"> <mat-form-field class="form-field" appearance="outline" *ngIf="projectId">
<mat-label>{{ 'PROJECT.MEMBER.ROLES' | translate }}</mat-label> <mat-label>{{ 'PROJECT.MEMBER.ROLES' | translate }}</mat-label>
<mat-select [(ngModel)]="member.rolesList" multiple [disabled]="disabled" <mat-select [(ngModel)]="member.rolesList" multiple
[disabled]="(['project.grant.member.write','project.grant.member.write:' + grantId] | hasRole | async) == false"
(selectionChange)="updateRoles(member, $event)"> (selectionChange)="updateRoles(member, $event)">
<mat-option *ngFor="let role of memberRoleOptions" [value]="role"> <mat-option *ngFor="let role of memberRoleOptions" [value]="role">
{{ role }} {{ role }}

View File

@ -26,7 +26,6 @@ export class ProjectGrantMembersComponent implements AfterViewInit, OnInit {
@Input() public type: ProjectType = ProjectType.PROJECTTYPE_GRANTED; @Input() public type: ProjectType = ProjectType.PROJECTTYPE_GRANTED;
@Input() public disabled: boolean = false;
@ViewChild(MatPaginator) public paginator!: MatPaginator; @ViewChild(MatPaginator) public paginator!: MatPaginator;
@ViewChild(MatTable) public table!: MatTable<ProjectMember.AsObject>; @ViewChild(MatTable) public table!: MatTable<ProjectMember.AsObject>;
public dataSource!: ProjectGrantMembersDataSource; public dataSource!: ProjectGrantMembersDataSource;

View File

@ -18,6 +18,7 @@ import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module'; import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module'; import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
import { SearchUserAutocompleteModule } from 'src/app/modules/search-user-autocomplete/search-user-autocomplete.module'; import { SearchUserAutocompleteModule } from 'src/app/modules/search-user-autocomplete/search-user-autocomplete.module';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.module';
import { import {
ProjectGrantMembersCreateDialogModule, ProjectGrantMembersCreateDialogModule,
@ -48,6 +49,7 @@ import { ProjectGrantMembersComponent } from './project-grant-members.component'
FormsModule, FormsModule,
TranslateModule, TranslateModule,
RefreshTableModule, RefreshTableModule,
HasRolePipeModule,
], ],
exports: [ exports: [
ProjectGrantMembersComponent, ProjectGrantMembersComponent,

View File

@ -59,6 +59,8 @@ export class ProjectGrantCreateComponent implements OnInit, OnDestroy {
} }
public addGrant(): void { public addGrant(): void {
console.log(this.org.id, this.projectId, this.rolesKeyList);
this.mgmtService this.mgmtService
.CreateProjectGrant(this.org.id, this.projectId, this.rolesKeyList) .CreateProjectGrant(this.org.id, this.projectId, this.rolesKeyList)
.then((data) => { .then((data) => {

View File

@ -34,7 +34,7 @@
</mat-form-field> </mat-form-field>
</div> </div>
<div class="btn-container"> <div class="btn-container">
<button class="submit-button" type="submit" color="primary" <button [disabled]="disabled" class="submit-button" type="submit" color="primary"
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button> mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
</div> </div>
</form> </form>

View File

@ -1,4 +1,4 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnChanges, OnDestroy, Output } from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms'; import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { Gender as authGender, UserProfile as authUP, UserView as authUV } from 'src/app/proto/generated/auth_pb'; import { Gender as authGender, UserProfile as authUP, UserView as authUV } from 'src/app/proto/generated/auth_pb';
@ -10,7 +10,7 @@ import { Gender as mgmtGender, UserProfile as mgmtUP, UserView as mgmtUV } from
templateUrl: './detail-form.component.html', templateUrl: './detail-form.component.html',
styleUrls: ['./detail-form.component.scss'], styleUrls: ['./detail-form.component.scss'],
}) })
export class DetailFormComponent implements OnInit, OnDestroy { export class DetailFormComponent implements OnDestroy, OnChanges {
@Input() public username!: string; @Input() public username!: string;
@Input() public user!: mgmtUV | authUV; @Input() public user!: mgmtUV | authUV;
@Input() public disabled: boolean = false; @Input() public disabled: boolean = false;
@ -36,7 +36,19 @@ export class DetailFormComponent implements OnInit, OnDestroy {
}); });
} }
public ngOnInit(): void { public ngOnChanges(): void {
console.log('disabled');
this.profileForm = this.fb.group({
userName: [{ value: '', disabled: true }, [
Validators.required,
]],
firstName: [{ value: '', disabled: this.disabled }, Validators.required],
lastName: [{ value: '', disabled: this.disabled }, Validators.required],
nickName: [{ value: '', disabled: this.disabled }],
gender: [{ value: 0 }, { disabled: this.disabled }],
preferredLanguage: [{ value: '', disabled: this.disabled }],
});
this.profileForm.patchValue({ userName: this.username, ...this.user }); this.profileForm.patchValue({ userName: this.username, ...this.user });
if (this.preferredLanguage) { if (this.preferredLanguage) {

View File

@ -28,7 +28,8 @@
<span>{{memberships.totalResult}}</span> <span>{{memberships.totalResult}}</span>
</div> </div>
</ng-template> </ng-template>
<button class="add-img" (click)="addMember()" mat-icon-button aria-label="add membership"> <button [disabled]="disabled" class="add-img" (click)="addMember()" mat-icon-button
aria-label="add membership">
<mat-icon>add</mat-icon> <mat-icon>add</mat-icon>
</button> </button>
</div> </div>

View File

@ -3,7 +3,7 @@ import { Component, Input, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component'; import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
import { MemberType, UserView, UserMembershipSearchResponse } from 'src/app/proto/generated/management_pb'; import { MemberType, UserMembershipSearchResponse, UserView } from 'src/app/proto/generated/management_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@ -34,6 +34,8 @@ export class MembershipsComponent implements OnInit {
public memberships!: UserMembershipSearchResponse.AsObject; public memberships!: UserMembershipSearchResponse.AsObject;
@Input() public user!: UserView.AsObject; @Input() public user!: UserView.AsObject;
@Input() public disabled: boolean = false;
public MemberType: any = MemberType; public MemberType: any = MemberType;
constructor( constructor(
@ -46,6 +48,7 @@ export class MembershipsComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
this.loadManager(this.user.id); this.loadManager(this.user.id);
console.log(this.disabled);
} }
public async loadManager(userId: string): Promise<void> { public async loadManager(userId: string): Promise<void> {
@ -56,7 +59,9 @@ export class MembershipsComponent implements OnInit {
} }
public navigateToObject(): void { public navigateToObject(): void {
this.router.navigate(['/users', this.user.id, 'memberships']); if (!this.disabled) {
this.router.navigate(['/users', this.user.id, 'memberships']);
}
} }
public addMember(): void { public addMember(): void {

View File

@ -1,5 +1,5 @@
<app-meta-layout> <app-meta-layout *ngIf="user && (['user.write','user.write:' + user.id] | hasRole) as canWrite$">
<div *ngIf="user" class="max-width-container"> <div class="max-width-container">
<div class="head"> <div class="head">
<a (click)="navigateBack()" mat-icon-button> <a (click)="navigateBack()" mat-icon-button>
<mat-icon class="icon">arrow_back</mat-icon> <mat-icon class="icon">arrow_back</mat-icon>
@ -38,9 +38,8 @@
<ng-template appHasRole [appHasRole]="['user.read', 'user.read:'+user?.id]"> <ng-template appHasRole [appHasRole]="['user.read', 'user.read:'+user?.id]">
<app-card *ngIf="user.human" title="{{ 'USER.PROFILE.TITLE' | translate }}"> <app-card *ngIf="user.human" title="{{ 'USER.PROFILE.TITLE' | translate }}">
<app-detail-form [disabled]="(['user.write:' + user?.id, 'user.write'] | hasRole | async) == false" <app-detail-form [disabled]="(canWrite$ | async) == false" [genders]="genders" [languages]="languages"
[genders]="genders" [languages]="languages" [username]="user.userName" [user]="user.human" [username]="user.userName" [user]="user.human" (submitData)="saveProfile($event)">
(submitData)="saveProfile($event)">
</app-detail-form> </app-detail-form>
</app-card> </app-card>
@ -51,7 +50,7 @@
</app-detail-form-machine> </app-detail-form-machine>
</app-card> </app-card>
<app-card *ngIf="user.id" title="{{ 'USER.MACHINE.KEYSTITLE' | translate }}" <app-card *ngIf="user.machine && user.id" title="{{ 'USER.MACHINE.KEYSTITLE' | translate }}"
description="{{ 'USER.MACHINE.KEYSDESC' | translate }}"> description="{{ 'USER.MACHINE.KEYSDESC' | translate }}">
<app-machine-keys [userId]="user.id"></app-machine-keys> <app-machine-keys [userId]="user.id"></app-machine-keys>
</app-card> </app-card>
@ -62,13 +61,12 @@
<div class="method-col"> <div class="method-col">
<div class="method-row"> <div class="method-row">
<span class="label">{{ 'USER.PROFILE.PASSWORD' | translate }}</span> <span class="label">{{ 'USER.PROFILE.PASSWORD' | translate }}</span>
<span>******</span>
<span>*********</span>
<div class="actions"> <div class="actions">
<button class="notify-change-pwd" (click)="sendSetPasswordNotification()" mat-stroked-button <button [disabled]="(canWrite$ | async) == false" class="notify-change-pwd"
color="primary" (click)="sendSetPasswordNotification()" mat-stroked-button color="primary"
*ngIf="user.state === UserState.USERSTATE_INITIAL">{{ 'USER.PASSWORD.RESENDNOTIFICATION' | translate }}</button> *ngIf="user.state === UserState.USERSTATE_INITIAL">{{ 'USER.PASSWORD.RESENDNOTIFICATION' | translate }}</button>
<a [routerLink]="['password']" mat-icon-button> <a [disabled]="(canWrite$ | async) == false" [routerLink]="['password']" mat-icon-button>
<mat-icon class="icon">chevron_right</mat-icon> <mat-icon class="icon">chevron_right</mat-icon>
</a> </a>
</div> </div>
@ -86,13 +84,16 @@
<mat-icon class="icon" color="warn" aria-hidden="false" aria-label="not verified icon"> <mat-icon class="icon" color="warn" aria-hidden="false" aria-label="not verified icon">
highlight_off highlight_off
</mat-icon> </mat-icon>
<a class="verify" matTooltip="{{'USER.LOGINMETHODS.EMAIL.RESEND' | translate}}" <ng-container *ngIf="(canWrite$ | async)">
(click)="resendVerification()">{{'USER.LOGINMETHODS.RESENDCODE' | translate}}</a> <a class="verify" matTooltip="{{'USER.LOGINMETHODS.EMAIL.RESEND' | translate}}"
(click)="resendVerification()">{{'USER.LOGINMETHODS.RESENDCODE' | translate}}</a>
</ng-container>
</ng-container> </ng-container>
</div> </div>
<div class="actions"> <div class="actions">
<button (click)="emailEditState = true" mat-icon-button> <button [disabled]="(canWrite$ | async) == false" (click)="emailEditState = true"
mat-icon-button>
<mat-icon class="icon">edit</mat-icon> <mat-icon class="icon">edit</mat-icon>
</button> </button>
</div> </div>
@ -105,8 +106,7 @@
<button (click)="emailEditState = false" mat-icon-button> <button (click)="emailEditState = false" mat-icon-button>
<mat-icon class="icon">close</mat-icon> <mat-icon class="icon">close</mat-icon>
</button> </button>
<button *ngIf="user.human" <button *ngIf="user.human" [disabled]="!user.human.email || (canWrite$ | async) == false"
[disabled]="!user.human.email || (['user.write','user.write' + user.id] | hasRole | async) == false"
class="submit-button" type="button" color="primary" (click)="saveEmail()" class="submit-button" type="button" color="primary" (click)="saveEmail()"
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button> mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
</ng-template> </ng-template>
@ -125,13 +125,16 @@
<mat-icon class="icon" color="warn" aria-hidden="false" aria-label="not verified icon"> <mat-icon class="icon" color="warn" aria-hidden="false" aria-label="not verified icon">
highlight_off highlight_off
</mat-icon> </mat-icon>
<a class="verify" matTooltip="{{'USER.LOGINMETHODS.PHONE.RESEND' | translate}}" <ng-container *ngIf="(canWrite$ | async)">
(click)="resendPhoneVerification()">{{'USER.LOGINMETHODS.RESENDCODE' | translate}}</a> <a class="verify" matTooltip="{{'USER.LOGINMETHODS.PHONE.RESEND' | translate}}"
(click)="resendPhoneVerification()">{{'USER.LOGINMETHODS.RESENDCODE' | translate}}</a>
</ng-container>
</ng-container> </ng-container>
</div> </div>
<div class="actions"> <div class="actions">
<button (click)="phoneEditState = true" mat-icon-button> <button [disabled]="(canWrite$ | async) == false" (click)="phoneEditState = true"
mat-icon-button>
<mat-icon class="icon">edit</mat-icon> <mat-icon class="icon">edit</mat-icon>
</button> </button>
</div> </div>
@ -140,16 +143,19 @@
<ng-template #phoneEdit> <ng-template #phoneEdit>
<mat-form-field class="name"> <mat-form-field class="name">
<mat-label>{{ 'USER.PHONE' | translate }}</mat-label> <mat-label>{{ 'USER.PHONE' | translate }}</mat-label>
<input *ngIf="user.human && user.human.phone" matInput [(ngModel)]="user.human.phone" /> <input *ngIf="user.human && user.human.phone" matInput
[disabled]="(canWrite$ | async) == false" [(ngModel)]="user.human.phone" />
</mat-form-field> </mat-form-field>
<button (click)="phoneEditState = false" mat-icon-button> <button (click)="phoneEditState = false" mat-icon-button
[disabled]="(canWrite$ | async) == false">
<mat-icon class="icon">close</mat-icon> <mat-icon class="icon">close</mat-icon>
</button> </button>
<button *ngIf="user.human?.phone" color="warn" (click)="deletePhone()" mat-icon-button> <button *ngIf="user.human?.phone" color="warn" (click)="deletePhone()"
[disabled]="(canWrite$ | async) == false" mat-icon-button>
<i class="las la-trash"></i> <i class="las la-trash"></i>
</button> </button>
<button *ngIf="user.human"
[disabled]="!user.human.phone || (['user.write', 'user.write'+ user.id]| hasRole | async) == false" <button *ngIf="user.human" [disabled]="!user.human.phone || (canWrite$ | async) == false"
type="button" color="primary" (click)="savePhone()" mat-raised-button> type="button" color="primary" (click)="savePhone()" mat-raised-button>
{{ 'ACTIONS.SAVE' | translate }}</button> {{ 'ACTIONS.SAVE' | translate }}</button>
</ng-template> </ng-template>
@ -176,7 +182,7 @@
</div> </div>
<ng-template appHasRole [appHasRole]="['user.membership.read']"> <ng-template appHasRole [appHasRole]="['user.membership.read']">
<app-memberships [user]="user"></app-memberships> <app-memberships [user]="user" [disabled]="(canWrite$ | async) == false"></app-memberships>
</ng-template> </ng-template>
<app-changes class="changes" [changeType]="ChangeType.USER" [id]="user.id"></app-changes> <app-changes class="changes" [changeType]="ChangeType.USER" [id]="user.id"></app-changes>

View File

@ -44,7 +44,7 @@
.label, .label,
.name { .name {
margin-right: 1rem; padding-right: 1rem;
} }
.actions { .actions {
@ -87,6 +87,16 @@
text-decoration: underline; text-decoration: underline;
} }
} }
@media only screen and (max-width: 700px) {
flex-direction: column;
align-items: center;
.label,
.name {
padding-right: 0;
}
}
} }
} }

View File

@ -155,7 +155,7 @@ export class UserDetailComponent implements OnInit, OnDestroy {
public saveEmail(): void { public saveEmail(): void {
this.emailEditState = false; this.emailEditState = false;
if (this.user && this.user.human?.phone) { if (this.user && this.user.human?.email) {
this.mgmtUserService this.mgmtUserService
.SaveUserEmail(this.user.id, this.user.human.email).then((data: UserEmail) => { .SaveUserEmail(this.user.id, this.user.human.email).then((data: UserEmail) => {
this.toast.showInfo('USER.TOAST.EMAILSENT', true); this.toast.showInfo('USER.TOAST.EMAILSENT', true);

View File

@ -3,7 +3,8 @@
<h1>{{ 'USER.PAGES.LIST' | translate }}</h1> <h1>{{ 'USER.PAGES.LIST' | translate }}</h1>
<p class="sub">{{ 'USER.PAGES.DESCRIPTION' | translate }}</p> <p class="sub">{{ 'USER.PAGES.DESCRIPTION' | translate }}</p>
<app-user-table [userType]="UserType.HUMAN"></app-user-table> <app-user-table [userType]="UserType.HUMAN" [disabled]="(['user.write'] | hasRole | async) == false">
</app-user-table>
</ng-container> </ng-container>
<ng-container *ngSwitchCase="UserType.MACHINE"> <ng-container *ngSwitchCase="UserType.MACHINE">
@ -11,7 +12,8 @@
<p class="sub">{{ 'USER.PAGES.DESCRIPTIONMACHINE' | translate }}</p> <p class="sub">{{ 'USER.PAGES.DESCRIPTIONMACHINE' | translate }}</p>
<app-user-table [userType]="UserType.MACHINE" <app-user-table [userType]="UserType.MACHINE"
[displayedColumns]="['select','name', 'username', 'description','state']"> [displayedColumns]="['select','name', 'username', 'description','state']"
[disabled]="(['user.write'] | hasRole | async) == false">
</app-user-table> </app-user-table>
</ng-container> </ng-container>
</div> </div>

View File

@ -15,6 +15,7 @@ import { AvatarModule } from 'src/app/modules/avatar/avatar.module';
import { CardModule } from 'src/app/modules/card/card.module'; import { CardModule } from 'src/app/modules/card/card.module';
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module'; import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
import { SharedModule } from 'src/app/modules/shared/shared.module'; import { SharedModule } from 'src/app/modules/shared/shared.module';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.module';
import { UserListRoutingModule } from './user-list-routing.module'; import { UserListRoutingModule } from './user-list-routing.module';
import { UserListComponent } from './user-list.component'; import { UserListComponent } from './user-list.component';
@ -41,6 +42,7 @@ import { UserTableComponent } from './user-table/user-table.component';
MatProgressSpinnerModule, MatProgressSpinnerModule,
MatCheckboxModule, MatCheckboxModule,
MatTooltipModule, MatTooltipModule,
HasRolePipeModule,
TranslateModule, TranslateModule,
SharedModule, SharedModule,
RefreshTableModule, RefreshTableModule,

View File

@ -2,15 +2,15 @@
[timestamp]="userResult?.viewTimestamp" [selection]="selection"> [timestamp]="userResult?.viewTimestamp" [selection]="selection">
<ng-template appHasRole [appHasRole]="['user.write']" actions> <ng-template appHasRole [appHasRole]="['user.write']" actions>
<button (click)="deactivateSelectedUsers()" matTooltip="{{'ORG_DETAIL.TABLE.DEACTIVATE' | translate}}" <button (click)="deactivateSelectedUsers()" matTooltip="{{'ORG_DETAIL.TABLE.DEACTIVATE' | translate}}"
class="icon-button" mat-icon-button *ngIf="selection.hasValue()"> class="icon-button" mat-icon-button *ngIf="selection.hasValue()" [disabled]="disabled">
<mat-icon svgIcon="mdi_account_cancel"></mat-icon> <mat-icon svgIcon="mdi_account_cancel"></mat-icon>
</button> </button>
<button (click)="reactivateSelectedUsers()" matTooltip="{{'ORG_DETAIL.TABLE.ACTIVATE' | translate}}" <button (click)="reactivateSelectedUsers()" matTooltip="{{'ORG_DETAIL.TABLE.ACTIVATE' | translate}}"
class="icon-button" mat-icon-button *ngIf="selection.hasValue()"> class="icon-button" mat-icon-button *ngIf="selection.hasValue()" [disabled]="disabled">
<mat-icon svgIcon="mdi_account_check_outline"></mat-icon> <mat-icon svgIcon="mdi_account_check_outline"></mat-icon>
</button> </button>
<a class="add-button" [routerLink]="[ '/users',userType == UserType.HUMAN ? 'create' : 'create-machine']" <a class="add-button" [routerLink]="[ '/users',userType == UserType.HUMAN ? 'create' : 'create-machine']"
color="primary" mat-raised-button> color="primary" mat-raised-button [disabled]="disabled">
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }} <mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
</a> </a>
</ng-template> </ng-template>

View File

@ -5,7 +5,7 @@ import { MatTableDataSource } from '@angular/material/table';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject, Observable } from 'rxjs'; import { BehaviorSubject, Observable } from 'rxjs';
import { UserView } from 'src/app/proto/generated/auth_pb'; import { UserView } from 'src/app/proto/generated/auth_pb';
import { UserSearchKey, UserSearchQuery, UserSearchResponse } from 'src/app/proto/generated/management_pb'; import { SearchMethod, UserSearchKey, UserSearchQuery, UserSearchResponse } from 'src/app/proto/generated/management_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@ -19,6 +19,7 @@ import { UserType } from '../user-list.component';
export class UserTableComponent implements OnInit { export class UserTableComponent implements OnInit {
public UserType: any = UserType; public UserType: any = UserType;
@Input() userType: UserType = UserType.HUMAN; @Input() userType: UserType = UserType.HUMAN;
@Input() disabled: boolean = false;
@ViewChild(MatPaginator) public paginator!: MatPaginator; @ViewChild(MatPaginator) public paginator!: MatPaginator;
public dataSource: MatTableDataSource<UserView.AsObject> = new MatTableDataSource<UserView.AsObject>(); public dataSource: MatTableDataSource<UserView.AsObject> = new MatTableDataSource<UserView.AsObject>();
public selection: SelectionModel<UserView.AsObject> = new SelectionModel<UserView.AsObject>(true, []); public selection: SelectionModel<UserView.AsObject> = new SelectionModel<UserView.AsObject>(true, []);
@ -79,9 +80,11 @@ export class UserTableComponent implements OnInit {
this.loadingSubject.next(true); this.loadingSubject.next(true);
const query = new UserSearchQuery(); const query = new UserSearchQuery();
query.setKey(UserSearchKey.USERSEARCHKEY_TYPE); query.setKey(UserSearchKey.USERSEARCHKEY_TYPE);
query.setMethod(SearchMethod.SEARCHMETHOD_EQUALS);
query.setValue(filterTypeValue); query.setValue(filterTypeValue);
console.log(filterTypeValue);
this.userService.SearchUsers(limit, offset).then(resp => { this.userService.SearchUsers(limit, offset, [query]).then(resp => {
this.userResult = resp.toObject(); this.userResult = resp.toObject();
this.dataSource.data = this.userResult.resultList; this.dataSource.data = this.userResult.resultList;
console.log(this.userResult.resultList); console.log(this.userResult.resultList);

View File

@ -50,7 +50,8 @@ export class GrpcService {
interceptors, interceptors,
); );
this.admin = new AdminServicePromiseClient( this.admin = new AdminServicePromiseClient(
data.adminServiceUrl, // TODO: replace with service url
data.mgmtServiceUrl,
null, null,
// @ts-ignore // @ts-ignore
interceptors, interceptors,

View File

@ -324,8 +324,8 @@ export class ManagementService {
} }
public async CreateProjectGrant( public async CreateProjectGrant(
projectId: string,
orgId: string, orgId: string,
projectId: string,
roleKeysList: string[], roleKeysList: string[],
): Promise<ProjectGrant> { ): Promise<ProjectGrant> {
const req = new ProjectGrantCreate(); const req = new ProjectGrantCreate();