]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: disabling role clearning button for admin user 69829/head
authorDevika Babrekar <devika.babrekar@ibm.com>
Wed, 8 Jul 2026 13:48:51 +0000 (19:18 +0530)
committerDevika Babrekar <devika.babrekar@ibm.com>
Thu, 16 Jul 2026 04:59:32 +0000 (10:29 +0530)
Signed-off-by: Devika Babrekar <devika.babrekar@ibm.com>
src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.scss
src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.ts

index bdfe497b9dda6de3554da6a52079d4bd14d8ce40..5fdf1b99fd85e63f7ab8be3ecf0b992c00944328 100644 (file)
       <div
         class="form-item"
         *ngIf="allRoles"
+        [class.roles-clear-disabled]="isAdminRoleProtected"
       >
         <cds-combo-box
           label="Roles"
           [cdRequiredField]="!isSSO ? 'Roles' : ''"
           [invalid]="!userForm.controls.roles.valid && userForm.controls.roles.dirty"
           [invalidText]="rolesError"
+          (clear)="onRolesClear()"
           i18n
         >
-          <cds-dropdown-list [listTpl]="roleItemTpl"></cds-dropdown-list>
+          <cds-dropdown-list></cds-dropdown-list>
         </cds-combo-box>
 
         <ng-template #rolesError>
             class="invalid-feedback"
             *ngIf="userForm.showError('roles', formDir, 'required')"
             i18n
-            >This field is required.</span>
-        </ng-template>        
-        
-        <ng-template #roleItemTpl
-                    let-item="item">
-          <div class="cds--form-item cds--checkbox-wrapper">
-            <label class="cds--checkbox-label"
-                  [attr.data-contained-checkbox-disabled]="item.disabled ? 'true' : null"
-                  [attr.data-contained-checkbox-state]="item.selected ? 'true' : 'false'"
-                  [ngbTooltip]="item.disabled ? roleTooltipTpl : null">
-              <input class="cds--checkbox"
-                    type="checkbox"
-                    [checked]="item.selected"
-                    [disabled]="item.disabled"
-                    tabindex="-1"/>
-              <span class="cds--checkbox-appearance"></span>
-              <span class="cds--checkbox-label-text">{{ item.content }}</span>
-            </label>
-          </div>
-        </ng-template>
-
-        <ng-template #roleTooltipTpl>
-          <span i18n>You cannot remove the administrator role from your own account.</span>
+            >This field is required.</span
+          >
         </ng-template>
       </div>
       <!-- Enabled -->
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..05625dd22bd11f6e2b6c5a3df532383df28c013b 100644 (file)
@@ -0,0 +1,7 @@
+.roles-clear-disabled {
+  cds-combo-box .cds--list-box__selection--multi {
+    pointer-events: none;
+    cursor: not-allowed;
+    opacity: 0.5;
+  }
+}
index 850dff1c5a7d7a8b88c3cc6065719f44587cf28d..bef5414cb573836ad18aaf226d6a20f722b4d123 100644 (file)
@@ -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');
index f994c0b4d2e2c20e5803de87b9cdd2777a725bd6..4a2242ddc1bb079f21d135753e9be954d8a9b453 100644 (file)
@@ -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 (