From: Alfonso Martínez Date: Mon, 9 Mar 2020 12:05:41 +0000 (+0100) Subject: mgr/dashboard: do not show API keys if user has only read-only privileges. X-Git-Tag: v14.2.10~164^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=a154a1a0091aa6525932ff459f3f79f7a15529f7;p=ceph.git mgr/dashboard: do not show API keys if user has only read-only privileges. Fixes: https://tracker.ceph.com/issues/42475 Signed-off-by: Alfonso Martínez (cherry picked from commit 38d419620508af75f7c164b56d07d1ecde9173b4) Conflicts: - src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.spec.ts - src/pybind/mgr/dashboard/tests/test_rgw.py Adapted (after cherry-pick) frontend & backend unit tests to nautilus code. --- diff --git a/src/pybind/mgr/dashboard/controllers/rgw.py b/src/pybind/mgr/dashboard/controllers/rgw.py index bd7a12b7be2..2a53f368352 100644 --- a/src/pybind/mgr/dashboard/controllers/rgw.py +++ b/src/pybind/mgr/dashboard/controllers/rgw.py @@ -10,7 +10,8 @@ from . import ApiController, BaseController, RESTController, Endpoint, \ from .. import logger from ..exceptions import DashboardException from ..rest_client import RequestException -from ..security import Scope +from ..security import Scope, Permission +from ..services.auth import AuthManager, JwtManager from ..services.ceph_service import CephService from ..services.rgw_client import RgwClient @@ -188,6 +189,13 @@ class RgwUser(RgwRESTController): if user['tenant'] else user['user_id'] return user + @staticmethod + def _keys_allowed(): + permissions = AuthManager.get_user(JwtManager.get_username()).permissions_dict() + edit_permissions = [Permission.CREATE, Permission.UPDATE, Permission.DELETE] + return Scope.RGW in permissions and Permission.READ in permissions[Scope.RGW] \ + and len(set(edit_permissions).intersection(set(permissions[Scope.RGW]))) > 0 + def list(self): users = [] marker = None @@ -208,6 +216,9 @@ class RgwUser(RgwRESTController): def get(self, uid): result = self.proxy('GET', 'user', {'uid': uid}) + if not self._keys_allowed(): + del result['keys'] + del result['swift_keys'] return self._append_uid(result) @Endpoint() diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.html index 4a90eb45a25..6d51e2ec29b 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.html @@ -14,7 +14,7 @@ class="bold col-sm-1">Full name {{ user.display_name }} - + Email address {{ user.email }} @@ -130,7 +130,8 @@ - { @@ -28,5 +29,35 @@ describe('RgwUserDetailsComponent', () => { it('should create', () => { expect(component).toBeTruthy(); + + const detailsTab = fixture.debugElement.nativeElement.querySelector('tab[heading="Details"]'); + expect(detailsTab).toBeFalsy(); + const keysTab = fixture.debugElement.nativeElement.querySelector('tab[heading="Keys"]'); + expect(keysTab).toBeFalsy(); + }); + + it('should show "Details" tab', () => { + component.selection.selected = [{ uid: 'myUsername' }]; + component.selection.hasSingleSelection = true; + fixture.detectChanges(); + + const detailsTab = fixture.debugElement.nativeElement.querySelector('tab[heading="Details"]'); + expect(detailsTab).toBeTruthy(); + const keysTab = fixture.debugElement.nativeElement.querySelector('tab[heading="Keys"]'); + expect(keysTab).toBeFalsy(); + }); + + it('should show "Keys" tab', () => { + const s3Key = new RgwUserS3Key(); + component.selection.selected = [{ keys: [s3Key] }]; + component.selection.hasSingleSelection = true; + component.selection.hasSelection = true; + component.ngOnChanges(); + fixture.detectChanges(); + + const detailsTab = fixture.debugElement.nativeElement.querySelector('tab[heading="Details"]'); + expect(detailsTab).toBeTruthy(); + const keysTab = fixture.debugElement.nativeElement.querySelector('tab[heading="Keys"]'); + expect(keysTab).toBeTruthy(); }); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.ts index daa2ff74eaa..a55dd13d658 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.ts @@ -70,22 +70,27 @@ export class RgwUserDetailsComponent implements OnChanges, OnInit { // Process the keys. this.keys = []; - this.user.keys.forEach((key: RgwUserS3Key) => { - this.keys.push({ - id: this.keys.length + 1, // Create an unique identifier - type: 'S3', - username: key.user, - ref: key + if (this.user.keys) { + this.user.keys.forEach((key: RgwUserS3Key) => { + this.keys.push({ + id: this.keys.length + 1, // Create an unique identifier + type: 'S3', + username: key.user, + ref: key + }); }); - }); - this.user.swift_keys.forEach((key: RgwUserSwiftKey) => { - this.keys.push({ - id: this.keys.length + 1, // Create an unique identifier - type: 'Swift', - username: key.user, - ref: key + } + if (this.user.swift_keys) { + this.user.swift_keys.forEach((key: RgwUserSwiftKey) => { + this.keys.push({ + id: this.keys.length + 1, // Create an unique identifier + type: 'Swift', + username: key.user, + ref: key + }); }); - }); + } + this.keys = _.sortBy(this.keys, 'user'); } } diff --git a/src/pybind/mgr/dashboard/tests/test_rgw.py b/src/pybind/mgr/dashboard/tests/test_rgw.py index 38c3d0af87b..b55d2b9b187 100644 --- a/src/pybind/mgr/dashboard/tests/test_rgw.py +++ b/src/pybind/mgr/dashboard/tests/test_rgw.py @@ -83,3 +83,33 @@ class RgwUserControllerTestCase(ControllerTestCase): }] self._get('/test/api/rgw/user') self.assertStatus(500) + + @mock.patch('dashboard.controllers.rgw.RgwRESTController.proxy') + @mock.patch.object(RgwUser, '_keys_allowed') + def test_user_get_with_keys(self, keys_allowed, mock_proxy): + keys_allowed.return_value = True + mock_proxy.return_value = { + 'tenant': '', + 'user_id': 'my_user_id', + 'keys': [], + 'swift_keys': [] + } + self._get('/test/api/rgw/user/testuser') + self.assertStatus(200) + self.assertInJsonBody('keys') + self.assertInJsonBody('swift_keys') + + @mock.patch('dashboard.controllers.rgw.RgwRESTController.proxy') + @mock.patch.object(RgwUser, '_keys_allowed') + def test_user_get_without_keys(self, keys_allowed, mock_proxy): + keys_allowed.return_value = False + mock_proxy.return_value = { + 'tenant': '', + 'user_id': 'my_user_id', + 'keys': [], + 'swift_keys': [] + } + self._get('/test/api/rgw/user/testuser') + self.assertStatus(200) + self.assertNotIn('keys', self.jsonBody()) + self.assertNotIn('swift_keys', self.jsonBody())