]> git.apps.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>
Wed, 19 Mar 2025 14:23:30 +0000 (19:53 +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>
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/frontend/src/app/core/navigation/administration/administration.component.html
src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.html
src/pybind/mgr/dashboard/services/access_control.py

index fa194024db9eefb13613de6d515ad5754f29f41a..372e6baf3de582cf9884dd1eb961d060b3c95db1 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;
 
@@ -162,15 +162,15 @@ export class DashboardPieComponent implements OnChanges, OnInit {
     const percentAvailable = this.calcPercentage(max - current, max);
     const percentUsed = this.calcPercentage(current, 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 fda0b407587a464c1cdd2da12345cf60b708e39d..6f5c535fd8d4114d39b47c1550d038b5ec711f18 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 72314baac47ff7ba02ffb40d7b6a6c2fce66b578..c122d3bc01422b8f0478216a4860b249c1700095 100644 (file)
@@ -26,6 +26,7 @@ import { MgrModuleService } from '~/app/shared/api/mgr-module.service';
 import { AlertClass } from '~/app/shared/enum/health-icon.enum';
 import { HardwareService } from '~/app/shared/api/hardware.service';
 import { SettingsService } from '~/app/shared/api/settings.service';
+import { OsdSettings } from '~/app/shared/models/osd-settings';
 
 @Component({
   selector: 'cd-dashboard-v3',
@@ -35,7 +36,7 @@ import { SettingsService } from '~/app/shared/api/settings.service';
 export class DashboardV3Component extends PrometheusListHelper implements OnInit, OnDestroy {
   detailsCardData: DashboardDetails = {};
   osdSettingsService: any;
-  osdSettings: any;
+  osdSettings = new OsdSettings();
   interval = new Subscription();
   permissions: Permissions;
   enabledFeature$: FeatureTogglesMap$;
@@ -117,7 +118,8 @@ export class DashboardV3Component extends PrometheusListHelper implements OnInit
     }
     this.interval = this.refreshIntervalService.intervalData$.subscribe(() => {
       this.getHealth();
-      this.getCapacityCardData();
+      this.getCapacity();
+      if (this.permissions.configOpt.read) this.getOsdSettings();
       if (this.hardwareEnabled) this.hardwareSubject.next([]);
     });
     this.getPrometheusData(this.prometheusService.lastHourDateObject);
@@ -165,16 +167,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 c3b740ec7c68250f92be0c360763738bac96d009..fa798ea067ac97f03ca668d35c677db24e7bbbe8 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]="multisiteReplicationActions">
   </cd-table-actions>
   <cd-table-actions *ngIf="showMigrateAndReplicationActions"
                     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 *ngIf="!showMigrateAndReplicationActions"
                     class="btn-group mb-4 me-2"
-                    [permission]="permission"
+                    [permission]="permissions.rgw"
                     [selection]="selection"
                     [tableActions]="createTableActions"
                     [primaryDropDown]="true">
   </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 c213ae26a599c5d16fbbda157eb938c486080ed9..fa1af615b27bf11bfc2365d099c53d6ccc3916f8 100644 (file)
@@ -21,7 +21,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';
@@ -68,7 +68,7 @@ export class RgwMultisiteDetailsComponent implements OnDestroy, OnInit {
   blockUI: NgBlockUI;
 
   icons = Icons;
-  permission: Permission;
+  permissions: Permissions;
   selection = new CdTableSelection();
   createTableActions: CdTableAction[];
   migrateTableAction: CdTableAction[];
@@ -144,7 +144,7 @@ export class RgwMultisiteDetailsComponent implements OnDestroy, OnInit {
     private rgwMultisiteService: RgwMultisiteService,
     private changeDetectionRef: ChangeDetectorRef
   ) {
-    this.permission = this.authStorageService.getPermissions().rgw;
+    this.permissions = this.authStorageService.getPermissions();
   }
 
   openModal(entity: any | string, edit = false) {
@@ -305,22 +305,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 e0d508172feb8db2290719759eb785be6b595308..bdabdd0b643940aa6b5c4b090bb1a6b96fc4dae2 100644 (file)
@@ -1,7 +1,6 @@
 <cds-overflow-menu [customTrigger]="customTrigger"
                    [flip]="true">
-  <li class="cds--overflow-menu-options__option mb-2"
-      *ngIf="userPermission.read">
+  <li class="cds--overflow-menu-options__option mb-2">
     <button routerLink="/user-management"
             class="cds--overflow-menu-options__btn"
             i18n>User management</button>
index a544bbcdb436cf3cee1a8aa52be7c0bbf32e54df..13b0d86b76338ea32be59b4b313fa4a3b6d9aa43 100644 (file)
@@ -39,7 +39,8 @@
       <div class="cds--btn cds--btn--icon-only cds--header__action">
         <cd-dashboard-help></cd-dashboard-help>
       </div>
-      <div class="cds--btn cds--btn--icon-only cds--header__action">
+      <div class="cds--btn cds--btn--icon-only cds--header__action"
+           *ngIf="permissions.user.read">
         <cd-administration></cd-administration>
       </div>
       <div class="cds--btn cds--btn--icon-only cds--header__action">
@@ -93,6 +94,7 @@
         </cds-sidenav-item>
         <!-- Multi-cluster Dashboard -->
         <cds-sidenav-menu title="Multi-Cluster"
+                          *ngIf="permissions.configOpt.read"
                           i18n-title>
           <svg cdsIcon="edge-cluster"
                icon
index 6319802b6cc7ba0e309b01154d68f8efb2f67f6d..c9898f7b7c7620636c5c468917151855237a7e4b 100644 (file)
@@ -232,6 +232,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]
     })
 
 
@@ -240,6 +241,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]
     })
 
 
@@ -263,6 +265,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
@@ -270,6 +273,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(
@@ -278,7 +282,8 @@ 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.SMB: [_P.READ]
+        Scope.SMB: [_P.READ],
+        Scope.PROMETHEUS: [_P.READ]
     })
 
 SMB_MGR_ROLE = Role(
@@ -287,7 +292,8 @@ SMB_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.NFS_GANESHA: [_P.READ]
+        Scope.NFS_GANESHA: [_P.READ],
+        Scope.PROMETHEUS: [_P.READ]
     })