]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: fix access control permissions for roles
authorNizamudeen A <nia@redhat.com>
Wed, 5 Mar 2025 16:46:03 +0000 (22:16 +0530)
committerNizamudeen A <nia@redhat.com>
Tue, 3 Mar 2026 10:12:18 +0000 (15:42 +0530)
Since prometheus is being used in the dashboard page we need to make
sure every role has prometheus read only access so that the dashboard
page can load the utilization metrics.

I also saw permission issue with the osd settings endpoint when its
trying to get the nearfull/full ratio. so instead of failing the entire
page i am proceeding with a chart that doesn't have those details when
the user doesn't have permission to access the config opt.

Multisite page was not accessible in the case of rgw-manager or
read-only user because its trying to show the status of rgw module. This
si also now gracefully handled to show the alert only when the user has
sufficient permission.

Fixes: https://tracker.ceph.com/issues/70331
Signed-off-by: Nizamudeen A <nia@redhat.com>
(cherry picked from commit f4bc03e4040ca32591d9b46b79309b162c3942db)

 Conflicts:
src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.ts
 - kept changes only relavant to bug fix and ignored the other changes
   like h/w monitoring
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.html
 - ignored multisite wizard changes
src/pybind/mgr/dashboard/frontend/src/app/core/navigation/administration/administration.component.html
 - kept the current changes since carbon is not there in squid which
   means this issue is not present
src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.html
 - kept the current changes for the same reason above
src/pybind/mgr/dashboard/services/access_control.py
 - ignored the SMB role manager and kept only what's available in squid

src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-pie/dashboard-pie.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.ts
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.ts
src/pybind/mgr/dashboard/services/access_control.py

index b0c253c33e9d8c7c436c827ff5eb7a35668b02e4..a341599e9ac5cf8f3b51e7265289fc2be07a54f7 100644 (file)
@@ -15,9 +15,9 @@ export class DashboardPieComponent implements OnChanges, OnInit {
   @Input()
   data: any;
   @Input()
-  highThreshold: number;
+  highThreshold = 0;
   @Input()
-  lowThreshold: number;
+  lowThreshold = 0;
 
   color: string;
 
@@ -160,15 +160,15 @@ export class DashboardPieComponent implements OnChanges, OnInit {
     const percentAvailable = this.calcPercentage(data.max - data.current, data.max);
     const percentUsed = this.calcPercentage(data.current, data.max);
 
-    if (fullRatioPercent >= 0 && percentUsed >= fullRatioPercent) {
+    if (fullRatioPercent > 0 && percentUsed >= fullRatioPercent) {
       this.color = 'chart-color-red';
-    } else if (nearFullRatioPercent >= 0 && percentUsed >= nearFullRatioPercent) {
+    } else if (nearFullRatioPercent > 0 && percentUsed >= nearFullRatioPercent) {
       this.color = 'chart-color-yellow';
     } else {
       this.color = 'chart-color-blue';
     }
 
-    if (fullRatioPercent >= 0 && nearFullRatioPercent >= 0) {
+    if (fullRatioPercent > 0 && nearFullRatioPercent > 0) {
       chart.dataset[0].data = [
         Math.round(nearFullRatioPercent),
         Math.round(Math.abs(nearFullRatioPercent - fullRatioPercent)),
index ac0110728e38f1ad1b572350637d191ab4af142f..3b01ad1e381e21d6abbbb890723fe6fa3a530532 100644 (file)
                    [fullHeight]="true"
                    aria-label="Capacity card">
             <ng-container class="ms-4 me-4"
-                          *ngIf="capacity && osdSettings">
+                          *ngIf="capacity">
               <cd-dashboard-pie [data]="{max: capacity.total_bytes, current: capacity.total_used_raw_bytes}"
                                 [lowThreshold]="osdSettings.nearfull_ratio"
                                 [highThreshold]="osdSettings.full_ratio">
index cf6e7116fddbf6f9b891651f5678d35fd1161850..f43c15cc29e60fef709814624a9938c4b45c86f2 100644 (file)
@@ -23,6 +23,7 @@ import { PrometheusListHelper } from '~/app/shared/helpers/prometheus-list-helpe
 import { PrometheusAlertService } from '~/app/shared/services/prometheus-alert.service';
 import { OrchestratorService } from '~/app/shared/api/orchestrator.service';
 import { AlertClass } from '~/app/shared/enum/health-icon.enum';
+import { OsdSettings } from '~/app/shared/models/osd-settings';
 
 @Component({
   selector: 'cd-dashboard-v3',
@@ -32,7 +33,7 @@ import { AlertClass } from '~/app/shared/enum/health-icon.enum';
 export class DashboardV3Component extends PrometheusListHelper implements OnInit, OnDestroy {
   detailsCardData: DashboardDetails = {};
   osdSettingsService: any;
-  osdSettings: any;
+  osdSettings = new OsdSettings();
   interval = new Subscription();
   permissions: Permissions;
   enabledFeature$: FeatureTogglesMap$;
@@ -90,7 +91,8 @@ export class DashboardV3Component extends PrometheusListHelper implements OnInit
     super.ngOnInit();
     this.interval = this.refreshIntervalService.intervalData$.subscribe(() => {
       this.getHealth();
-      this.getCapacityCardData();
+      this.getCapacity();
+      if (this.permissions.configOpt.read) this.getOsdSettings();
     });
     this.getPrometheusData(this.prometheusService.lastHourDateObject);
     this.getDetailsCardData();
@@ -136,16 +138,19 @@ export class DashboardV3Component extends PrometheusListHelper implements OnInit
     );
   }
 
-  getCapacityCardData() {
+  private getCapacity() {
+    this.capacityService = this.healthService.getClusterCapacity().subscribe((data: any) => {
+      this.capacity = data;
+    });
+  }
+
+  private getOsdSettings() {
     this.osdSettingsService = this.osdService
       .getOsdSettings()
       .pipe(take(1))
-      .subscribe((data: any) => {
+      .subscribe((data: OsdSettings) => {
         this.osdSettings = data;
       });
-    this.capacityService = this.healthService.getClusterCapacity().subscribe((data: any) => {
-      this.capacity = data;
-    });
   }
 
   public getPrometheusData(selectedTime: any) {
index 3e7ac5eb8b6732cc7fc3d0ed77d03764c2102993..4b3576b2c3108c02433b85f1b7dda75234db8213 100644 (file)
@@ -1,6 +1,7 @@
 <cd-rgw-multisite-tabs></cd-rgw-multisite-tabs>
 <div>
-  <cd-alert-panel *ngIf="!rgwModuleStatus"
+  <!-- Show the alert only when the user has the permission to configure -->
+  <cd-alert-panel *ngIf="permissions.configOpt.create && !rgwModuleStatus"
                   type="info"
                   spacingClass="mb-3"
                   class="d-flex align-items-center"
        Cluster->Services</a>
   </cd-alert-panel>
   <cd-table-actions class="btn-group mb-4 me-2"
-                    [permission]="permission"
+                    [permission]="permissions.rgw"
                     [selection]="selection"
                     [tableActions]="createTableActions">
   </cd-table-actions>
   <cd-table-actions class="btn-group mb-4 me-2 secondary"
-                    [permission]="permission"
+                    [permission]="permissions.rgw"
                     [btnColor]="'light'"
                     [selection]="selection"
                     [tableActions]="migrateTableAction">
   </cd-table-actions>
   <cd-table-actions class="btn-group mb-4 me-2"
-                    [permission]="permission"
+                    [permission]="permissions.rgw"
                     [btnColor]="'light'"
                     [selection]="selection"
                     [tableActions]="importAction">
   </cd-table-actions>
   <cd-table-actions class="btn-group mb-4 me-2"
-                    [permission]="permission"
+                    [permission]="permissions.rgw"
                     [btnColor]="'light'"
                     [selection]="selection"
                     [tableActions]="exportAction">
index 455dcebf91b864d3daa2f018882ed386d497a3b1..b05112863a8ae3abce44f6fc5a40e55fbccc9112 100644 (file)
@@ -19,7 +19,7 @@ import { Icons } from '~/app/shared/enum/icons.enum';
 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
 import { CdTableAction } from '~/app/shared/models/cd-table-action';
 import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
-import { Permission } from '~/app/shared/models/permissions';
+import { Permissions } from '~/app/shared/models/permissions';
 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
 import { ModalService } from '~/app/shared/services/modal.service';
 import { NotificationService } from '~/app/shared/services/notification.service';
@@ -61,7 +61,7 @@ export class RgwMultisiteDetailsComponent implements OnDestroy, OnInit {
   blockUI: NgBlockUI;
 
   icons = Icons;
-  permission: Permission;
+  permissions: Permissions;
   selection = new CdTableSelection();
   createTableActions: CdTableAction[];
   migrateTableAction: CdTableAction[];
@@ -117,7 +117,7 @@ export class RgwMultisiteDetailsComponent implements OnDestroy, OnInit {
     public mgrModuleService: MgrModuleService,
     private notificationService: NotificationService
   ) {
-    this.permission = this.authStorageService.getPermissions().rgw;
+    this.permissions = this.authStorageService.getPermissions();
   }
 
   openModal(entity: any, edit = false) {
@@ -263,22 +263,17 @@ export class RgwMultisiteDetailsComponent implements OnDestroy, OnInit {
         },
         (_error) => {}
       );
-    this.mgrModuleService.list().subscribe((moduleData: any) => {
-      this.rgwModuleData = moduleData.filter((module: object) => module['name'] === 'rgw');
-      if (this.rgwModuleData.length > 0) {
-        this.rgwModuleStatus = this.rgwModuleData[0].enabled;
-      }
-    });
+
+    // Only get the module status if you can read from configOpt
+    if (this.permissions.configOpt.read) {
+      this.mgrModuleService.list().subscribe((moduleData: any) => {
+        this.rgwModuleData = moduleData.filter((module: object) => module['name'] === 'rgw');
+        if (this.rgwModuleData.length > 0) {
+          this.rgwModuleStatus = this.rgwModuleData[0].enabled;
+        }
+      });
+    }
   }
-  /* setConfigValues() {
-    this.rgwDaemonService
-      .setMultisiteConfig(
-        this.defaultsInfo['defaultRealmName'],
-        this.defaultsInfo['defaultZonegroupName'],
-        this.defaultsInfo['defaultZoneName']
-      )
-      .subscribe(() => {});
-  }*/
 
   ngOnDestroy() {
     this.sub.unsubscribe();
index 01d0557740b0f5450bb86c15a5f47439a4e29aba..6ca5505e6d2849cf38b98c87d555725e21b15066 100644 (file)
@@ -223,6 +223,7 @@ BLOCK_MGR_ROLE = Role(
         Scope.RBD_MIRRORING: [_P.READ, _P.CREATE, _P.UPDATE, _P.DELETE],
         Scope.GRAFANA: [_P.READ],
         Scope.NVME_OF: [_P.READ, _P.CREATE, _P.UPDATE, _P.DELETE],
+        Scope.PROMETHEUS: [_P.READ]
     })
 
 
@@ -231,6 +232,7 @@ RGW_MGR_ROLE = Role(
     'rgw-manager', 'allows full permissions for the rgw scope', {
         Scope.RGW: [_P.READ, _P.CREATE, _P.UPDATE, _P.DELETE],
         Scope.GRAFANA: [_P.READ],
+        Scope.PROMETHEUS: [_P.READ]
     })
 
 
@@ -255,6 +257,7 @@ POOL_MGR_ROLE = Role(
     'pool-manager', 'allows full permissions for the pool scope', {
         Scope.POOL: [_P.READ, _P.CREATE, _P.UPDATE, _P.DELETE],
         Scope.GRAFANA: [_P.READ],
+        Scope.PROMETHEUS: [_P.READ]
     })
 
 # CephFS manager role provides all permissions for CephFS related scopes
@@ -262,6 +265,7 @@ CEPHFS_MGR_ROLE = Role(
     'cephfs-manager', 'allows full permissions for the cephfs scope', {
         Scope.CEPHFS: [_P.READ, _P.CREATE, _P.UPDATE, _P.DELETE],
         Scope.GRAFANA: [_P.READ],
+        Scope.PROMETHEUS: [_P.READ]
     })
 
 GANESHA_MGR_ROLE = Role(
@@ -270,6 +274,7 @@ GANESHA_MGR_ROLE = Role(
         Scope.CEPHFS: [_P.READ, _P.CREATE, _P.UPDATE, _P.DELETE],
         Scope.RGW: [_P.READ, _P.CREATE, _P.UPDATE, _P.DELETE],
         Scope.GRAFANA: [_P.READ],
+        Scope.PROMETHEUS: [_P.READ]
     })