]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: do not show API keys if user has only read-only privileges. 33665/head
authorAlfonso Martínez <almartin@redhat.com>
Mon, 9 Mar 2020 12:05:41 +0000 (13:05 +0100)
committerAlfonso Martínez <almartin@redhat.com>
Fri, 20 Mar 2020 07:43:22 +0000 (08:43 +0100)
Fixes: https://tracker.ceph.com/issues/42475
Signed-off-by: Alfonso Martínez <almartin@redhat.com>
(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.

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 bd7a12b7be2da850bdd91352ae7f6bcb4e33653f..2a53f368352c66d69851abf20bb0522b805910f0 100644 (file)
@@ -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()
index 4a90eb45a2587970f82803a3e46d0e960c5e097e..6d51e2ec29bb4471af53df2c9d45b6c8c6f4256d 100644 (file)
@@ -14,7 +14,7 @@
                 class="bold col-sm-1">Full name</td>
             <td class="col-sm-3">{{ user.display_name }}</td>
           </tr>
-          <tr *ngIf="user.email.length">
+          <tr *ngIf="user.email?.length">
             <td i18n
                 class="bold col-sm-1">Email address</td>
             <td class="col-sm-3">{{ user.email }}</td>
     </div>
   </tab>
 
-  <tab i18n-heading
+  <tab *ngIf="keys.length"
+       i18n-heading
        heading="Keys">
     <cd-table [data]="keys"
               [columns]="keysColumns"
index d5afbc5285a03707c73c44a2877342dbafbe5aa3..2dc8b286a6b8143270fcb7049bb4cd326a265bcf 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,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();
   });
 });
index daa2ff74eaaeae515782c04266854546ba4422e5..a55dd13d65829deacadbbb5c914fd820faf577a8 100644 (file)
@@ -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');
     }
   }
index 38c3d0af87b0dc0c913fa0dda91bb83e6c0be80d..b55d2b9b187afd89f20a3be7e3c40862be1f6127 100644 (file)
@@ -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())