fix(console): delete role assignment in admin profile #10376 (#11867)

# Which Problems Are Solved

Currently it's not possible to delete role assignments in the user
profile.
<img width="1026" height="516" alt="CleanShot 2026-03-18 at 10 50 01"
src="https://github.com/user-attachments/assets/7b8eb316-852b-4c16-9f4b-4c041215e410"
/>

# How the Problems Are Solved

Switched the removal of role assignments to the newer [Delete
Authorization
call](https://zitadel.com/docs/reference/api/authorization/zitadel.authorization.v2.AuthorizationService.DeleteAuthorization).

# Additional Changes

Correctly pass the role assignment id based on the context of the call.

# Additional Context

- Closes: #10376

---------

Co-authored-by: Max Peintner <[email protected]>
Co-authored-by: Max Peintner <[email protected]>
This commit is contained in:
Ramon
2026-04-29 10:32:49 +02:00
committed by GitHub
co-authored by Max Peintner Max Peintner
parent 2c8d63bb71
commit 13f8ba4215
3 changed files with 50 additions and 28 deletions
@@ -31,6 +31,7 @@ import { UserGrantContext, UserGrantsDataSource } from './user-grants-datasource
import { Org } from 'src/app/proto/generated/zitadel/org_pb';
import { QueryClient } from '@tanstack/angular-query-experimental';
import { NewOrganizationService } from '../../services/new-organization.service';
import { AuthorizationService } from '../../services/authorization.service';
export enum UserGrantListSearchKey {
DISPLAY_NAME,
@@ -55,7 +56,7 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
@Input() context: UserGrantContext = UserGrantContext.NONE;
@Input() refreshOnPreviousRoutes: string[] = [];
public dataSource: UserGrantsDataSource = new UserGrantsDataSource(this.authService, this.userService);
public dataSource: UserGrantsDataSource = new UserGrantsDataSource(this.authService, this.mgmtService);
public selection: SelectionModel<UserGrantAsObject> = new SelectionModel<UserGrantAsObject>(true, []);
@ViewChild(PaginatorComponent) public paginator?: PaginatorComponent;
@ViewChild(MatTable) public table?: MatTable<UserGrantAsObject>;
@@ -80,7 +81,8 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
public myOrgs: Array<Org.AsObject> = [];
constructor(
private readonly authService: GrpcAuthService,
private readonly userService: ManagementService,
private readonly mgmtService: ManagementService,
private readonly authorizationService: AuthorizationService,
private readonly toast: ToastService,
private readonly dialog: MatDialog,
private readonly queryClient: QueryClient,
@@ -198,12 +200,8 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
if (!resp || !resp.roles) {
return;
}
this.userService
.updateUserGrant(
(grant as MgmtUserGrant.AsObject).id ?? (grant as AuthUserGrant.AsObject).grantId,
grant.userId,
resp.roles,
)
this.mgmtService
.updateUserGrant('id' in grant ? grant.id : grant.grantId, grant.userId, resp.roles)
.then(() => {
this.toast.showInfo('GRANTS.TOAST.UPDATED', true);
grant.roleKeysList = resp.roles;
@@ -218,7 +216,7 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
});
}
public deleteGrant(event: any, grant: MgmtUserGrant.AsObject): void {
public deleteGrant(event: PointerEvent, grant: UserGrantAsObject): void {
event.stopPropagation();
const dialogRef = this.dialog.open(WarnDialogComponent, {
@@ -232,25 +230,32 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
});
dialogRef.afterClosed().subscribe((resp) => {
if (resp) {
this.userService
.removeUserGrant(grant.id, grant.userId)
.then(() => {
this.toast.showInfo('GRANTS.TOAST.REMOVED', true);
const data = this.dataSource.grantsSubject.getValue();
const index = data.findIndex(
(i) => (i as MgmtUserGrant.AsObject).id && (i as MgmtUserGrant.AsObject).id === grant.id,
);
if (index > -1) {
data.splice(index, 1);
this.dataSource.grantsSubject.next(data);
}
})
.catch((error) => {
this.toast.showError(error);
});
if (!resp) {
return;
}
this.authorizationService
.deleteAuthorization('id' in grant ? grant.id : grant.grantId)
.then(() => {
this.toast.showInfo('GRANTS.TOAST.REMOVED', true);
const data = this.dataSource.grantsSubject.getValue();
const index = data.findIndex((i) => {
if ('id' in i && 'id' in grant) {
return i.id === grant.id;
}
if ('grantId' in i && 'grantId' in grant) {
return i.grantId === grant.grantId;
}
return false;
});
if (index > -1) {
data.splice(index, 1);
this.dataSource.grantsSubject.next(data);
}
})
.catch((error: RpcError) => {
this.toast.showError(error);
});
});
}
@@ -267,7 +272,7 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
dialogRef.afterClosed().subscribe((resp) => {
if (resp) {
this.userService
this.mgmtService
.bulkRemoveUserGrant(this.selection.selected.map((grant) => (grant as MgmtUserGrant.AsObject).id))
.then(() => {
this.toast.showInfo('GRANTS.TOAST.BULKREMOVED', true);
@@ -0,0 +1,13 @@
import { inject, Injectable } from '@angular/core';
import { GrpcService } from './grpc.service';
@Injectable({
providedIn: 'root',
})
export class AuthorizationService {
private readonly grpcService = inject(GrpcService);
public deleteAuthorization(id: string) {
return this.grpcService.authorization.deleteAuthorization({ id });
}
}
+4
View File
@@ -28,10 +28,12 @@ import { createClientFor } from '@zitadel/client';
import { ActionService } from '@zitadel/proto/zitadel/action/v2/action_service_pb';
import { ApplicationService } from '@zitadel/proto/zitadel/application/v2/application_service_pb';
import { AuthorizationService } from '@zitadel/proto/zitadel/authorization/v2/authorization_service_pb';
import { WebKeyService } from '@zitadel/proto/zitadel/webkey/v2/webkey_service_pb';
const createWebKeyServiceClient = createClientFor(WebKeyService);
const createActionServiceClient = createClientFor(ActionService);
const createAuthorizationServiceClient = createClientFor(AuthorizationService);
const createApplicationServiceClient = createClientFor(ApplicationService);
@Injectable({
@@ -51,6 +53,7 @@ export class GrpcService {
public webKey!: ReturnType<typeof createWebKeyServiceClient>;
public organizationNew!: ReturnType<typeof createOrganizationServiceClient>;
public adminNew!: ReturnType<typeof createAdminServiceClient>;
public authorization!: ReturnType<typeof createAuthorizationServiceClient>;
public application!: ReturnType<typeof createApplicationServiceClient>;
public assets!: void;
@@ -111,6 +114,7 @@ export class GrpcService {
this.webKey = createWebKeyServiceClient(transport);
this.organizationNew = createOrganizationServiceClient(transport);
this.adminNew = createAdminServiceClient(transportOldAPIs);
this.authorization = createAuthorizationServiceClient(transport);
this.application = createApplicationServiceClient(transport);
const authConfig: AuthConfig = {