# editor backup files etc.
\#*\#
+
+.idea
else:
assert False
try:
+ if not cls._resp.ok:
+ # Output response for easier debugging.
+ log.error("Request response: %s", cls._resp.text)
if cls._resp.text and cls._resp.text != "":
return cls._resp.json()
return cls._resp.text
import logging
logger = logging.getLogger(__name__)
-from .helper import DashboardTestCase, authenticate
+from .helper import DashboardTestCase, authenticate, JObj, JList, JLeaf
class RgwControllerTest(DashboardTestCase):
@classmethod
def setUpClass(cls):
super(RgwProxyExceptionsTest, cls).setUpClass()
-
cls._ceph_cmd(['dashboard', 'set-rgw-api-secret-key', ''])
cls._ceph_cmd(['dashboard', 'set-rgw-api-access-key', ''])
class RgwProxyTest(DashboardTestCase):
+
@classmethod
def setUpClass(cls):
super(RgwProxyTest, cls).setUpClass()
self.assertStatus(200)
data = self._get(
- '/api/rgw/proxy/user', params={'uid': 'teuth-test-user'})
+ '/api/rgw/proxy/user',
+ params={
+ 'uid': 'teuth-test-user'
+ })
self.assertStatus(200)
self.assertEqual(data['user_id'], 'teuth-test-user')
def _test_get(self):
data = self._get(
- '/api/rgw/proxy/user', params={'uid': 'teuth-test-user'})
+ '/api/rgw/proxy/user',
+ params={
+ 'uid': 'teuth-test-user'
+ })
self._assert_user_data(data)
self.assertStatus(200)
self.assertEqual(self._resp.json()['display_name'], 'new name')
def _test_delete(self):
- self._delete('/api/rgw/proxy/user', params={'uid': 'teuth-test-user'})
- self.assertStatus(200)
-
- self._delete('/api/rgw/proxy/user', params={'uid': 'teuth-test-user'})
+ self._delete('/api/rgw/user/teuth-test-user')
+ self.assertStatus(204)
+ self._delete('/api/rgw/user/teuth-test-user')
self.assertStatus(500)
resp = self._resp.json()
self.assertIn('detail', resp)
self.assertIn('"HostId"', resp['detail'])
self.assertIn('"RequestId"', resp['detail'])
+ @authenticate
+ def test_user_list(self):
+ data = self._get('/api/rgw/proxy/metadata/user')
+ self.assertStatus(200)
+ self.assertGreaterEqual(len(data), 1)
+ self.assertIn('admin', data)
+
@authenticate
def test_rgw_proxy(self):
"""Test basic request types"""
# DELETE - Delete the user
self._test_delete()
+
+
+class RgwTestCase(DashboardTestCase):
+
+ maxDiff = None
+ create_test_user = False
+
+ @classmethod
+ def setUpClass(cls):
+ super(RgwTestCase, cls).setUpClass()
+ # Create the administrator account.
+ cls._radosgw_admin_cmd([
+ 'user', 'create', '--uid=admin', '--display-name=admin',
+ '--system', '--access-key=admin', '--secret=admin'
+ ])
+ # Update the dashboard configuration.
+ cls._ceph_cmd(['dashboard', 'set-rgw-api-secret-key', 'admin'])
+ cls._ceph_cmd(['dashboard', 'set-rgw-api-access-key', 'admin'])
+ # Create a test user?
+ if cls.create_test_user:
+ cls._radosgw_admin_cmd([
+ 'user', 'create', '--uid', 'teuth-test-user', '--display-name',
+ 'teuth-test-user'
+ ])
+ cls._radosgw_admin_cmd([
+ 'caps', 'add', '--uid', 'teuth-test-user', '--caps',
+ 'metadata=write'
+ ])
+ cls._radosgw_admin_cmd([
+ 'subuser', 'create', '--uid', 'teuth-test-user', '--subuser',
+ 'teuth-test-subuser', '--access', 'full', '--key-type', 's3',
+ '--access-key', 'xyz123'
+ ])
+ cls._radosgw_admin_cmd([
+ 'subuser', 'create', '--uid', 'teuth-test-user', '--subuser',
+ 'teuth-test-subuser2', '--access', 'full', '--key-type',
+ 'swift'
+ ])
+
+ @classmethod
+ def tearDownClass(cls):
+ if cls.create_test_user:
+ cls._radosgw_admin_cmd(['user', 'rm', '--uid=teuth-test-user'])
+ super(RgwTestCase, cls).tearDownClass()
+
+
+class RgwBucketTest(RgwTestCase):
+
+ @classmethod
+ 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()
+
+ @authenticate
+ def test_all(self):
+ # Create a new bucket.
+ self._post(
+ '/api/rgw/bucket',
+ params={
+ 'bucket': 'teuth-test-bucket',
+ 'uid': 'admin'
+ })
+ self.assertStatus(201)
+ data = self.jsonBody()
+ 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/proxy/bucket')
+ self.assertStatus(200)
+ self.assertEqual(len(data), 1)
+ self.assertIn('teuth-test-bucket', data)
+
+ # Get the bucket.
+ data = self._get('/api/rgw/bucket/teuth-test-bucket')
+ self.assertStatus(200)
+ 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')
+
+ # Update the bucket.
+ self._put(
+ '/api/rgw/proxy/bucket',
+ params={
+ 'bucket': 'teuth-test-bucket',
+ 'bucket-id': data['id'],
+ 'uid': 'teuth-test-user'
+ })
+ 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.
+ self._delete('/api/rgw/proxy/bucket', params={
+ 'bucket': 'teuth-test-bucket',
+ 'purge-objects': 'true'
+ })
+ self.assertStatus(200)
+ data = self._get('/api/rgw/proxy/bucket')
+ self.assertStatus(200)
+ self.assertEqual(len(data), 0)
+
+ @authenticate
+ 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/proxy/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.
+ # Currently it is not possible to link such a bucket that is
+ # owned by a tenanted user to a 'normal' user.
+ # See http://tracker.ceph.com/issues/39991
+ #self._put(
+ # '/api/rgw/proxy/bucket',
+ # params={
+ # 'bucket': urllib.quote_plus('testx/teuth-test-bucket'),
+ # '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/proxy/bucket', params={
+ 'bucket': urllib.quote_plus('testx/teuth-test-bucket'),
+ 'purge-objects': 'true'
+ })
+ self.assertStatus(200)
+ data = self._get('/api/rgw/proxy/bucket')
+ self.assertStatus(200)
+ self.assertEqual(len(data), 0)
+
+
+class RgwUserTest(RgwTestCase):
+
+ def _assert_user_data(self, 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)
+ }, allow_unknown=True))
+ self.assertGreaterEqual(len(data['keys']), 1)
+
+ @authenticate
+ def test_get(self):
+ data = self._get('/api/rgw/user/admin')
+ self.assertStatus(200)
+ self._assert_user_data(data)
+ self.assertIn('uid', data)
+ self.assertEqual(data['user_id'], 'admin')
+
+ @authenticate
+ def test_create_get_update_delete(self):
+ # Create a new user.
+ self._put(
+ '/api/rgw/proxy/user',
+ params={
+ 'uid': 'teuth-test-user',
+ 'display-name': 'display name'
+ })
+ self.assertStatus(200)
+ 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('/api/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._post(
+ '/api/rgw/proxy/user',
+ params={
+ 'uid': 'teuth-test-user',
+ '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/teuth-test-user')
+ self.assertStatus(204)
+ self._get('/api/rgw/user/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'])
+
+ @authenticate
+ def test_create_get_update_delete_w_tenant(self):
+ # Create a new user.
+ self._put(
+ '/api/rgw/proxy/user',
+ params={
+ 'uid': 'test01$teuth-test-user',
+ 'display-name': 'display name'
+ })
+ self.assertStatus(200)
+ 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('/api/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._post(
+ '/api/rgw/proxy/user',
+ params={
+ 'uid': 'test01$teuth-test-user',
+ '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('/api/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'])
from __future__ import absolute_import
import json
+import sys
import cherrypy
from . import ApiController, BaseController, RESTController, AuthRequired
from ..services.rgw_client import RgwClient
from ..rest_client import RequestException
+if sys.version_info >= (3, 0):
+ from urllib.parse import unquote # pylint: disable=no-name-in-module,import-error
+else:
+ from urllib import unquote # pylint: disable=no-name-in-module
+
@ApiController('rgw')
@AuthRequired()
instance.userid)
raise RequestException(status['message'])
status['available'] = True
- except RequestException:
- pass
+ except (RequestException, LookupError) as ex:
+ status['message'] = str(ex)
return status
if cherrypy.request.body.length:
data = cherrypy.request.body.read()
+ for key, value in params.items():
+ # pylint: disable=undefined-variable
+ if (sys.version_info < (3, 0) and isinstance(value, unicode)) \
+ or isinstance(value, str):
+ params[key] = unquote(value)
+
return rgw_client.proxy(method, path, params, data)
except RequestException as e:
# Always use status code 500 and NOT the status that may delivered
return json.dumps({'detail': str(e)}).encode('utf-8')
+class RgwRESTController(RESTController):
+
+ @staticmethod
+ def proxy(method, path, params=None, json_response=True):
+ instance = RgwClient.admin_instance()
+ result = instance.proxy(method, path, params, None)
+ if json_response:
+ result = result.decode('utf-8')
+ if result != '':
+ result = json.loads(result)
+ else:
+ result = {}
+ return result
+
+
@ApiController('rgw/bucket')
@AuthRequired()
-class RgwBucket(RESTController):
+class RgwBucket(RgwRESTController):
+
+ @staticmethod
+ def _append_bid(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 get(self, bucket):
+ try:
+ result = self.proxy('GET', 'bucket', {'bucket': bucket})
+ return self._append_bid(result)
+ except RequestException as e:
+ cherrypy.response.headers['Content-Type'] = 'application/json'
+ cherrypy.response.status = 500
+ return {'detail': str(e)}
def create(self, bucket, uid):
try:
@ApiController('rgw/user')
@AuthRequired()
-class RgwUser(RESTController):
+class RgwUser(RgwRESTController):
+
+ @staticmethod
+ def _append_uid(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 get(self, uid):
+ try:
+ result = self.proxy('GET', 'user', {'uid': uid})
+ return self._append_uid(result)
+ except RequestException as e:
+ cherrypy.response.headers['Content-Type'] = 'application/json'
+ cherrypy.response.status = 500
+ return {'detail': str(e)}
def delete(self, uid):
try:
rgw_client = RgwClient.admin_instance()
-
# Ensure the user is not configured to access the Object Gateway.
if rgw_client.userid == uid:
raise RequestException('Unable to delete "{}" - this user '
'account is required for managing the '
'Object Gateway'.format(uid))
-
# Finally redirect request to the RGW proxy.
- return rgw_client.proxy('DELETE', 'user', cherrypy.request.params, None)
+ return self.proxy('DELETE', 'user', cherrypy.request.params)
except RequestException as e:
cherrypy.response.headers['Content-Type'] = 'application/json'
cherrypy.response.status = 500
canActivate: [AuthGuardService]
},
{
- path: 'bucket/edit/:bucket',
+ path: 'bucket/edit/:bid',
component: RgwBucketFormComponent,
canActivate: [AuthGuardService]
}
<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
<!-- Name -->
<div class="form-group"
- [ngClass]="{'has-error': (frm.submitted || bucketForm.controls.bucket.dirty) && bucketForm.controls.bucket.invalid}">
+ [ngClass]="{'has-error': (frm.submitted || bucketForm.controls.bid.dirty) && bucketForm.controls.bid.invalid}">
<label i18n
class="control-label col-sm-3"
- for="bucket">Name
+ for="bid">Name
<span class="required"
*ngIf="!editing"></span>
</label>
<div class="col-sm-9">
- <input id="bucket"
- name="bucket"
+ <input id="bid"
class="form-control"
type="text"
i18n-placeholder
placeholder="Name..."
- formControlName="bucket"
+ formControlName="bid"
[readonly]="editing"
autofocus>
<span i18n
class="help-block"
- *ngIf="(frm.submitted || bucketForm.controls.bucket.dirty) && bucketForm.controls.bucket.hasError('required')">
+ *ngIf="(frm.submitted || bucketForm.controls.bid.dirty) && bucketForm.controls.bid.hasError('required')">
This field is required.
</span>
<span i18n
class="help-block"
- *ngIf="(frm.submitted || bucketForm.controls.bucket.dirty) && bucketForm.controls.bucket.hasError('bucketNameInvalid')">
+ *ngIf="(frm.submitted || bucketForm.controls.bid.dirty) && bucketForm.controls.bid.hasError('bucketNameInvalid')">
The value is not valid.
</span>
<span i18n
class="help-block"
- *ngIf="(frm.submitted || bucketForm.controls.bucket.dirty) && bucketForm.controls.bucket.hasError('bucketNameExists')">
+ *ngIf="(frm.submitted || bucketForm.controls.bid.dirty) && bucketForm.controls.bid.hasError('bucketNameExists')">
The chosen name is already in use.
</span>
</div>
createForm() {
this.bucketForm = this.formBuilder.group({
id: [null],
- bucket: [null, [Validators.required], [this.bucketNameValidator()]],
+ bid: [null, [Validators.required], [this.bucketNameValidator()]],
owner: [null, [Validators.required]]
});
}
// 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);
if (this.bucketForm.pristine) {
this.goToListView();
}
- const bucketCtl = this.bucketForm.get('bucket');
+ const bucketCtl = this.bucketForm.get('bid');
const ownerCtl = this.bucketForm.get('owner');
if (this.editing) {
// Edit
<button type="button"
class="btn btn-sm btn-primary"
*ngIf="selection.hasSingleSelection"
- routerLink="/rgw/bucket/edit/{{ selection.first()?.bucket | encodeUri }}">
+ routerLink="/rgw/bucket/edit/{{ selection.first()?.bid | encodeUri }}">
<i class="fa fa-fw fa-pencil"></i>
<ng-container i18n>Edit</ng-container>
</button>
<li role="menuitem"
[ngClass]="{'disabled': !selection.hasSingleSelection}">
<a class="dropdown-item"
- routerLink="/rgw/bucket/edit/{{ selection.first()?.bucket | encodeUri }}"
+ routerLink="/rgw/bucket/edit/{{ selection.first()?.bid | encodeUri }}"
i18n>
<i class="fa fa-fw fa-pencil"></i>
Edit
this.columns = [
{
name: 'Name',
- prop: 'bucket',
+ prop: 'bid',
flexGrow: 1
},
{
<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
<!-- Username -->
<div class="form-group"
- [ngClass]="{'has-error': (frm.submitted || userForm.controls.user_id.dirty) && userForm.controls.user_id.invalid}">
+ [ngClass]="{'has-error': (frm.submitted || userForm.controls.uid.dirty) && userForm.controls.uid.invalid}">
<label class="control-label col-sm-3"
- for="user_id"
+ for="uid"
i18n>Username
<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="(frm.submitted || userForm.controls.user_id.dirty) && userForm.controls.user_id.hasError('required')"
+ *ngIf="(frm.submitted || userForm.controls.uid.dirty) && userForm.controls.uid.hasError('required')"
i18n>
This field is required.
</span>
<span class="help-block"
- *ngIf="(frm.submitted || userForm.controls.user_id.dirty) && userForm.controls.user_id.hasError('userIdExists')"
+ *ngIf="(frm.submitted || userForm.controls.uid.dirty) && userForm.controls.uid.hasError('userIdExists')"
i18n>
The chosen user ID is already in use.
</span>
<!-- Maximum size -->
<div class="form-group"
[ngClass]="{'has-error': (frm.submitted || userForm.controls.user_quota_max_size.dirty) && userForm.controls.user_quota_max_size.invalid}"
- *ngIf="!userForm.controls.user_quota_max_size_unlimited.value">
+ *ngIf="userForm.controls.user_quota_enabled.value && !userForm.controls.user_quota_max_size_unlimited.value">
<label class="control-label col-sm-3"
for="user_quota_max_size"
i18n>Max. size
<!-- Maximum objects -->
<div class="form-group"
[ngClass]="{'has-error': (frm.submitted || userForm.controls.user_quota_max_objects.dirty) && userForm.controls.user_quota_max_objects.invalid}"
- *ngIf="!userForm.controls.user_quota_max_objects_unlimited.value">
+ *ngIf="userForm.controls.user_quota_enabled.value && !userForm.controls.user_quota_max_objects_unlimited.value">
<label class="control-label col-sm-3"
for="user_quota_max_objects"
i18n>Max. objects
<!-- Maximum size -->
<div class="form-group"
[ngClass]="{'has-error': (frm.submitted || userForm.controls.bucket_quota_max_size.dirty) && userForm.controls.bucket_quota_max_size.invalid}"
- *ngIf="!userForm.controls.bucket_quota_max_size_unlimited.value">
+ *ngIf="userForm.controls.bucket_quota_enabled.value && !userForm.controls.bucket_quota_max_size_unlimited.value">
<label class="control-label col-sm-3"
for="bucket_quota_max_size"
i18n>Max. size
<!-- Maximum objects -->
<div class="form-group"
[ngClass]="{'has-error': (frm.submitted || userForm.controls.bucket_quota_max_objects.dirty) && userForm.controls.bucket_quota_max_objects.invalid}"
- *ngIf="!userForm.controls.bucket_quota_max_objects_unlimited.value">
+ *ngIf="userForm.controls.bucket_quota_enabled.value && !userForm.controls.bucket_quota_max_objects_unlimited.value">
<label class="control-label col-sm-3"
for="bucket_quota_max_objects"
i18n>Max. objects
createForm() {
this.userForm = this.formBuilder.group({
// General
- user_id: [null, [Validators.required], [this.userIdValidator()]],
+ uid: [null, [Validators.required], [this.userIdValidator()]],
display_name: [null, [Validators.required]],
email: [null, [CdValidators.email]],
max_buckets: [null, [Validators.min(0)]],
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));
Observable.forkJoin(observables).subscribe(
(resp: any[]) => {
this.loading = false;
value[type + '_quota_max_size'] = `${quota.max_size} B`;
}
if (quota.max_objects < 0) {
- value[type + '_quota_max_size_unlimited'] = true;
- value[type + '_quota_max_size'] = null;
+ value[type + '_quota_max_objects_unlimited'] = true;
+ value[type + '_quota_max_objects'] = null;
} else {
value[type + '_quota_max_objects_unlimited'] = false;
value[type + '_quota_max_objects'] = quota.max_objects;
// Create an observable to modify the subuser when the form is submitted.
this.submitObservables.push(
this.rgwUserService.addSubuser(
- this.userForm.get('user_id').value,
+ this.userForm.get('uid').value,
subuser.id,
subuser.permissions,
subuser.secret_key,
// Create an observable to add the subuser when the form is submitted.
this.submitObservables.push(
this.rgwUserService.addSubuser(
- this.userForm.get('user_id').value,
+ this.userForm.get('uid').value,
subuser.id,
subuser.permissions,
subuser.secret_key,
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.get('user_id').value, subuser.id)
+ this.rgwUserService.deleteSubuser(this.userForm.get('uid').value, subuser.id)
);
// Remove the associated S3 keys.
this.s3Keys = this.s3Keys.filter((key) => {
* Add/Update a capability.
*/
setCapability(cap: RgwUserCapability, index?: number) {
- const uid = this.userForm.get('user_id').value;
+ const uid = this.userForm.get('uid').value;
if (_.isNumber(index)) {
// Modify
const oldCap = this.capabilities[index];
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.get('user_id').value, cap.type, cap.perm)
+ this.rgwUserService.deleteCapability(this.userForm.get('uid').value, cap.type, cap.perm)
);
// Remove the capability to update the UI.
this.capabilities.splice(index, 1);
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.get('user_id').value, key.access_key)
+ this.rgwUserService.deleteS3Key(this.userForm.get('uid').value, key.access_key)
);
// Remove the S3 key to update the UI.
this.s3Keys.splice(index, 1);
* @param {number | undefined} index The subuser to show.
*/
showSubuserModal(index?: number) {
- const uid = this.userForm.get('user_id').value;
+ const uid = this.userForm.get('uid').value;
const modalRef = this.bsModalService.show(RgwUserSubuserModalComponent);
if (_.isNumber(index)) {
// Edit
*/
private _getApiPutArgs() {
const result = {
- uid: this.userForm.get('user_id').value,
+ uid: this.userForm.get('uid').value,
'display-name': this.userForm.get('display_name').value
};
const suspendedCtl = this.userForm.get('suspended');
*/
private _getApiPostArgs() {
const result = {
- uid: this.userForm.get('user_id').value
+ uid: this.userForm.get('uid').value
};
const argsMap = {
'display-name': 'display_name',
*/
private _getApiUserQuotaArgs(): object {
const result = {
- uid: this.userForm.get('user_id').value,
+ uid: this.userForm.get('uid').value,
'quota-type': 'user',
enabled: this.userForm.get('user_quota_enabled').value,
'max-size-kb': -1,
*/
private _getApiBucketQuotaArgs(): object {
const result = {
- uid: this.userForm.get('user_id').value,
+ uid: this.userForm.get('uid').value,
'quota-type': 'bucket',
enabled: this.userForm.get('bucket_quota_enabled').value,
'max-size-kb': -1,
private _getS3KeyUserCandidates() {
let result = [];
// Add the current user id.
- const user_id = this.userForm.get('user_id').value;
- if (_.isString(user_id) && !_.isEmpty(user_id)) {
- result.push(user_id);
+ const uid = this.userForm.get('uid').value;
+ if (_.isString(uid) && !_.isEmpty(uid)) {
+ result.push(uid);
}
// Append the subusers.
this.subusers.forEach((subUser) => {
columnMode="flex"
selectionType="multi"
(updateSelection)="updateSelection($event)"
- identifier="user_id"
+ identifier="uid"
(fetchData)="getUserList()">
<div class="table-actions">
<div class="btn-group" dropdown>
<button type="button"
class="btn btn-sm btn-primary"
*ngIf="selection.hasSingleSelection"
- routerLink="/rgw/user/edit/{{ selection.first()?.user_id }}">
+ routerLink="/rgw/user/edit/{{ selection.first()?.uid | encodeUri }}">
<i class="fa fa-fw fa-pencil"></i>
<ng-container i18n>Edit</ng-container>
</button>
<li role="menuitem"
[ngClass]="{'disabled': !selection.hasSingleSelection}">
<a class="dropdown-item"
- routerLink="/rgw/user/edit/{{ selection.first()?.user_id }}"
+ routerLink="/rgw/user/edit/{{ selection.first()?.uid | encodeUri }}"
i18n>
<i class="fa fa-fw fa-pencil"></i>
Edit
this.columns = [
{
name: 'Username',
- prop: 'user_id',
+ prop: 'uid',
flexGrow: 1
},
{
// Delete all selected data table rows.
Observable.forkJoin(
this.selection.selected.map((user: any) => {
- return this.rgwUserService.delete(user.user_id);
+ return this.rgwUserService.delete(user.uid);
})
).subscribe(
null,
}
get(bucket: string) {
- let params = new HttpParams();
- params = params.append('bucket', bucket);
- return this.http.get(this.url, {params: params});
+ return this.http.get(`api/rgw/bucket/${bucket}`);
}
create(bucket: string, uid: string) {
- const body = {
- 'bucket': bucket,
- 'uid': uid
- };
- return this.http.post('api/rgw/bucket', body);
+ let params = new HttpParams();
+ params = params.append('bucket', bucket);
+ params = params.append('uid', uid);
+ return this.http.post('api/rgw/bucket', null, {params: params});
}
update(bucketId: string, bucket: string, uid: string) {
params = params.append('bucket', bucket);
params = params.append('bucket-id', bucketId as string);
params = params.append('uid', uid);
- return this.http.put(this.url, null, { params: params });
+ return this.http.put(this.url, null, {params: params});
}
delete(bucket: string, purgeObjects = true) {
}
get(uid: string) {
- let params = new HttpParams();
- params = params.append('uid', uid);
- return this.http.get(this.url, {params: params});
+ return this.http.get(`api/rgw/user/${uid}`);
}
getQuota(uid: string) {