]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: do not show API keys if user has only read-only privileges. 33178/head
authorAlfonso Martínez <almartin@redhat.com>
Wed, 26 Feb 2020 13:43:38 +0000 (14:43 +0100)
committerAlfonso Martínez <almartin@redhat.com>
Wed, 26 Feb 2020 13:43:52 +0000 (14:43 +0100)
Fixes: https://tracker.ceph.com/issues/42475
Signed-off-by: Alfonso Martínez <almartin@redhat.com>
src/pybind/mgr/dashboard/controllers/rgw.py
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.ts
src/pybind/mgr/dashboard/tests/test_rgw.py

index 4e3f8f2ac9099501340c652f58a7f5cbd08afd06..b96407c31cf5b2d8394e0ad56233457a5468df70 100644 (file)
@@ -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()
index 54fff66f5a796f759a4d0c453506a55fecd2b0b1..5ae559c493ff4a185f6fdb12fbed1f8f0df2fa69 100644 (file)
@@ -14,7 +14,7 @@
                 class="bold">Full name</td>
             <td>{{ user.display_name }}</td>
           </tr>
-          <tr *ngIf="user.email.length">
+          <tr *ngIf="user.email?.length">
             <td i18n
                 class="bold">Email address</td>
             <td>{{ user.email }}</td>
     </div>
   </tab>
 
-  <tab i18n-heading
+  <tab *ngIf="keys.length"
+       i18n-heading
        heading="Keys">
     <cd-table [data]="keys"
               [columns]="keysColumns"
index 7f2cb633f0f9dfb174169eab5e526d4a16a9909b..370e717db7fdd7ea2a045311be2c0cd325e2cc72 100644 (file)
@@ -7,6 +7,7 @@ import { TabsModule } from 'ngx-bootstrap/tabs';
 import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
 import { CdTableSelection } from '../../../shared/models/cd-table-selection';
 import { SharedModule } from '../../../shared/shared.module';
+import { RgwUserS3Key } from '../models/rgw-user-s3-key';
 import { RgwUserDetailsComponent } from './rgw-user-details.component';
 
 describe('RgwUserDetailsComponent', () => {
@@ -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', () => {
index 1379e462e399ef77b69c06ca0b5034542c16c3ad..cb6d0969f0bc00d80be66cc20c1b93e9fe67dd42 100644 (file)
@@ -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');
     }
   }
index 9ce5161a3fe7648c30351fe99fc011d3901c3a7d..2c90e7d11be1dea7da026155586627ee7510caa4 100644 (file)
@@ -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())