From: Alfonso Martínez Date: Wed, 26 Feb 2020 13:43:38 +0000 (+0100) Subject: mgr/dashboard: do not show API keys if user has only read-only privileges. X-Git-Tag: v15.1.1~174^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=38d419620508af75f7c164b56d07d1ecde9173b4;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 --- diff --git a/src/pybind/mgr/dashboard/controllers/rgw.py b/src/pybind/mgr/dashboard/controllers/rgw.py index 4e3f8f2ac909..b96407c31cf5 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, \ ReadPermission 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 from ..tools import json_str_to_object @@ -249,6 +250,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): # type: () -> List[str] users = [] # type: List[str] @@ -271,6 +279,9 @@ class RgwUser(RgwRESTController): def get(self, uid): # type: (str) -> dict 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 54fff66f5a79..5ae559c493ff 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">Full name {{ user.display_name }} - + Email address {{ user.email }} @@ -125,7 +125,8 @@ - { @@ -28,6 +29,33 @@ 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' }]; + 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.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(); }); it('should show correct "System" info', () => { 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 1379e462e399..cb6d0969f0bc 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 @@ -73,22 +73,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 9ce5161a3fe7..2c90e7d11be1 100644 --- a/src/pybind/mgr/dashboard/tests/test_rgw.py +++ b/src/pybind/mgr/dashboard/tests/test_rgw.py @@ -86,3 +86,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.json_body()) + self.assertNotIn('swift_keys', self.json_body())