Merge pull request #24757 from votdev/issue_36480

mgr/dashboard: Can't handle user editing when tenants are specified

Reviewed-by: Ernesto Puerta <epuertat@redhat.com>
Reviewed-by: Tatjana Dehler <tdehler@suse.com>
Reviewed-by: Tiago Melo <tmelo@suse.com>
This commit is contained in:
Lenz Grimmer 2018-11-23 12:36:26 +01:00 committed by GitHub
commit dc8237e94d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 260 additions and 85 deletions

View File

@ -1,8 +1,11 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import logging
from .helper import DashboardTestCase
import logging
import urllib
import six
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):

View File

@ -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 [<tenant>/]<bucket>.
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 [<tenant>$]<user>.
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:

View File

@ -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' } }
]
}
]

View File

@ -6,7 +6,7 @@
<tr>
<td i18n
class="bold col-sm-1">Name</td>
<td class="col-sm-3">{{ bucket.bucket }}</td>
<td class="col-sm-3">{{ bucket.bid }}</td>
</tr>
<tr>
<td i18n

View File

@ -36,31 +36,31 @@
<!-- Name -->
<div class="form-group"
[ngClass]="{'has-error': bucketForm.showError('bucket', frm)}">
[ngClass]="{'has-error': bucketForm.showError('bid', frm)}">
<label class="control-label col-sm-3"
for="bucket">
for="bid">
<ng-container i18n>Name</ng-container>
<span class="required"
*ngIf="!editing"></span>
</label>
<div class="col-sm-9">
<input id="bucket"
name="bucket"
<input id="bid"
name="bid"
class="form-control"
type="text"
i18n-placeholder
placeholder="Name..."
formControlName="bucket"
formControlName="bid"
[readonly]="editing"
autofocus>
<span class="help-block"
*ngIf="bucketForm.showError('bucket', frm, 'required')"
*ngIf="bucketForm.showError('bid', frm, 'required')"
i18n>This field is required.</span>
<span class="help-block"
*ngIf="bucketForm.showError('bucket', frm, 'bucketNameInvalid')"
*ngIf="bucketForm.showError('bid', frm, 'bucketNameInvalid')"
i18n>The value is not valid.</span>
<span class="help-block"
*ngIf="bucketForm.showError('bucket', frm, 'bucketNameExists')"
*ngIf="bucketForm.showError('bid', frm, 'bucketNameExists')"
i18n>The chosen name is already in use.</span>
</div>
</div>

View File

@ -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();
},

View File

@ -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,

View File

@ -7,7 +7,7 @@
<tr>
<td i18n
class="bold col-sm-1">Username</td>
<td class="col-sm-3">{{ user.user_id }}</td>
<td class="col-sm-3">{{ user.uid }}</td>
</tr>
<tr>
<td i18n
@ -24,6 +24,11 @@
class="bold col-sm-1">Suspended</td>
<td class="col-sm-3">{{ user.suspended ? "Yes" : "No" }}</td>
</tr>
<tr>
<td i18n
class="bold col-sm-1">System</td>
<td class="col-sm-3">{{ user.system ? "Yes" : "No" }}</td>
</tr>
<tr>
<td i18n
class="bold col-sm-1">Maximum buckets</td>

View File

@ -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 = [];

View File

@ -18,26 +18,26 @@
<!-- Username -->
<div class="form-group"
[ngClass]="{'has-error': userForm.showError('user_id', frm)}">
[ngClass]="{'has-error': userForm.showError('uid', frm)}">
<label class="control-label col-sm-3"
for="user_id">
for="uid">
<ng-container i18n>Username</ng-container>
<span class="required"
*ngIf="!editing">
</span>
</label>
<div class="col-sm-9">
<input id="user_id"
<input id="uid"
class="form-control"
type="text"
formControlName="user_id"
formControlName="uid"
[readonly]="editing"
autofocus>
<span class="help-block"
*ngIf="userForm.showError('user_id', frm, 'required')"
*ngIf="userForm.showError('uid', frm, 'required')"
i18n>This field is required.</span>
<span class="help-block"
*ngIf="userForm.showError('user_id', frm, 'notUnique')"
*ngIf="userForm.showError('uid', frm, 'notUnique')"
i18n>The chosen user ID is already in use.</span>
</div>
</div>

View File

@ -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');
}));
});

View File

@ -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) => {

View File

@ -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,