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

View File

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

View File

@ -3,7 +3,7 @@
<app-refresh-table (refreshed)="refreshPage()" [dataSize]="dataSource?.totalResult"
[timestamp]="dataSource?.viewTimestamp" [selection]="selection" [loading]="dataSource?.loading$ | async">
<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">
<i class="las la-trash"></i>
</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 => {
return this.mgmtService.RemoveMyOrgMember(member.userId).then(() => {
this.toast.showInfo('ORG.TOAST.MEMBERREMOVED', true);

View File

@ -18,8 +18,8 @@
description="{{'GRANTS.PROJECT.DESCRIPTION' | translate }}">
<app-user-grants [context]="userGrantContext" [projectId]="projectId" [grantId]="grantId"
[displayedColumns]="['select','user', 'projectId', 'creationDate', 'changeDate', 'roleNamesList']"
[allowCreate]="['project.grant.user.grant.write'] | hasRole | async"
[allowDelete]="['project.grant.user.grant.delete'] | hasRole | async">
[allowCreate]="['user.grant.write','user.grant.write:'+grantId] | hasRole | async"
[allowDelete]="['user.grant.delete','user.grant.delete:'+grantId] | hasRole | async">
</app-user-grants>
</app-card>
</ng-template>
@ -35,10 +35,11 @@
<mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true">
<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 }}"
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>
</mat-tab>
<mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="flex-col">

View File

@ -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" class="state-button"
(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" class="state-button"
(click)="changeState(ProjectState.PROJECTSTATE_ACTIVE)">{{'PROJECT.TABLE.ACTIVATE' | translate}}</button>
@ -51,17 +51,15 @@
<ng-container *ngIf="project">
<ng-template appHasRole [appHasRole]="['project.app.read:' + project.projectId, 'project.app.read']">
<app-application-grid *ngIf="grid"
[disabled]="project?.state !== ProjectState.PROJECTSTATE_ACTIVE || isZitadel"
(changeView)="grid = false" [projectId]="projectId"></app-application-grid>
<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]="project?.state !== ProjectState.PROJECTSTATE_ACTIVE || isZitadel"
[projectId]="projectId"></app-applications>
<app-applications [disabled]="isZitadel" [projectId]="projectId"></app-applications>
</app-card>
</ng-template>
@ -70,7 +68,8 @@
[appHasRole]="['project.grant.read:' + project.projectId, 'project.grant.read']">
<app-card title="{{ 'PROJECT.GRANT.TITLE' | 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">
</app-project-grants>
</app-card>
@ -79,7 +78,8 @@
<ng-template appHasRole [appHasRole]="['project.role.read:' + project.projectId, 'project.role.read']">
<app-card title="{{ 'PROJECT.ROLE.TITLE' | 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">
</app-project-roles>
</app-card>
@ -89,9 +89,8 @@
<app-card *ngIf="project?.projectId" title="{{ 'GRANTS.PROJECT.TITLE' | translate }}"
description="{{'GRANTS.PROJECT.DESCRIPTION' | translate }}">
<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'] | hasRole) | async"
[allowDelete]="(['user.grant.delete'] | hasRole) | async">
[allowCreate]="(['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>
@ -109,10 +108,11 @@
<mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true">
<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 }}"
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>
</mat-tab>
<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>
<ng-template appHasRole [appHasRole]="['project.write']">
<ng-template appHasRole [appHasRole]="['project.create']">
<div class="add-project-button card" (click)="addItem()">
<mat-icon class="icon">add</mat-icon>
<span>{{'PROJECT.PAGES.ADDNEW' | translate}}</span>

View File

@ -11,7 +11,7 @@
<app-refresh-table (refreshed)="refreshPage()" [dataSize]="totalResult" [timestamp]="viewTimestamp"
[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>
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
</a>
@ -70,4 +70,4 @@
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator>
</div>
</app-refresh-table>
</div>
</div>

View File

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

View File

@ -38,10 +38,6 @@ export class ProjectGrantDetailComponent {
this.projectid = params.projectid;
this.grantid = params.grantid;
this.mgmtService.GetIam().then(iam => {
this.isZitadel = iam.toObject().iamProjectId === this.projectid;
});
this.getRoleOptions(params.projectid);
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 { HasRoleModule } from 'src/app/directives/has-role/has-role.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 { ProjectGrantDetailComponent } from './project-grant-detail.component';
@ -43,6 +44,7 @@ import { ProjectGrantMembersModule } from './project-grant-members/project-grant
TranslateModule,
MatSelectModule,
DetailLayoutModule,
HasRolePipeModule,
],
})
export class ProjectGrantDetailModule { }

View File

@ -1,11 +1,14 @@
<app-refresh-table (refreshed)="refreshPage()" [dataSize]="dataSource.totalResult"
[timestamp]="dataSource?.viewTimestamp" [selection]="selection" [loading]="dataSource.loading$ | async">
<button (click)="removeProjectMemberSelection()" matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}"
class="icon-button" color="warn" mat-icon-button *ngIf="selection.hasValue()">
<button (click)="removeProjectMemberSelection()"
[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>
</button>
<a color="primary" [disabled]="disabled" class="add-button" (click)="openAddMember()" color="primary"
mat-raised-button>
<a color="primary"
[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 }}
</a>
@ -59,7 +62,8 @@
<td class="pointer" mat-cell *matCellDef="let member">
<mat-form-field class="form-field" appearance="outline" *ngIf="projectId">
<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)">
<mat-option *ngFor="let role of memberRoleOptions" [value]="role">
{{ role }}

View File

@ -26,7 +26,6 @@ export class ProjectGrantMembersComponent implements AfterViewInit, OnInit {
@Input() public type: ProjectType = ProjectType.PROJECTTYPE_GRANTED;
@Input() public disabled: boolean = false;
@ViewChild(MatPaginator) public paginator!: MatPaginator;
@ViewChild(MatTable) public table!: MatTable<ProjectMember.AsObject>;
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 { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.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 {
ProjectGrantMembersCreateDialogModule,
@ -48,6 +49,7 @@ import { ProjectGrantMembersComponent } from './project-grant-members.component'
FormsModule,
TranslateModule,
RefreshTableModule,
HasRolePipeModule,
],
exports: [
ProjectGrantMembersComponent,

View File

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

View File

@ -34,7 +34,7 @@
</mat-form-field>
</div>
<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>
</div>
</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 { Subscription } from 'rxjs';
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',
styleUrls: ['./detail-form.component.scss'],
})
export class DetailFormComponent implements OnInit, OnDestroy {
export class DetailFormComponent implements OnDestroy, OnChanges {
@Input() public username!: string;
@Input() public user!: mgmtUV | authUV;
@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 });
if (this.preferredLanguage) {

View File

@ -28,7 +28,8 @@
<span>{{memberships.totalResult}}</span>
</div>
</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>
</button>
</div>

View File

@ -3,7 +3,7 @@ import { Component, Input, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Router } from '@angular/router';
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 { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service';
@ -34,6 +34,8 @@ export class MembershipsComponent implements OnInit {
public memberships!: UserMembershipSearchResponse.AsObject;
@Input() public user!: UserView.AsObject;
@Input() public disabled: boolean = false;
public MemberType: any = MemberType;
constructor(
@ -46,6 +48,7 @@ export class MembershipsComponent implements OnInit {
ngOnInit(): void {
this.loadManager(this.user.id);
console.log(this.disabled);
}
public async loadManager(userId: string): Promise<void> {
@ -56,7 +59,9 @@ export class MembershipsComponent implements OnInit {
}
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 {

View File

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

View File

@ -44,7 +44,7 @@
.label,
.name {
margin-right: 1rem;
padding-right: 1rem;
}
.actions {
@ -87,6 +87,16 @@
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 {
this.emailEditState = false;
if (this.user && this.user.human?.phone) {
if (this.user && this.user.human?.email) {
this.mgmtUserService
.SaveUserEmail(this.user.id, this.user.human.email).then((data: UserEmail) => {
this.toast.showInfo('USER.TOAST.EMAILSENT', true);

View File

@ -3,7 +3,8 @@
<h1>{{ 'USER.PAGES.LIST' | translate }}</h1>
<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 *ngSwitchCase="UserType.MACHINE">
@ -11,7 +12,8 @@
<p class="sub">{{ 'USER.PAGES.DESCRIPTIONMACHINE' | translate }}</p>
<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>
</ng-container>
</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 { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.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 { UserListComponent } from './user-list.component';
@ -41,6 +42,7 @@ import { UserTableComponent } from './user-table/user-table.component';
MatProgressSpinnerModule,
MatCheckboxModule,
MatTooltipModule,
HasRolePipeModule,
TranslateModule,
SharedModule,
RefreshTableModule,

View File

@ -2,15 +2,15 @@
[timestamp]="userResult?.viewTimestamp" [selection]="selection">
<ng-template appHasRole [appHasRole]="['user.write']" actions>
<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>
</button>
<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>
</button>
<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 }}
</a>
</ng-template>

View File

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

View File

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

View File

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