From: Devika Babrekar Date: Wed, 8 Jul 2026 13:48:51 +0000 (+0530) Subject: mgr/dashboard: disabling role clearning button for admin user X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=31565e1d390271718c6ed1210b7f20f32dfd8ac0;p=ceph.git mgr/dashboard: disabling role clearning button for admin user Signed-off-by: Devika Babrekar --- diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.html b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.html index bdfe497b9dd..5fdf1b99fd8 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.html @@ -217,6 +217,7 @@
- + @@ -243,29 +245,8 @@ class="invalid-feedback" *ngIf="userForm.showError('roles', formDir, 'required')" i18n - >This field is required. - - - -
- -
-
- - - You cannot remove the administrator role from your own account. + >This field is required.
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.scss index e69de29bb2d..05625dd22bd 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.scss +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.scss @@ -0,0 +1,7 @@ +.roles-clear-disabled { + cds-combo-box .cds--list-box__selection--multi { + pointer-events: none; + cursor: not-allowed; + opacity: 0.5; + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.spec.ts index 850dff1c5a7..bef5414cb57 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.spec.ts @@ -205,6 +205,7 @@ describe('UserFormComponent', () => { beforeEach(() => { spyOn(userService, 'get').and.callFake(() => of(user)); spyOn(TestBed.inject(RoleService), 'list').and.callFake(() => of(roles)); + spyOn(TestBed.inject(AuthStorageService), 'getUsername').and.returnValue(user.username); setUrl('/user-management/users/edit/user1'); spyOn(TestBed.inject(SettingsService), 'getStandardSettings').and.callFake(() => of({ @@ -249,8 +250,19 @@ describe('UserFormComponent', () => { expect(form.get('confirmpassword').valid).toBeTruthy(); }); + it('should disable administrator role for current user', () => { + const administratorRole = component.allRoles.find((role) => role.name === 'administrator'); + expect(administratorRole.disabled).toBeTruthy(); + expect(component.disableRolesClearButton()).toBeTruthy(); + }); + + it('should restore roles when clear is triggered for current user with protected admin role', () => { + formHelper.setValue('roles', []); + component.onRolesClear(); + expect(form.getValue('roles')).toContain('administrator'); + }); + it('should alert if user is removing needed role permission', () => { - spyOn(TestBed.inject(AuthStorageService), 'getUsername').and.callFake(() => user.username); let modalBodyTpl = null; spyOn(modalService, 'show').and.callFake((_content, initialState) => { modalBodyTpl = initialState.bodyTpl; @@ -261,7 +273,6 @@ describe('UserFormComponent', () => { }); it('should logout if current user roles have been changed', () => { - spyOn(TestBed.inject(AuthStorageService), 'getUsername').and.callFake(() => user.username); formHelper.setValue('roles', ['user-manager']); component.submit(); const userReq = httpTesting.expectOne(`api/user/${user.username}`); @@ -272,7 +283,6 @@ describe('UserFormComponent', () => { }); it('should submit', () => { - spyOn(TestBed.inject(AuthStorageService), 'getUsername').and.callFake(() => user.username); component.submit(); const userReq = httpTesting.expectOne(`api/user/${user.username}`); expect(userReq.request.method).toBe('PUT'); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.ts index f994c0b4d2e..4a2242ddc1b 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { Component, OnInit, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core'; import { Validators } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; @@ -33,7 +33,8 @@ import { UserFormModel } from './user-form.model'; selector: 'cd-user-form', templateUrl: './user-form.component.html', styleUrls: ['./user-form.component.scss'], - standalone: false + standalone: false, + encapsulation: ViewEncapsulation.None }) export class UserFormComponent extends CdForm implements OnInit { @ViewChild('removeSelfUserReadUpdatePermissionTpl', { static: true }) @@ -59,6 +60,8 @@ export class UserFormComponent extends CdForm implements OnInit { selectedRole: string[]; passwordexp: boolean = false; isSSO = false; + isAdminRoleProtected: boolean = false; + constructor( private authService: AuthService, private authStorageService: AuthStorageService, @@ -180,13 +183,13 @@ export class UserFormComponent extends CdForm implements OnInit { this.userService.get(username).subscribe((userFormModel: UserFormModel) => { this.response = _.cloneDeep(userFormModel); this.setResponse(userFormModel); - if (this.authStorageService.getUsername() === username) { - this.allRoles = _.map(this.allRoles, (role) => { - role.disabled = - role.name.toLowerCase() === 'administrator' && this.isCurrentUser() ? true : false; - return role; - }); + if (this.authStorageService.getUsername() === userFormModel.username) { + this.allRoles = _.map(this.allRoles, (role) => ({ + ...role, + disabled: role.name.toLowerCase() === 'administrator' + })); } + this.isAdminRoleProtected = this.disableRolesClearButton(); this.loadingReady(); }); }); @@ -276,6 +279,26 @@ export class UserFormComponent extends CdForm implements OnInit { return this.authStorageService.getUsername() === this.userForm.getValue('username'); } + disableRolesClearButton(): boolean { + if (!this.isCurrentUser() || !this.allRoles) { + return false; + } + const administratorRole = this.allRoles.find( + (role) => role.name.toLowerCase() === 'administrator' + ); + return !!administratorRole?.disabled; + } + + onRolesClear(): void { + if (!this.disableRolesClearButton()) { + return; + } + const roles = this.userForm.getValue('roles') ?? []; + if (!roles.includes('administrator')) { + this.userForm.get('roles').setValue([...roles, 'administrator'], { emitEvent: false }); + } + } + private isUserChangingRoles(): boolean { const isCurrentUser = this.isCurrentUser(); return (