From 3098aca8ee3e5f373d2a70a872cebb5975a97f40 Mon Sep 17 00:00:00 2001 From: Nizamudeen A Date: Wed, 5 Mar 2025 22:16:03 +0530 Subject: [PATCH] mgr/dashboard: fix access control permissions for roles 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 (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 --- .../dashboard-pie/dashboard-pie.component.ts | 10 +++--- .../dashboard/dashboard-v3.component.html | 2 +- .../dashboard/dashboard-v3.component.ts | 19 +++++++----- .../rgw-multisite-details.component.html | 11 ++++--- .../rgw-multisite-details.component.ts | 31 ++++++++----------- .../mgr/dashboard/services/access_control.py | 5 +++ 6 files changed, 42 insertions(+), 36 deletions(-) diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-pie/dashboard-pie.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-pie/dashboard-pie.component.ts index b0c253c33e9..a341599e9ac 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-pie/dashboard-pie.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-pie/dashboard-pie.component.ts @@ -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)), diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.html index ac0110728e3..3b01ad1e381 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.html @@ -203,7 +203,7 @@ [fullHeight]="true" aria-label="Capacity card"> + *ngIf="capacity"> diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.ts index cf6e7116fdd..f43c15cc29e 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.ts @@ -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) { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.html index 3e7ac5eb8b6..4b3576b2c31 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.html @@ -1,6 +1,7 @@
- + Services diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.ts index 455dcebf91b..b05112863a8 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.ts @@ -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(); diff --git a/src/pybind/mgr/dashboard/services/access_control.py b/src/pybind/mgr/dashboard/services/access_control.py index 01d0557740b..6ca5505e6d2 100644 --- a/src/pybind/mgr/dashboard/services/access_control.py +++ b/src/pybind/mgr/dashboard/services/access_control.py @@ -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] }) -- 2.47.3