mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-30 02:52:16 +00:00
# Which Problems Are Solved The management console deletes all roles if user grant is not manipulated before saving in the dialog. # How the Problems Are Solved Correctly prefill the selected role assignments so when no change was done we keep the current state. # Additional Changes - Prevents displaying misleading error messages to users when a role assignment operation results in no actual changes. # Additional Context - Closes #10517 Co-authored-by: Max Peintner <[email protected]>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { SelectionModel } from '@angular/cdk/collections';
|
||||
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
|
||||
import { Component, DestroyRef, EventEmitter, inject, Input, OnInit, Output, ViewChild } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatTable } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
@@ -11,6 +11,7 @@ import { PaginatorComponent } from '../paginator/paginator.component';
|
||||
import { ProjectRoleDetailDialogComponent } from '../project-role-detail-dialog/project-role-detail-dialog.component';
|
||||
import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component';
|
||||
import { ProjectRolesDataSource } from './project-roles-table-datasource';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-project-roles-table',
|
||||
@@ -33,6 +34,8 @@ export class ProjectRolesTableComponent implements OnInit {
|
||||
@Output() public changedSelection: EventEmitter<Array<string>> = new EventEmitter();
|
||||
@Input() public displayedColumns: string[] = ['key', 'displayname', 'group', 'creationDate', 'changeDate', 'actions'];
|
||||
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
|
||||
constructor(
|
||||
private mgmtService: ManagementService,
|
||||
private toast: ToastService,
|
||||
@@ -48,7 +51,7 @@ export class ProjectRolesTableComponent implements OnInit {
|
||||
this.loadRolesPage();
|
||||
this.selection.select(...this.selectedKeys);
|
||||
|
||||
this.selection.changed.subscribe(() => {
|
||||
this.selection.changed.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
|
||||
this.changedSelection.emit(this.selection.selected);
|
||||
});
|
||||
}
|
||||
|
||||
+11
-11
@@ -1,17 +1,17 @@
|
||||
<span class="title" mat-dialog-title>{{ data.i18nTitle | translate }}</span>
|
||||
<div mat-dialog-content>
|
||||
<div class="dialog-table-wrapper">
|
||||
<cnsl-project-roles-table
|
||||
class="role-table"
|
||||
*ngIf="projectId"
|
||||
[displayedColumns]="['select', 'key', 'displayname', 'group']"
|
||||
(changedSelection)="selectRoles($event)"
|
||||
[projectId]="projectId"
|
||||
[grantId]="grantId"
|
||||
[selectedKeys]="selectedRoleKeysList"
|
||||
[showSelectionActionButton]="false"
|
||||
>
|
||||
</cnsl-project-roles-table>
|
||||
@if (data.projectId) {
|
||||
<cnsl-project-roles-table
|
||||
class="role-table"
|
||||
[displayedColumns]="['select', 'key', 'displayname', 'group']"
|
||||
(changedSelection)="selectRoles($event)"
|
||||
[projectId]="data.projectId"
|
||||
[grantId]="data.grantId ?? ''"
|
||||
[selectedKeys]="data.selectedRoleKeysList"
|
||||
[showSelectionActionButton]="false"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div mat-dialog-actions class="action">
|
||||
|
||||
+16
-18
@@ -1,6 +1,15 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { Component, inject, Inject, signal } from '@angular/core';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
|
||||
export type UserGrantRoleDialogData = {
|
||||
projectId: string;
|
||||
grantId?: string;
|
||||
selectedRoleKeysList: string[];
|
||||
i18nTitle: string;
|
||||
};
|
||||
|
||||
export type UserGrantRoleDialogResult = { roles: string[] };
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-user-grant-role-dialog',
|
||||
templateUrl: './user-grant-role-dialog.component.html',
|
||||
@@ -8,30 +17,19 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
standalone: false,
|
||||
})
|
||||
export class UserGrantRoleDialogComponent {
|
||||
public projectId: string = '';
|
||||
public grantId: string = '';
|
||||
public selectedRoleKeysList: string[] = [];
|
||||
|
||||
public selectedRoleKeys: string[] = [];
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<UserGrantRoleDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
) {
|
||||
this.projectId = data.projectId;
|
||||
this.grantId = data.grantId;
|
||||
this.selectedRoleKeysList = data.selectedRoleKeysList;
|
||||
}
|
||||
protected readonly data: UserGrantRoleDialogData = inject(MAT_DIALOG_DATA);
|
||||
private readonly dialogRef: MatDialogRef<UserGrantRoleDialogComponent, UserGrantRoleDialogResult> = inject(MatDialogRef);
|
||||
private readonly selectedRoleKeys = signal([...this.data.selectedRoleKeysList]);
|
||||
|
||||
public selectRoles(selected: string[]): void {
|
||||
this.selectedRoleKeys = selected;
|
||||
this.selectedRoleKeys.set(selected);
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this.dialogRef.close(false);
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
public closeDialogWithSuccess(): void {
|
||||
this.dialogRef.close({ roles: this.selectedRoleKeys });
|
||||
this.dialogRef.close({ roles: this.selectedRoleKeys() });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { MatInput } from '@angular/material/input';
|
||||
import { MatTable } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { RpcError } from 'grpc-web';
|
||||
import { enterAnimations } from 'src/app/animations';
|
||||
import { UserGrant as AuthUserGrant } from 'src/app/proto/generated/zitadel/auth_pb';
|
||||
import {
|
||||
@@ -20,7 +21,11 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { ActionKeysType } from '../action-keys/action-keys.component';
|
||||
import { PageEvent, PaginatorComponent } from '../paginator/paginator.component';
|
||||
import { UserGrantRoleDialogComponent } from '../user-grant-role-dialog/user-grant-role-dialog.component';
|
||||
import {
|
||||
UserGrantRoleDialogComponent,
|
||||
UserGrantRoleDialogData,
|
||||
UserGrantRoleDialogResult,
|
||||
} from '../user-grant-role-dialog/user-grant-role-dialog.component';
|
||||
import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component';
|
||||
import { UserGrantContext, UserGrantsDataSource } from './user-grants-datasource';
|
||||
import { Org } from 'src/app/proto/generated/zitadel/org_pb';
|
||||
@@ -176,32 +181,40 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
|
||||
public openEditDialog(grant: UserGrantAsObject): void {
|
||||
const dialogRef = this.dialog.open(UserGrantRoleDialogComponent, {
|
||||
data: {
|
||||
projectId: grant.projectId,
|
||||
grantId: grant?.projectGrantId,
|
||||
selectedRoleKeysList: grant.roleKeysList,
|
||||
i18nTitle: 'GRANTS.EDIT.TITLE',
|
||||
const dialogRef = this.dialog.open<UserGrantRoleDialogComponent, UserGrantRoleDialogData, UserGrantRoleDialogResult>(
|
||||
UserGrantRoleDialogComponent,
|
||||
{
|
||||
data: {
|
||||
projectId: grant.projectId,
|
||||
grantId: grant?.projectGrantId,
|
||||
selectedRoleKeysList: grant.roleKeysList,
|
||||
i18nTitle: 'GRANTS.EDIT.TITLE',
|
||||
},
|
||||
width: '600px',
|
||||
},
|
||||
width: '600px',
|
||||
});
|
||||
);
|
||||
|
||||
dialogRef.afterClosed().subscribe((resp) => {
|
||||
if (resp && resp.roles) {
|
||||
this.userService
|
||||
.updateUserGrant(
|
||||
(grant as MgmtUserGrant.AsObject).id ?? (grant as AuthUserGrant.AsObject).grantId,
|
||||
grant.userId,
|
||||
resp.roles,
|
||||
)
|
||||
.then(() => {
|
||||
this.toast.showInfo('GRANTS.TOAST.UPDATED', true);
|
||||
grant.roleKeysList = resp.roles;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
if (!resp || !resp.roles) {
|
||||
return;
|
||||
}
|
||||
this.userService
|
||||
.updateUserGrant(
|
||||
(grant as MgmtUserGrant.AsObject).id ?? (grant as AuthUserGrant.AsObject).grantId,
|
||||
grant.userId,
|
||||
resp.roles,
|
||||
)
|
||||
.then(() => {
|
||||
this.toast.showInfo('GRANTS.TOAST.UPDATED', true);
|
||||
grant.roleKeysList = resp.roles;
|
||||
})
|
||||
.catch((error) => {
|
||||
// Errors.UserGrant.NotChanged
|
||||
if (error instanceof RpcError && error.message.includes('COMMAND-Rs8fy')) {
|
||||
return;
|
||||
}
|
||||
this.toast.showError(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+34
-23
@@ -2,16 +2,20 @@ import { Component, EventEmitter } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { PageEvent } from '@angular/material/paginator';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { RpcError } from 'grpc-web';
|
||||
import { ActionKeysType } from 'src/app/modules/action-keys/action-keys.component';
|
||||
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
|
||||
import { UserGrantRoleDialogComponent } from 'src/app/modules/user-grant-role-dialog/user-grant-role-dialog.component';
|
||||
import {
|
||||
UserGrantRoleDialogComponent,
|
||||
UserGrantRoleDialogData,
|
||||
UserGrantRoleDialogResult,
|
||||
} from 'src/app/modules/user-grant-role-dialog/user-grant-role-dialog.component';
|
||||
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
|
||||
import { GrantedProject, ProjectGrantState, Role } from 'src/app/proto/generated/zitadel/project_pb';
|
||||
import { User } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { ProjectGrantMembersDataSource } from './project-grant-members-datasource';
|
||||
|
||||
@Component({
|
||||
@@ -260,31 +264,38 @@ export class ProjectGrantDetailComponent {
|
||||
}
|
||||
|
||||
public editRoles(): void {
|
||||
const dialogRef = this.dialog.open(UserGrantRoleDialogComponent, {
|
||||
data: {
|
||||
projectId: this.grant.projectId,
|
||||
selectedRoleKeysList: this.grant.grantedRoleKeysList,
|
||||
i18nTitle: 'PROJECT.GRANT.EDITTITLE',
|
||||
const dialogRef = this.dialog.open<UserGrantRoleDialogComponent, UserGrantRoleDialogData, UserGrantRoleDialogResult>(
|
||||
UserGrantRoleDialogComponent,
|
||||
{
|
||||
data: {
|
||||
projectId: this.grant.projectId,
|
||||
selectedRoleKeysList: this.grant.grantedRoleKeysList,
|
||||
i18nTitle: 'PROJECT.GRANT.EDITTITLE',
|
||||
},
|
||||
width: '600px',
|
||||
},
|
||||
width: '600px',
|
||||
});
|
||||
);
|
||||
|
||||
dialogRef.afterClosed().subscribe((resp) => {
|
||||
if (resp && resp.roles) {
|
||||
this.mgmtService
|
||||
.updateProjectGrant(this.grant.grantId, this.grant.projectId, resp.roles)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUPDATED', true);
|
||||
this.grant.grantedRoleKeysList = resp.roles;
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.changePage.emit();
|
||||
this.toast.showError(error);
|
||||
});
|
||||
if (!resp || !resp.roles) {
|
||||
return;
|
||||
}
|
||||
this.mgmtService
|
||||
.updateProjectGrant(this.grant.grantId, this.grant.projectId, resp.roles)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUPDATED', true);
|
||||
this.grant.grantedRoleKeysList = resp.roles;
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
// Errors.UserGrant.NotChanged
|
||||
if (!(error instanceof RpcError && error.message.includes('COMMAND-Rs8fy'))) {
|
||||
this.toast.showError(error);
|
||||
}
|
||||
this.changePage.emit();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user