mirror of
https://github.com/ceph/ceph
synced 2026-08-01 22:45:39 +00:00
mgr/dashboard: grant hosts read to block-manager role
Block-manager users were denied access to the NVMe/TCP page because host listing APIs require hosts read permission. Fixes: https://tracker.ceph.com/issues/77952 Signed-off-by: Abhishek Desai <adesai@redhat.com>
This commit is contained in:
parent
622483c52e
commit
0d0a241ba7
@ -30,7 +30,7 @@ class HostControllerTest(DashboardTestCase):
|
||||
def test_data_daemons(self):
|
||||
return self.ORCHESTRATOR_TEST_DATA['daemons']
|
||||
|
||||
@DashboardTestCase.RunAs('test', 'test', ['block-manager'])
|
||||
@DashboardTestCase.RunAs('test', 'test', ['rgw-manager'])
|
||||
def test_access_permissions(self):
|
||||
self._get(self.URL_HOST, version='1.1')
|
||||
self.assertStatus(403)
|
||||
|
||||
@ -395,7 +395,8 @@ class Health(BaseController):
|
||||
'quorum': data.get('quorum', {})
|
||||
}
|
||||
|
||||
if self._has_permissions(Permission.READ, Scope.OSD):
|
||||
if (self._has_permissions(Permission.READ, Scope.OSD)
|
||||
or self._has_permissions(Permission.READ, Scope.POOL)):
|
||||
summary['osdmap'] = {
|
||||
'in': data.get('osdmap', {}).get('num_in_osds'),
|
||||
'up': data.get('osdmap', {}).get('num_up_osds'),
|
||||
|
||||
@ -24,14 +24,17 @@
|
||||
></a
|
||||
>
|
||||
</ng-container>
|
||||
@if (canViewSilences) {
|
||||
@if (prometheusPermissions.read) {
|
||||
<ng-container ngbNavItem>
|
||||
<a class="nav-link"
|
||||
routerLink="/monitoring/silences"
|
||||
routerLinkActive="active"
|
||||
ariaCurrentWhenActive="page"
|
||||
[routerLinkActiveOptions]="{exact: true}"
|
||||
i18n>Silences</a>
|
||||
<a
|
||||
class="nav-link"
|
||||
routerLink="/monitoring/silences"
|
||||
routerLinkActive="active"
|
||||
ariaCurrentWhenActive="page"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
i18n
|
||||
>Silences</a
|
||||
>
|
||||
</ng-container>
|
||||
}
|
||||
<ng-container ngbNavItem>
|
||||
|
||||
@ -40,6 +40,6 @@ describe('PrometheusTabsComponent', () => {
|
||||
});
|
||||
|
||||
it('should show silences to users with read access', () => {
|
||||
expect(component.canViewSilences).toBe(true);
|
||||
expect(component.prometheusPermissions.read).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@ -2,6 +2,7 @@ import { Component } from '@angular/core';
|
||||
|
||||
import { PrometheusAlertService } from '~/app/shared/services/prometheus-alert.service';
|
||||
import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
|
||||
import { Permission } from '~/app/shared/models/permissions';
|
||||
|
||||
@Component({
|
||||
selector: 'cd-prometheus-tabs',
|
||||
@ -10,13 +11,12 @@ import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
|
||||
standalone: false
|
||||
})
|
||||
export class PrometheusTabsComponent {
|
||||
canViewSilences: boolean;
|
||||
prometheusPermissions: Permission;
|
||||
|
||||
constructor(
|
||||
public prometheusAlertService: PrometheusAlertService,
|
||||
private authStorageService: AuthStorageService
|
||||
) {
|
||||
const prometheusPermission = this.authStorageService.getPermissions().prometheus;
|
||||
this.canViewSilences = prometheusPermission.read;
|
||||
this.prometheusPermissions = this.authStorageService.getPermissions().prometheus;
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,7 +99,12 @@ export class OverviewComponent {
|
||||
);
|
||||
|
||||
readonly hasNoOSDs$ = this.healthData$.pipe(
|
||||
map((data: HealthSnapshotMap) => (data?.osdmap?.num_osds ?? 0) === 0),
|
||||
map((data: HealthSnapshotMap) => {
|
||||
if (data?.osdmap == null) {
|
||||
return false;
|
||||
}
|
||||
return data.osdmap.num_osds === 0;
|
||||
}),
|
||||
shareReplay({ bufferSize: 1, refCount: true })
|
||||
);
|
||||
|
||||
|
||||
@ -31,11 +31,17 @@
|
||||
></svg>
|
||||
</a>
|
||||
</li>
|
||||
<cds-overflow-menu-option (click)="openAboutModal()"
|
||||
i18n>About</cds-overflow-menu-option>
|
||||
<cds-overflow-menu-option
|
||||
(click)="openAboutModal()"
|
||||
i18n
|
||||
>About</cds-overflow-menu-option
|
||||
>
|
||||
@if (configOptPermission.read) {
|
||||
<cds-overflow-menu-option (click)="openFeedbackModal()"
|
||||
i18n>Report an issue...</cds-overflow-menu-option>
|
||||
<cds-overflow-menu-option
|
||||
(click)="openFeedbackModal()"
|
||||
i18n
|
||||
>Report an issue...</cds-overflow-menu-option
|
||||
>
|
||||
}
|
||||
</cds-overflow-menu>
|
||||
|
||||
|
||||
@ -33,6 +33,16 @@ describe('DashboardHelpComponent', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show report issue when config-opt is readable', () => {
|
||||
const options = fixture.debugElement.queryAll(By.css('cds-overflow-menu-option'));
|
||||
// About + Report an issue (Documentation and API are plain <li> links)
|
||||
expect(options.length).toBe(2);
|
||||
expect(options.map((o) => o.nativeElement.textContent.trim())).toEqual([
|
||||
'About',
|
||||
'Report an issue...'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should hide report issue when config-opt is not readable', () => {
|
||||
permissions.configOpt = new Permission([]);
|
||||
(TestBed.inject(AuthStorageService).getPermissions as jasmine.Spy).and.returnValue(permissions);
|
||||
@ -42,6 +52,8 @@ describe('DashboardHelpComponent', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const options = fixture.debugElement.queryAll(By.css('cds-overflow-menu-option'));
|
||||
expect(options.length).toBe(3);
|
||||
// Only About remains; Documentation and API are plain <li> links
|
||||
expect(options.length).toBe(1);
|
||||
expect(options[0].nativeElement.textContent.trim()).toBe('About');
|
||||
});
|
||||
});
|
||||
|
||||
@ -218,7 +218,8 @@ ADMIN_ROLE = Role(
|
||||
# read-only role provides read-only permission for all scopes
|
||||
READ_ONLY_ROLE = Role(
|
||||
'read-only',
|
||||
'allows read permission for all security scope except user, dashboard settings and config-opt', {
|
||||
'allows read permission for all security scope except user, '
|
||||
'dashboard settings and config-opt', {
|
||||
scope_name: [_P.READ] for scope_name in Scope.all_scopes()
|
||||
if scope_name not in (Scope.USER, Scope.DASHBOARD_SETTINGS, Scope.CONFIG_OPT)
|
||||
})
|
||||
@ -231,6 +232,7 @@ BLOCK_MGR_ROLE = Role(
|
||||
Scope.POOL: [_P.READ],
|
||||
Scope.ISCSI: [_P.READ, _P.CREATE, _P.UPDATE, _P.DELETE],
|
||||
Scope.RBD_MIRRORING: [_P.READ, _P.CREATE, _P.UPDATE, _P.DELETE],
|
||||
Scope.HOSTS: [_P.READ],
|
||||
Scope.GRAFANA: [_P.READ],
|
||||
Scope.NVME_OF: [_P.READ, _P.CREATE, _P.UPDATE, _P.DELETE],
|
||||
Scope.PROMETHEUS: [_P.READ]
|
||||
@ -293,6 +295,7 @@ SMB_MGR_ROLE = Role(
|
||||
'smb-manager', 'allows full permissions for the smb scope', {
|
||||
Scope.SMB: [_P.READ, _P.CREATE, _P.UPDATE, _P.DELETE],
|
||||
Scope.HOSTS: [_P.READ],
|
||||
Scope.POOL: [_P.READ],
|
||||
Scope.CEPHFS: [_P.READ, _P.CREATE, _P.UPDATE, _P.DELETE],
|
||||
Scope.RGW: [_P.READ, _P.CREATE, _P.UPDATE, _P.DELETE],
|
||||
Scope.GRAFANA: [_P.READ],
|
||||
|
||||
@ -156,7 +156,8 @@ class AccessControlTest(unittest.TestCase, CLICommandTestMixin):
|
||||
self.assertEqual(role['name'], 'read-only')
|
||||
self.assertEqual(
|
||||
role['description'],
|
||||
'allows read permission for all security scope except user, dashboard settings and config-opt'
|
||||
'allows read permission for all security scope except user, '
|
||||
'dashboard settings and config-opt'
|
||||
)
|
||||
|
||||
def test_cluster_manager_role_has_pool_read(self):
|
||||
@ -167,6 +168,18 @@ class AccessControlTest(unittest.TestCase, CLICommandTestMixin):
|
||||
role = self.exec_cmd('ac-role-show', rolename='smb-manager')
|
||||
self.assertEqual(role['scopes_permissions'][Scope.HOSTS], [Permission.READ])
|
||||
|
||||
def test_smb_manager_role_has_pool_read(self):
|
||||
role = self.exec_cmd('ac-role-show', rolename='smb-manager')
|
||||
self.assertEqual(role['scopes_permissions'][Scope.POOL], [Permission.READ])
|
||||
|
||||
def test_block_manager_role_has_pool_read(self):
|
||||
role = self.exec_cmd('ac-role-show', rolename='block-manager')
|
||||
self.assertEqual(role['scopes_permissions'][Scope.POOL], [Permission.READ])
|
||||
|
||||
def test_block_manager_role_has_hosts_read(self):
|
||||
role = self.exec_cmd('ac-role-show', rolename='block-manager')
|
||||
self.assertEqual(role['scopes_permissions'][Scope.HOSTS], [Permission.READ])
|
||||
|
||||
def test_delete_system_role(self):
|
||||
with self.assertRaises(CmdException) as ctx:
|
||||
self.exec_cmd('ac-role-delete', rolename='administrator')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user