From: Volker Theile Date: Tue, 20 Nov 2018 08:07:28 +0000 (+0100) Subject: mgr/dashboard: Can't handle user editing when tenants are specified X-Git-Tag: v14.1.0~828^2 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=945e790cab57785e1bc2dbf708d5ddafec618aba;p=ceph-ci.git mgr/dashboard: Can't handle user editing when tenants are specified Fixes: https://tracker.ceph.com/issues/36480 Signed-off-by: Volker Theile --- diff --git a/qa/tasks/mgr/dashboard/test_rgw.py b/qa/tasks/mgr/dashboard/test_rgw.py index 846eff4122e..eb8cff6cb23 100644 --- a/qa/tasks/mgr/dashboard/test_rgw.py +++ b/qa/tasks/mgr/dashboard/test_rgw.py @@ -1,8 +1,11 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import + import logging +import urllib +import six -from .helper import DashboardTestCase +from .helper import DashboardTestCase, JObj, JList, JLeaf logger = logging.getLogger(__name__) @@ -124,6 +127,16 @@ class RgwBucketTest(RgwTestCase): def setUpClass(cls): cls.create_test_user = True super(RgwBucketTest, cls).setUpClass() + # Create a tenanted user. + cls._radosgw_admin_cmd([ + 'user', 'create', '--tenant', 'testx', '--uid', 'teuth-test-user', + '--display-name', 'tenanted teuth-test-user' + ]) + + @classmethod + def tearDownClass(cls): + cls._radosgw_admin_cmd(['user', 'rm', '--tenant', 'testx', '--uid=teuth-test-user']) + super(RgwBucketTest, cls).tearDownClass() def test_all(self): # Create a new bucket. @@ -135,14 +148,20 @@ class RgwBucketTest(RgwTestCase): }) self.assertStatus(201) data = self.jsonBody() - self.assertIn('bucket_info', data) - data = data['bucket_info'] - self.assertIn('bucket', data) - self.assertIn('quota', data) - self.assertIn('creation_time', data) - self.assertIn('name', data['bucket']) - self.assertIn('bucket_id', data['bucket']) - self.assertEqual(data['bucket']['name'], 'teuth-test-bucket') + self.assertSchema(data, JObj(sub_elems={ + 'bucket_info': JObj(sub_elems={ + 'bucket': JObj(allow_unknown=True, sub_elems={ + 'name': JLeaf(str), + 'bucket_id': JLeaf(str), + 'tenant': JLeaf(str) + }), + 'quota': JObj(sub_elems={}, allow_unknown=True), + 'creation_time': JLeaf(str) + }, allow_unknown=True) + }, allow_unknown=True)) + data = data['bucket_info']['bucket'] + self.assertEqual(data['name'], 'teuth-test-bucket') + self.assertEqual(data['tenant'], '') # List all buckets. data = self._get('/api/rgw/bucket') @@ -153,10 +172,14 @@ class RgwBucketTest(RgwTestCase): # Get the bucket. data = self._get('/api/rgw/bucket/teuth-test-bucket') self.assertStatus(200) - self.assertIn('id', data) - self.assertIn('bucket', data) - self.assertIn('bucket_quota', data) - self.assertIn('owner', data) + self.assertSchema(data, JObj(sub_elems={ + 'id': JLeaf(str), + 'bid': JLeaf(str), + 'tenant': JLeaf(str), + 'bucket': JLeaf(str), + 'bucket_quota': JObj(sub_elems={}, allow_unknown=True), + 'owner': JLeaf(str) + }, allow_unknown=True)) self.assertEqual(data['bucket'], 'teuth-test-bucket') self.assertEqual(data['owner'], 'admin') @@ -170,6 +193,11 @@ class RgwBucketTest(RgwTestCase): self.assertStatus(200) data = self._get('/api/rgw/bucket/teuth-test-bucket') self.assertStatus(200) + self.assertSchema(data, JObj(sub_elems={ + 'owner': JLeaf(str), + 'bid': JLeaf(str), + 'tenant': JLeaf(str) + }, allow_unknown=True)) self.assertEqual(data['owner'], 'teuth-test-user') # Delete the bucket. @@ -179,6 +207,65 @@ class RgwBucketTest(RgwTestCase): self.assertStatus(200) self.assertEqual(len(data), 0) + def test_create_get_update_delete_w_tenant(self): + # Create a new bucket. The tenant of the user is used when + # the bucket is created. + self._post( + '/api/rgw/bucket', + params={ + 'bucket': 'teuth-test-bucket', + 'uid': 'testx$teuth-test-user' + }) + self.assertStatus(201) + # It's not possible to validate the result because there + # IS NO result object returned by the RGW Admin OPS API + # when a tenanted bucket is created. + data = self.jsonBody() + self.assertIsNone(data) + + # List all buckets. + data = self._get('/api/rgw/bucket') + self.assertStatus(200) + self.assertEqual(len(data), 1) + self.assertIn('testx/teuth-test-bucket', data) + + # Get the bucket. + data = self._get('/api/rgw/bucket/{}'.format(urllib.quote_plus( + 'testx/teuth-test-bucket'))) + self.assertStatus(200) + self.assertSchema(data, JObj(sub_elems={ + 'owner': JLeaf(str), + 'bucket': JLeaf(str), + 'tenant': JLeaf(str), + 'bid': JLeaf(str) + }, allow_unknown=True)) + self.assertEqual(data['owner'], 'testx$teuth-test-user') + self.assertEqual(data['bucket'], 'teuth-test-bucket') + self.assertEqual(data['tenant'], 'testx') + self.assertEqual(data['bid'], 'testx/teuth-test-bucket') + + # Update the bucket. + self._put( + '/api/rgw/bucket/{}'.format(urllib.quote_plus('testx/teuth-test-bucket')), + params={ + 'bucket_id': data['id'], + 'uid': 'admin' + }) + self.assertStatus(200) + data = self._get('/api/rgw/bucket/{}'.format(urllib.quote_plus( + 'testx/teuth-test-bucket'))) + self.assertStatus(200) + self.assertIn('owner', data) + self.assertEqual(data['owner'], 'admin') + + # Delete the bucket. + self._delete('/api/rgw/bucket/{}'.format(urllib.quote_plus( + 'testx/teuth-test-bucket'))) + self.assertStatus(204) + data = self._get('/api/rgw/bucket') + self.assertStatus(200) + self.assertEqual(len(data), 0) + class RgwDaemonTest(DashboardTestCase): @@ -237,17 +324,20 @@ class RgwUserTest(RgwTestCase): super(RgwUserTest, cls).setUpClass() def _assert_user_data(self, data): - self.assertIn('caps', data) - self.assertIn('display_name', data) - self.assertIn('email', data) - self.assertIn('keys', data) + self.assertSchema(data, JObj(sub_elems={ + 'caps': JList(JObj(sub_elems={}, allow_unknown=True)), + 'display_name': JLeaf(str), + 'email': JLeaf(str), + 'keys': JList(JObj(sub_elems={}, allow_unknown=True)), + 'max_buckets': JLeaf(int), + 'subusers': JList(JLeaf(str)), + 'suspended': JLeaf(int), + 'swift_keys': JList(JObj(sub_elems={}, allow_unknown=True)), + 'tenant': JLeaf(str), + 'user_id': JLeaf(str), + 'uid': JLeaf(str) + }, allow_unknown=True)) self.assertGreaterEqual(len(data['keys']), 1) - self.assertIn('max_buckets', data) - self.assertIn('subusers', data) - self.assertIn('suspended', data) - self.assertIn('swift_keys', data) - self.assertIn('tenant', data) - self.assertIn('user_id', data) def test_get(self): data = self.get_rgw_user('admin') @@ -261,7 +351,7 @@ class RgwUserTest(RgwTestCase): self.assertGreaterEqual(len(data), 1) self.assertIn('admin', data) - def test_create_update_delete(self): + def test_create_get_update_delete(self): # Create a new user. self._post('/api/rgw/user', params={ 'uid': 'teuth-test-user', @@ -277,7 +367,9 @@ class RgwUserTest(RgwTestCase): data = self.get_rgw_user('teuth-test-user') self.assertStatus(200) self._assert_user_data(data) + self.assertEqual(data['tenant'], '') self.assertEqual(data['user_id'], 'teuth-test-user') + self.assertEqual(data['uid'], 'teuth-test-user') # Update the user. self._put( @@ -302,6 +394,49 @@ class RgwUserTest(RgwTestCase): self.assertIn('"HostId"', resp['detail']) self.assertIn('"RequestId"', resp['detail']) + def test_create_get_update_delete_w_tenant(self): + # Create a new user. + self._post('/api/rgw/user', params={ + 'uid': 'test01$teuth-test-user', + 'display_name': 'display name' + }) + self.assertStatus(201) + data = self.jsonBody() + self._assert_user_data(data) + self.assertEqual(data['user_id'], 'teuth-test-user') + self.assertEqual(data['display_name'], 'display name') + + # Get the user. + data = self.get_rgw_user('test01$teuth-test-user') + self.assertStatus(200) + self._assert_user_data(data) + self.assertEqual(data['tenant'], 'test01') + self.assertEqual(data['user_id'], 'teuth-test-user') + self.assertEqual(data['uid'], 'test01$teuth-test-user') + + # Update the user. + self._put( + '/api/rgw/user/test01$teuth-test-user', + params={ + 'display_name': 'new name' + }) + self.assertStatus(200) + data = self.jsonBody() + self._assert_user_data(data) + self.assertEqual(data['display_name'], 'new name') + + # Delete the user. + self._delete('/api/rgw/user/test01$teuth-test-user') + self.assertStatus(204) + self.get_rgw_user('test01$teuth-test-user') + self.assertStatus(500) + resp = self.jsonBody() + self.assertIn('detail', resp) + self.assertIn('failed request with status code 404', resp['detail']) + self.assertIn('"Code":"NoSuchUser"', resp['detail']) + self.assertIn('"HostId"', resp['detail']) + self.assertIn('"RequestId"', resp['detail']) + class RgwUserCapabilityTest(RgwTestCase): diff --git a/src/pybind/mgr/dashboard/controllers/rgw.py b/src/pybind/mgr/dashboard/controllers/rgw.py index 4576b97d81d..e533287df27 100644 --- a/src/pybind/mgr/dashboard/controllers/rgw.py +++ b/src/pybind/mgr/dashboard/controllers/rgw.py @@ -105,11 +105,27 @@ class RgwRESTController(RESTController): @ApiController('/rgw/bucket', Scope.RGW) class RgwBucket(RgwRESTController): + def _append_bid(self, bucket): + """ + Append the bucket identifier that looks like [/]. + See http://docs.ceph.com/docs/nautilus/radosgw/multitenancy/ for + more information. + :param bucket: The bucket parameters. + :type bucket: dict + :return: The modified bucket parameters including the 'bid' parameter. + :rtype: dict + """ + if isinstance(bucket, dict): + bucket['bid'] = '{}/{}'.format(bucket['tenant'], bucket['bucket']) \ + if bucket['tenant'] else bucket['bucket'] + return bucket + def list(self): return self.proxy('GET', 'bucket') def get(self, bucket): - return self.proxy('GET', 'bucket', {'bucket': bucket}) + result = self.proxy('GET', 'bucket', {'bucket': bucket}) + return self._append_bid(result) def create(self, bucket, uid): try: @@ -119,11 +135,12 @@ class RgwBucket(RgwRESTController): raise DashboardException(e, http_status_code=500, component='rgw') def set(self, bucket, bucket_id, uid): - return self.proxy('PUT', 'bucket', { + result = self.proxy('PUT', 'bucket', { 'bucket': bucket, 'bucket-id': bucket_id, 'uid': uid }, json_response=False) + return self._append_bid(result) def delete(self, bucket, purge_objects='true'): return self.proxy('DELETE', 'bucket', { @@ -135,11 +152,27 @@ class RgwBucket(RgwRESTController): @ApiController('/rgw/user', Scope.RGW) class RgwUser(RgwRESTController): + def _append_uid(self, user): + """ + Append the user identifier that looks like [$]. + See http://docs.ceph.com/docs/jewel/radosgw/multitenancy/ for + more information. + :param user: The user parameters. + :type user: dict + :return: The modified user parameters including the 'uid' parameter. + :rtype: dict + """ + if isinstance(user, dict): + user['uid'] = '{}${}'.format(user['tenant'], user['user_id']) \ + if user['tenant'] else user['user_id'] + return user + def list(self): return self.proxy('GET', 'metadata/user') def get(self, uid): - return self.proxy('GET', 'user', {'uid': uid}) + result = self.proxy('GET', 'user', {'uid': uid}) + return self._append_uid(result) def create(self, uid, display_name, email=None, max_buckets=None, suspended=None, generate_key=None, access_key=None, @@ -159,7 +192,8 @@ class RgwUser(RgwRESTController): params['access-key'] = access_key if secret_key is not None: params['secret-key'] = secret_key - return self.proxy('PUT', 'user', params) + result = self.proxy('PUT', 'user', params) + return self._append_uid(result) def set(self, uid, display_name=None, email=None, max_buckets=None, suspended=None): @@ -172,7 +206,8 @@ class RgwUser(RgwRESTController): params['max-buckets'] = max_buckets if suspended is not None: params['suspended'] = suspended - return self.proxy('POST', 'user', params) + result = self.proxy('POST', 'user', params) + return self._append_uid(result) def delete(self, uid): try: diff --git a/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts index 5164c4b76ac..a77015735e7 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts @@ -205,7 +205,7 @@ const routes: Routes = [ children: [ { path: '', component: RgwBucketListComponent }, { path: 'add', component: RgwBucketFormComponent, data: { breadcrumbs: 'Add' } }, - { path: 'edit/:bucket', component: RgwBucketFormComponent, data: { breadcrumbs: 'Edit' } } + { path: 'edit/:bid', component: RgwBucketFormComponent, data: { breadcrumbs: 'Edit' } } ] } ] diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-details/rgw-bucket-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-details/rgw-bucket-details.component.html index 306d6de9d02..3ccb9cb684f 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-details/rgw-bucket-details.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-details/rgw-bucket-details.component.html @@ -6,7 +6,7 @@ Name - {{ bucket.bucket }} + {{ bucket.bid }}
+ [ngClass]="{'has-error': bucketForm.showError('bid', frm)}">
- This field is required. The value is not valid. The chosen name is already in use.
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.ts index e8b812af4e8..893a68e5712 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.ts @@ -34,7 +34,7 @@ export class RgwBucketFormComponent implements OnInit { createForm() { this.bucketForm = this.formBuilder.group({ id: [null], - bucket: [null, [Validators.required], [this.bucketNameValidator()]], + bid: [null, [Validators.required], [this.bucketNameValidator()]], owner: [null, [Validators.required]] }); } @@ -47,15 +47,15 @@ export class RgwBucketFormComponent implements OnInit { // Process route parameters. this.route.params.subscribe( - (params: { bucket: string }) => { - if (!params.hasOwnProperty('bucket')) { + (params: { bid: string }) => { + if (!params.hasOwnProperty('bid')) { return; } - params.bucket = decodeURIComponent(params.bucket); + const bid = decodeURIComponent(params.bid); this.loading = true; // Load the bucket data in 'edit' mode. this.editing = true; - this.rgwBucketService.get(params.bucket).subscribe((resp: object) => { + this.rgwBucketService.get(bid).subscribe((resp: object) => { this.loading = false; // Get the default values. const defaults = _.clone(this.bucketForm.value); @@ -82,12 +82,12 @@ export class RgwBucketFormComponent implements OnInit { if (this.bucketForm.pristine) { this.goToListView(); } - const bucketCtl = this.bucketForm.get('bucket'); + const bidCtl = this.bucketForm.get('bid'); const ownerCtl = this.bucketForm.get('owner'); if (this.editing) { // Edit const idCtl = this.bucketForm.get('id'); - this.rgwBucketService.update(bucketCtl.value, idCtl.value, ownerCtl.value).subscribe( + this.rgwBucketService.update(bidCtl.value, idCtl.value, ownerCtl.value).subscribe( () => { this.goToListView(); }, @@ -98,7 +98,7 @@ export class RgwBucketFormComponent implements OnInit { ); } else { // Add - this.rgwBucketService.create(bucketCtl.value, ownerCtl.value).subscribe( + this.rgwBucketService.create(bidCtl.value, ownerCtl.value).subscribe( () => { this.goToListView(); }, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.ts index 3c4e8ce0547..2a2791f7a92 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.ts @@ -39,7 +39,7 @@ export class RgwBucketListComponent { this.columns = [ { name: this.i18n('Name'), - prop: 'bucket', + prop: 'bid', flexGrow: 1 }, { @@ -49,7 +49,7 @@ export class RgwBucketListComponent { } ]; const getBucketUri = () => - this.selection.first() && `${encodeURI(this.selection.first().bucket)}`; + this.selection.first() && `${encodeURIComponent(this.selection.first().bid)}`; const addAction: CdTableAction = { permission: 'create', icon: 'fa-plus', @@ -97,7 +97,7 @@ export class RgwBucketListComponent { // Delete all selected data table rows. observableForkJoin( this.selection.selected.map((bucket: any) => { - return this.rgwBucketService.delete(bucket.bucket); + return this.rgwBucketService.delete(bucket.bid); }) ).subscribe( null, 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 4a8ae5cae9b..ab5f22f7387 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 @@ -7,7 +7,7 @@ Username - {{ user.user_id }} + {{ user.uid }} Suspended {{ user.suspended ? "Yes" : "No" }} + + System + {{ user.system ? "Yes" : "No" }} + Maximum buckets 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 108efc63b9f..daa2ff74eaa 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 @@ -64,11 +64,9 @@ export class RgwUserDetailsComponent implements OnChanges, OnInit { this.user.caps = _.sortBy(this.user.caps, 'type'); // Load the user/bucket quota of the selected user. - if (this.user.tenant === '') { - this.rgwUserService.getQuota(this.user.user_id).subscribe((resp: object) => { - _.extend(this.user, resp); - }); - } + this.rgwUserService.getQuota(this.user.uid).subscribe((resp: object) => { + _.extend(this.user, resp); + }); // Process the keys. this.keys = []; diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.html index 33f6726169c..88466bbb1f9 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.html @@ -18,26 +18,26 @@
+ [ngClass]="{'has-error': userForm.showError('uid', frm)}">
- This field is required. The chosen user ID is already in use.
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.spec.ts index 498c0fa0e58..6e183172da8 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.spec.ts @@ -119,19 +119,19 @@ describe('RgwUserFormComponent', () => { }); it('should validate that username is required', () => { - formHelper.expectErrorChange('user_id', '', 'required', true); + formHelper.expectErrorChange('uid', '', 'required', true); }); it('should validate that username is valid', fakeAsync(() => { - formHelper.setValue('user_id', 'ab', true); + formHelper.setValue('uid', 'ab', true); tick(500); - formHelper.expectValid('user_id'); + formHelper.expectValid('uid'); })); it('should validate that username is invalid', fakeAsync(() => { - formHelper.setValue('user_id', 'abc', true); + formHelper.setValue('uid', 'abc', true); tick(500); - formHelper.expectError('user_id', 'notUnique'); + formHelper.expectError('uid', 'notUnique'); })); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.ts index 7daf07ebf1b..db8ebd178d8 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.ts @@ -51,7 +51,7 @@ export class RgwUserFormComponent implements OnInit { createForm() { this.userForm = this.formBuilder.group({ // General - user_id: [ + uid: [ null, [Validators.required], [CdValidators.unique(this.rgwUserService.exists, this.rgwUserService)] @@ -154,13 +154,14 @@ export class RgwUserFormComponent implements OnInit { if (!params.hasOwnProperty('uid')) { return; } + const uid = decodeURIComponent(params.uid); this.loading = true; // Load the user data in 'edit' mode. this.editing = true; // Load the user and quota information. const observables = []; - observables.push(this.rgwUserService.get(params.uid)); - observables.push(this.rgwUserService.getQuota(params.uid)); + observables.push(this.rgwUserService.get(uid)); + observables.push(this.rgwUserService.getQuota(uid)); observableForkJoin(observables).subscribe( (resp: any[]) => { this.loading = false; @@ -224,7 +225,7 @@ export class RgwUserFormComponent implements OnInit { if (this.userForm.pristine) { this.goToListView(); } - const uid = this.userForm.getValue('user_id'); + const uid = this.userForm.getValue('uid'); if (this.editing) { // Edit if (this._isGeneralDirty()) { @@ -284,7 +285,7 @@ export class RgwUserFormComponent implements OnInit { 'full-control': 'full', 'read-write': 'readwrite' }; - const uid = this.userForm.getValue('user_id'); + const uid = this.userForm.getValue('uid'); const args = { subuser: subuser.id, access: @@ -323,7 +324,7 @@ export class RgwUserFormComponent implements OnInit { const subuser = this.subusers[index]; // Create an observable to delete the subuser when the form is submitted. this.submitObservables.push( - this.rgwUserService.deleteSubuser(this.userForm.getValue('user_id'), subuser.id) + this.rgwUserService.deleteSubuser(this.userForm.getValue('uid'), subuser.id) ); // Remove the associated S3 keys. this.s3Keys = this.s3Keys.filter((key) => { @@ -343,7 +344,7 @@ export class RgwUserFormComponent implements OnInit { * Add/Update a capability. */ setCapability(cap: RgwUserCapability, index?: number) { - const uid = this.userForm.getValue('user_id'); + const uid = this.userForm.getValue('uid'); if (_.isNumber(index)) { // Modify const oldCap = this.capabilities[index]; @@ -375,7 +376,7 @@ export class RgwUserFormComponent implements OnInit { const cap = this.capabilities[index]; // Create an observable to delete the capability when the form is submitted. this.submitObservables.push( - this.rgwUserService.deleteCapability(this.userForm.getValue('user_id'), cap.type, cap.perm) + this.rgwUserService.deleteCapability(this.userForm.getValue('uid'), cap.type, cap.perm) ); // Remove the capability to update the UI. this.capabilities.splice(index, 1); @@ -423,7 +424,7 @@ export class RgwUserFormComponent implements OnInit { const key = this.s3Keys[index]; // Create an observable to delete the S3 key when the form is submitted. this.submitObservables.push( - this.rgwUserService.deleteS3Key(this.userForm.getValue('user_id'), key.access_key) + this.rgwUserService.deleteS3Key(this.userForm.getValue('uid'), key.access_key) ); // Remove the S3 key to update the UI. this.s3Keys.splice(index, 1); @@ -436,7 +437,7 @@ export class RgwUserFormComponent implements OnInit { * @param {number | undefined} index The subuser to show. */ showSubuserModal(index?: number) { - const uid = this.userForm.getValue('user_id'); + const uid = this.userForm.getValue('uid'); const modalRef = this.bsModalService.show(RgwUserSubuserModalComponent); if (_.isNumber(index)) { // Edit @@ -555,7 +556,7 @@ export class RgwUserFormComponent implements OnInit { */ private _getCreateArgs() { const result = { - uid: this.userForm.getValue('user_id'), + uid: this.userForm.getValue('uid'), display_name: this.userForm.getValue('display_name'), suspended: this.userForm.getValue('suspended'), email: '', @@ -645,9 +646,9 @@ export class RgwUserFormComponent implements OnInit { private _getS3KeyUserCandidates() { let result = []; // Add the current user id. - const user_id = this.userForm.getValue('user_id'); - if (_.isString(user_id) && !_.isEmpty(user_id)) { - result.push(user_id); + const uid = this.userForm.getValue('uid'); + if (_.isString(uid) && !_.isEmpty(uid)) { + result.push(uid); } // Append the subusers. this.subusers.forEach((subUser) => { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-list/rgw-user-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-list/rgw-user-list.component.ts index 2dacb8390c5..122c7564f8b 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-list/rgw-user-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-list/rgw-user-list.component.ts @@ -40,7 +40,7 @@ export class RgwUserListComponent { this.columns = [ { name: this.i18n('Username'), - prop: 'user_id', + prop: 'uid', flexGrow: 1 }, { @@ -65,7 +65,8 @@ export class RgwUserListComponent { flexGrow: 1 } ]; - const getUserUri = () => this.selection.first() && this.selection.first().user_id; + const getUserUri = () => + this.selection.first() && `${encodeURIComponent(this.selection.first().uid)}`; const addAction: CdTableAction = { permission: 'create', icon: 'fa-plus', @@ -111,7 +112,7 @@ export class RgwUserListComponent { // Delete all selected data table rows. observableForkJoin( this.selection.selected.map((user: any) => { - return this.rgwUserService.delete(user.user_id); + return this.rgwUserService.delete(user.uid); }) ).subscribe( null,