]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Disable multisite Action dropdown for read-only user 69639/head
authorDevika Babrekar <devika.babrekar@ibm.com>
Mon, 22 Jun 2026 14:53:17 +0000 (20:23 +0530)
committerDevika Babrekar <devika.babrekar@ibm.com>
Fri, 17 Jul 2026 11:34:50 +0000 (17:04 +0530)
Fixes: https://tracker.ceph.com/issues/77570
Signed-off-by: Devika Babrekar <devika.babrekar@ibm.com>
Conflicts:
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.html
- added disabled property on table component to disable the table
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.html
- added disabled property on table component to disable the table

src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.html
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.ts

index 448acead01e8e69d28dc558dee5290c743fbfce3..d95986556de6d6cb1d0546d78c2fc3f529191e7a 100644 (file)
@@ -53,6 +53,7 @@
       dropDownOnly="Actions"
       [dropDownOnlyBtnColor]="'tertiary'"
       [dropDownOnlyOffset]="{ x: 45, y: 0 }"
+      [disabled]="disableActions"
     >
     </cd-table-actions>
   </div>
index ed2b838eefb5f739710a414c6689bf7cd9f22572..527b14c1e103825d47fb516fd83ee19f24d38fff 100644 (file)
@@ -1,6 +1,7 @@
 import { HttpClientTestingModule } from '@angular/common/http/testing';
 import { DebugElement } from '@angular/core';
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
 
 import { SharedModule } from '~/app/shared/shared.module';
 
@@ -10,6 +11,11 @@ import { configureTestBed } from '~/testing/unit-test-helper';
 import { NgbNavModule, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
 import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
 import { RgwMultisiteRealmFormComponent } from '../rgw-multisite-realm-form/rgw-multisite-realm-form.component';
+import { TableActionsComponent } from '~/app/shared/datatable/table-actions/table-actions.component';
+import { Icons } from '~/app/shared/enum/icons.enum';
+import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
+import { Permission, Permissions } from '~/app/shared/models/permissions';
+import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
 
 describe('RgwMultisiteDetailsComponent', () => {
   let component: RgwMultisiteDetailsComponent;
@@ -56,4 +62,94 @@ describe('RgwMultisiteDetailsComponent', () => {
       multisiteInfo: component.multisiteInfo
     });
   });
+
+  describe('disableActions', () => {
+    it('should return true when rgw.create is false', () => {
+      component.permissions = new Permissions({ rgw: ['read'] });
+      expect(component.disableActions).toBe(true);
+    });
+
+    it('should return false when rgw.create is true', () => {
+      component.permissions = new Permissions({ rgw: ['read', 'create'] });
+      expect(component.disableActions).toBe(false);
+    });
+  });
+
+  describe('multisite create table actions', () => {
+    const createFixture = (rgwPermissions: string[]) => {
+      spyOn(TestBed.inject(AuthStorageService), 'getPermissions').and.returnValue(
+        new Permissions({ rgw: rgwPermissions, 'config-opt': ['read'] })
+      );
+      const testFixture = TestBed.createComponent(RgwMultisiteDetailsComponent);
+      testFixture.detectChanges();
+      return testFixture;
+    };
+
+    const getMultisiteTableActions = (
+      testFixture: ComponentFixture<RgwMultisiteDetailsComponent>
+    ): TableActionsComponent =>
+      testFixture.debugElement.query(By.css('cd-table-actions.multisite-actions'))
+        .componentInstance;
+
+    it('should pass disabled=true to TableActionsComponent when rgw.create is false', () => {
+      const testFixture = createFixture(['read']);
+      const tableActions = getMultisiteTableActions(testFixture);
+      const button = testFixture.debugElement.query(
+        By.css('cd-table-actions.multisite-actions button.cds--btn--tertiary')
+      );
+
+      expect(testFixture.componentInstance.disableActions).toBe(true);
+      expect(tableActions.disabled).toBe(true);
+      expect(button.nativeElement.disabled).toBe(true);
+    });
+
+    it('should pass disabled=false to TableActionsComponent when rgw.create is true', () => {
+      const testFixture = createFixture(['read', 'create']);
+      const tableActions = getMultisiteTableActions(testFixture);
+      const button = testFixture.debugElement.query(
+        By.css('cd-table-actions.multisite-actions button.cds--btn--tertiary')
+      );
+
+      expect(testFixture.componentInstance.disableActions).toBe(false);
+      expect(tableActions.disabled).toBe(false);
+      expect(button.nativeElement.disabled).toBe(false);
+    });
+  });
+
+  describe('TableActionsComponent disabled input', () => {
+    const createTableActionsFixture = (disabled: boolean) => {
+      const tableActionsFixture = TestBed.createComponent(TableActionsComponent);
+      const tableActionsComponent = tableActionsFixture.componentInstance;
+      tableActionsComponent.permission = new Permission(['create']);
+      tableActionsComponent.dropDownOnly = 'Actions';
+      tableActionsComponent.dropDownOnlyBtnColor = 'tertiary';
+      tableActionsComponent.tableActions = [
+        { permission: 'create', icon: Icons.add, name: 'Create Realm', click: () => undefined }
+      ];
+      tableActionsComponent.selection = new CdTableSelection();
+      tableActionsComponent.disabled = disabled;
+      tableActionsFixture.detectChanges();
+      return tableActionsFixture;
+    };
+
+    const getDropdownTriggerButton = (
+      tableActionsFixture: ComponentFixture<TableActionsComponent>
+    ) => tableActionsFixture.debugElement.query(By.css('button.cds--btn--tertiary'));
+
+    it('should disable the trigger button when disabled is true', () => {
+      const tableActionsFixture = createTableActionsFixture(true);
+      const button = getDropdownTriggerButton(tableActionsFixture);
+
+      expect(tableActionsFixture.componentInstance.disabled).toBe(true);
+      expect(button.nativeElement.disabled).toBe(true);
+    });
+
+    it('should enable the trigger button when disabled is false', () => {
+      const tableActionsFixture = createTableActionsFixture(false);
+      const button = getDropdownTriggerButton(tableActionsFixture);
+
+      expect(tableActionsFixture.componentInstance.disabled).toBe(false);
+      expect(button.nativeElement.disabled).toBe(false);
+    });
+  });
 });
index 0d0b56ef2d400ca5a3412d4b4b2234c83674e98c..2b448298aa649cdaf1cc603776dbec376ed788e7 100644 (file)
@@ -125,6 +125,10 @@ export class RgwMultisiteDetailsComponent extends CdForm implements OnDestroy, O
   MODULE_NAME = 'rgw';
   NAVIGATE_TO = '/rgw/multisite';
 
+  get disableActions(): boolean {
+    return !this.permissions?.rgw?.create;
+  }
+
   constructor(
     private modalService: ModalService,
     private timerService: TimerService,
index 6021cfa1f5da1f74b37a95f7c546d7bbfeb3f708..bfd6a5753c422b351e4a8bfbb45b0e5a3a9f4196 100644 (file)
@@ -94,6 +94,7 @@
   <button
     [cdsButton]="dropDownOnlyBtnColor"
     type="button"
+    [disabled]="disabled"
   >
     <span i18n>{{ dropDownOnly }}</span>
     <svg
index 2e3f4046646a45efd74095efa3227eccbbc89fa9..d670b8a932244b47e556bd3f4f7384f782166b5a 100644 (file)
@@ -31,6 +31,8 @@ export class TableActionsComponent implements OnChanges, OnInit {
   dropDownOnly?: string;
   @Input()
   primaryDropDown = false;
+  @Input()
+  disabled: boolean = false;
 
   currentAction?: CdTableAction;
   // Array with all visible actions