]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.ts
bump version to 15.2.4-pve1
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / rgw / rgw-user-form / rgw-user-form.component.ts
1 import { Component, OnInit } from '@angular/core';
2 import { AbstractControl, ValidationErrors, Validators } from '@angular/forms';
3 import { ActivatedRoute, Router } from '@angular/router';
4
5 import { I18n } from '@ngx-translate/i18n-polyfill';
6 import * as _ from 'lodash';
7 import { BsModalService } from 'ngx-bootstrap/modal';
8 import { concat as observableConcat, forkJoin as observableForkJoin, Observable } from 'rxjs';
9
10 import { RgwUserService } from '../../../shared/api/rgw-user.service';
11 import { ActionLabelsI18n, URLVerbs } from '../../../shared/constants/app.constants';
12 import { Icons } from '../../../shared/enum/icons.enum';
13 import { NotificationType } from '../../../shared/enum/notification-type.enum';
14 import { CdFormBuilder } from '../../../shared/forms/cd-form-builder';
15 import { CdFormGroup } from '../../../shared/forms/cd-form-group';
16 import { CdValidators, isEmptyInputValue } from '../../../shared/forms/cd-validators';
17 import { FormatterService } from '../../../shared/services/formatter.service';
18 import { NotificationService } from '../../../shared/services/notification.service';
19 import { RgwUserCapabilities } from '../models/rgw-user-capabilities';
20 import { RgwUserCapability } from '../models/rgw-user-capability';
21 import { RgwUserS3Key } from '../models/rgw-user-s3-key';
22 import { RgwUserSubuser } from '../models/rgw-user-subuser';
23 import { RgwUserSwiftKey } from '../models/rgw-user-swift-key';
24 import { RgwUserCapabilityModalComponent } from '../rgw-user-capability-modal/rgw-user-capability-modal.component';
25 import { RgwUserS3KeyModalComponent } from '../rgw-user-s3-key-modal/rgw-user-s3-key-modal.component';
26 import { RgwUserSubuserModalComponent } from '../rgw-user-subuser-modal/rgw-user-subuser-modal.component';
27 import { RgwUserSwiftKeyModalComponent } from '../rgw-user-swift-key-modal/rgw-user-swift-key-modal.component';
28
29 @Component({
30 selector: 'cd-rgw-user-form',
31 templateUrl: './rgw-user-form.component.html',
32 styleUrls: ['./rgw-user-form.component.scss']
33 })
34 export class RgwUserFormComponent implements OnInit {
35 userForm: CdFormGroup;
36 editing = false;
37 error = false;
38 loading = false;
39 submitObservables: Observable<Object>[] = [];
40 icons = Icons;
41 subusers: RgwUserSubuser[] = [];
42 s3Keys: RgwUserS3Key[] = [];
43 swiftKeys: RgwUserSwiftKey[] = [];
44 capabilities: RgwUserCapability[] = [];
45
46 action: string;
47 resource: string;
48 subuserLabel: string;
49 s3keyLabel: string;
50 capabilityLabel: string;
51
52 constructor(
53 private formBuilder: CdFormBuilder,
54 private route: ActivatedRoute,
55 private router: Router,
56 private rgwUserService: RgwUserService,
57 private bsModalService: BsModalService,
58 private notificationService: NotificationService,
59 private i18n: I18n,
60 public actionLabels: ActionLabelsI18n
61 ) {
62 this.resource = this.i18n('user');
63 this.subuserLabel = this.i18n('subuser');
64 this.s3keyLabel = this.i18n('S3 Key');
65 this.capabilityLabel = this.i18n('capability');
66 this.createForm();
67 }
68
69 createForm() {
70 this.userForm = this.formBuilder.group({
71 // General
72 uid: [
73 null,
74 [Validators.required],
75 [CdValidators.unique(this.rgwUserService.exists, this.rgwUserService)]
76 ],
77 display_name: [null, [Validators.required]],
78 email: [
79 null,
80 [CdValidators.email],
81 [CdValidators.unique(this.rgwUserService.emailExists, this.rgwUserService)]
82 ],
83 max_buckets_mode: [1],
84 max_buckets: [
85 1000,
86 [
87 CdValidators.requiredIf({ max_buckets_mode: '1' }),
88 CdValidators.number(false),
89 Validators.min(1)
90 ]
91 ],
92 suspended: [false],
93 // S3 key
94 generate_key: [true],
95 access_key: [null, [CdValidators.requiredIf({ generate_key: false })]],
96 secret_key: [null, [CdValidators.requiredIf({ generate_key: false })]],
97 // User quota
98 user_quota_enabled: [false],
99 user_quota_max_size_unlimited: [true],
100 user_quota_max_size: [
101 null,
102 [
103 CdValidators.composeIf(
104 {
105 user_quota_enabled: true,
106 user_quota_max_size_unlimited: false
107 },
108 [Validators.required, this.quotaMaxSizeValidator]
109 )
110 ]
111 ],
112 user_quota_max_objects_unlimited: [true],
113 user_quota_max_objects: [
114 null,
115 [
116 Validators.min(0),
117 CdValidators.requiredIf({
118 user_quota_enabled: true,
119 user_quota_max_objects_unlimited: false
120 })
121 ]
122 ],
123 // Bucket quota
124 bucket_quota_enabled: [false],
125 bucket_quota_max_size_unlimited: [true],
126 bucket_quota_max_size: [
127 null,
128 [
129 CdValidators.composeIf(
130 {
131 bucket_quota_enabled: true,
132 bucket_quota_max_size_unlimited: false
133 },
134 [Validators.required, this.quotaMaxSizeValidator]
135 )
136 ]
137 ],
138 bucket_quota_max_objects_unlimited: [true],
139 bucket_quota_max_objects: [
140 null,
141 [
142 Validators.min(0),
143 CdValidators.requiredIf({
144 bucket_quota_enabled: true,
145 bucket_quota_max_objects_unlimited: false
146 })
147 ]
148 ]
149 });
150 }
151
152 ngOnInit() {
153 this.editing = this.router.url.startsWith(`/rgw/user/${URLVerbs.EDIT}`);
154 this.action = this.editing ? this.actionLabels.EDIT : this.actionLabels.CREATE;
155 // Process route parameters.
156 this.route.params.subscribe((params: { uid: string }) => {
157 if (!params.hasOwnProperty('uid')) {
158 return;
159 }
160 const uid = decodeURIComponent(params.uid);
161 this.loading = true;
162 // Load the user and quota information.
163 const observables = [];
164 observables.push(this.rgwUserService.get(uid));
165 observables.push(this.rgwUserService.getQuota(uid));
166 observableForkJoin(observables).subscribe(
167 (resp: any[]) => {
168 this.loading = false;
169 // Get the default values.
170 const defaults = _.clone(this.userForm.value);
171 // Extract the values displayed in the form.
172 let value = _.pick(resp[0], _.keys(this.userForm.value));
173 // Map the max. buckets values.
174 switch (value['max_buckets']) {
175 case -1:
176 value['max_buckets_mode'] = -1;
177 value['max_buckets'] = '';
178 break;
179 case 0:
180 value['max_buckets_mode'] = 0;
181 value['max_buckets'] = '';
182 break;
183 default:
184 value['max_buckets_mode'] = 1;
185 break;
186 }
187 // Map the quota values.
188 ['user', 'bucket'].forEach((type) => {
189 const quota = resp[1][type + '_quota'];
190 value[type + '_quota_enabled'] = quota.enabled;
191 if (quota.max_size < 0) {
192 value[type + '_quota_max_size_unlimited'] = true;
193 value[type + '_quota_max_size'] = null;
194 } else {
195 value[type + '_quota_max_size_unlimited'] = false;
196 value[type + '_quota_max_size'] = `${quota.max_size} B`;
197 }
198 if (quota.max_objects < 0) {
199 value[type + '_quota_max_objects_unlimited'] = true;
200 value[type + '_quota_max_objects'] = null;
201 } else {
202 value[type + '_quota_max_objects_unlimited'] = false;
203 value[type + '_quota_max_objects'] = quota.max_objects;
204 }
205 });
206 // Merge with default values.
207 value = _.merge(defaults, value);
208 // Update the form.
209 this.userForm.setValue(value);
210
211 // Get the sub users.
212 this.subusers = resp[0].subusers;
213
214 // Get the keys.
215 this.s3Keys = resp[0].keys;
216 this.swiftKeys = resp[0].swift_keys;
217
218 // Process the capabilities.
219 const mapPerm = { 'read, write': '*' };
220 resp[0].caps.forEach((cap: any) => {
221 if (cap.perm in mapPerm) {
222 cap.perm = mapPerm[cap.perm];
223 }
224 });
225 this.capabilities = resp[0].caps;
226 },
227 (error) => {
228 this.error = error;
229 }
230 );
231 });
232 }
233
234 goToListView() {
235 this.router.navigate(['/rgw/user']);
236 }
237
238 onSubmit() {
239 let notificationTitle: string;
240 // Exit immediately if the form isn't dirty.
241 if (this.userForm.pristine) {
242 this.goToListView();
243 return;
244 }
245 const uid = this.userForm.getValue('uid');
246 if (this.editing) {
247 // Edit
248 if (this._isGeneralDirty()) {
249 const args = this._getUpdateArgs();
250 this.submitObservables.push(this.rgwUserService.update(uid, args));
251 }
252 notificationTitle = this.i18n('Updated Object Gateway user "{{uid}}"', { uid: uid });
253 } else {
254 // Add
255 const args = this._getCreateArgs();
256 this.submitObservables.push(this.rgwUserService.create(args));
257 notificationTitle = this.i18n('Created Object Gateway user "{{uid}}"', { uid: uid });
258 }
259 // Check if user quota has been modified.
260 if (this._isUserQuotaDirty()) {
261 const userQuotaArgs = this._getUserQuotaArgs();
262 this.submitObservables.push(this.rgwUserService.updateQuota(uid, userQuotaArgs));
263 }
264 // Check if bucket quota has been modified.
265 if (this._isBucketQuotaDirty()) {
266 const bucketQuotaArgs = this._getBucketQuotaArgs();
267 this.submitObservables.push(this.rgwUserService.updateQuota(uid, bucketQuotaArgs));
268 }
269 // Finally execute all observables one by one in serial.
270 observableConcat(...this.submitObservables).subscribe({
271 error: () => {
272 // Reset the 'Submit' button.
273 this.userForm.setErrors({ cdSubmitButton: true });
274 },
275 complete: () => {
276 this.notificationService.show(NotificationType.success, notificationTitle);
277 this.goToListView();
278 }
279 });
280 }
281
282 /**
283 * Validate the quota maximum size, e.g. 1096, 1K, 30M or 1.9MiB.
284 */
285 quotaMaxSizeValidator(control: AbstractControl): ValidationErrors | null {
286 if (isEmptyInputValue(control.value)) {
287 return null;
288 }
289 const m = RegExp('^(\\d+(\\.\\d+)?)\\s*(B|K(B|iB)?|M(B|iB)?|G(B|iB)?|T(B|iB)?)?$', 'i').exec(
290 control.value
291 );
292 if (m === null) {
293 return { quotaMaxSize: true };
294 }
295 const bytes = new FormatterService().toBytes(control.value);
296 return bytes < 1024 ? { quotaMaxSize: true } : null;
297 }
298
299 /**
300 * Add/Update a subuser.
301 */
302 setSubuser(subuser: RgwUserSubuser, index?: number) {
303 const mapPermissions: Record<string, string> = {
304 'full-control': 'full',
305 'read-write': 'readwrite'
306 };
307 const uid = this.userForm.getValue('uid');
308 const args = {
309 subuser: subuser.id,
310 access:
311 subuser.permissions in mapPermissions
312 ? mapPermissions[subuser.permissions]
313 : subuser.permissions,
314 key_type: 'swift',
315 secret_key: subuser.secret_key,
316 generate_secret: subuser.generate_secret ? 'true' : 'false'
317 };
318 this.submitObservables.push(this.rgwUserService.createSubuser(uid, args));
319 if (_.isNumber(index)) {
320 // Modify
321 // Create an observable to modify the subuser when the form is submitted.
322 this.subusers[index] = subuser;
323 } else {
324 // Add
325 // Create an observable to add the subuser when the form is submitted.
326 this.subusers.push(subuser);
327 // Add a Swift key. If the secret key is auto-generated, then visualize
328 // this to the user by displaying a notification instead of the key.
329 this.swiftKeys.push({
330 user: subuser.id,
331 secret_key: subuser.generate_secret ? 'Apply your changes first...' : subuser.secret_key
332 });
333 }
334 // Mark the form as dirty to be able to submit it.
335 this.userForm.markAsDirty();
336 }
337
338 /**
339 * Delete a subuser.
340 * @param {number} index The subuser to delete.
341 */
342 deleteSubuser(index: number) {
343 const subuser = this.subusers[index];
344 // Create an observable to delete the subuser when the form is submitted.
345 this.submitObservables.push(
346 this.rgwUserService.deleteSubuser(this.userForm.getValue('uid'), subuser.id)
347 );
348 // Remove the associated S3 keys.
349 this.s3Keys = this.s3Keys.filter((key) => {
350 return key.user !== subuser.id;
351 });
352 // Remove the associated Swift keys.
353 this.swiftKeys = this.swiftKeys.filter((key) => {
354 return key.user !== subuser.id;
355 });
356 // Remove the subuser to update the UI.
357 this.subusers.splice(index, 1);
358 // Mark the form as dirty to be able to submit it.
359 this.userForm.markAsDirty();
360 }
361
362 /**
363 * Add/Update a capability.
364 */
365 setCapability(cap: RgwUserCapability, index?: number) {
366 const uid = this.userForm.getValue('uid');
367 if (_.isNumber(index)) {
368 // Modify
369 const oldCap = this.capabilities[index];
370 // Note, the RadosGW Admin OPS API does not support the modification of
371 // user capabilities. Because of that it is necessary to delete it and
372 // then to re-add the capability with its new value/permission.
373 this.submitObservables.push(
374 this.rgwUserService.deleteCapability(uid, oldCap.type, oldCap.perm)
375 );
376 this.submitObservables.push(this.rgwUserService.addCapability(uid, cap.type, cap.perm));
377 this.capabilities[index] = cap;
378 } else {
379 // Add
380 // Create an observable to add the capability when the form is submitted.
381 this.submitObservables.push(this.rgwUserService.addCapability(uid, cap.type, cap.perm));
382 this.capabilities.push(cap);
383 }
384 // Mark the form as dirty to be able to submit it.
385 this.userForm.markAsDirty();
386 }
387
388 /**
389 * Delete the given capability:
390 * - Delete it from the local array to update the UI
391 * - Create an observable that will be executed on form submit
392 * @param {number} index The capability to delete.
393 */
394 deleteCapability(index: number) {
395 const cap = this.capabilities[index];
396 // Create an observable to delete the capability when the form is submitted.
397 this.submitObservables.push(
398 this.rgwUserService.deleteCapability(this.userForm.getValue('uid'), cap.type, cap.perm)
399 );
400 // Remove the capability to update the UI.
401 this.capabilities.splice(index, 1);
402 // Mark the form as dirty to be able to submit it.
403 this.userForm.markAsDirty();
404 }
405
406 hasAllCapabilities() {
407 return !_.difference(RgwUserCapabilities.getAll(), _.map(this.capabilities, 'type')).length;
408 }
409
410 /**
411 * Add/Update a S3 key.
412 */
413 setS3Key(key: RgwUserS3Key, index?: number) {
414 if (_.isNumber(index)) {
415 // Modify
416 // Nothing to do here at the moment.
417 } else {
418 // Add
419 // Split the key's user name into its user and subuser parts.
420 const userMatches = key.user.match(/([^:]+)(:(.+))?/);
421 // Create an observable to add the S3 key when the form is submitted.
422 const uid = userMatches[1];
423 const args = {
424 subuser: userMatches[2] ? userMatches[3] : '',
425 generate_key: key.generate_key ? 'true' : 'false'
426 };
427 if (args['generate_key'] === 'false') {
428 if (!_.isNil(key.access_key)) {
429 args['access_key'] = key.access_key;
430 }
431 if (!_.isNil(key.secret_key)) {
432 args['secret_key'] = key.secret_key;
433 }
434 }
435 this.submitObservables.push(this.rgwUserService.addS3Key(uid, args));
436 // If the access and the secret key are auto-generated, then visualize
437 // this to the user by displaying a notification instead of the key.
438 this.s3Keys.push({
439 user: key.user,
440 access_key: key.generate_key ? 'Apply your changes first...' : key.access_key,
441 secret_key: key.generate_key ? 'Apply your changes first...' : key.secret_key
442 });
443 }
444 // Mark the form as dirty to be able to submit it.
445 this.userForm.markAsDirty();
446 }
447
448 /**
449 * Delete a S3 key.
450 * @param {number} index The S3 key to delete.
451 */
452 deleteS3Key(index: number) {
453 const key = this.s3Keys[index];
454 // Create an observable to delete the S3 key when the form is submitted.
455 this.submitObservables.push(
456 this.rgwUserService.deleteS3Key(this.userForm.getValue('uid'), key.access_key)
457 );
458 // Remove the S3 key to update the UI.
459 this.s3Keys.splice(index, 1);
460 // Mark the form as dirty to be able to submit it.
461 this.userForm.markAsDirty();
462 }
463
464 /**
465 * Show the specified subuser in a modal dialog.
466 * @param {number | undefined} index The subuser to show.
467 */
468 showSubuserModal(index?: number) {
469 const uid = this.userForm.getValue('uid');
470 const modalRef = this.bsModalService.show(RgwUserSubuserModalComponent);
471 if (_.isNumber(index)) {
472 // Edit
473 const subuser = this.subusers[index];
474 modalRef.content.setEditing();
475 modalRef.content.setValues(uid, subuser.id, subuser.permissions);
476 } else {
477 // Add
478 modalRef.content.setEditing(false);
479 modalRef.content.setValues(uid);
480 modalRef.content.setSubusers(this.subusers);
481 }
482 modalRef.content.submitAction.subscribe((subuser: RgwUserSubuser) => {
483 this.setSubuser(subuser, index);
484 });
485 }
486
487 /**
488 * Show the specified S3 key in a modal dialog.
489 * @param {number | undefined} index The S3 key to show.
490 */
491 showS3KeyModal(index?: number) {
492 const modalRef = this.bsModalService.show(RgwUserS3KeyModalComponent);
493 if (_.isNumber(index)) {
494 // View
495 const key = this.s3Keys[index];
496 modalRef.content.setViewing();
497 modalRef.content.setValues(key.user, key.access_key, key.secret_key);
498 } else {
499 // Add
500 const candidates = this._getS3KeyUserCandidates();
501 modalRef.content.setViewing(false);
502 modalRef.content.setUserCandidates(candidates);
503 modalRef.content.submitAction.subscribe((key: RgwUserS3Key) => {
504 this.setS3Key(key);
505 });
506 }
507 }
508
509 /**
510 * Show the specified Swift key in a modal dialog.
511 * @param {number} index The Swift key to show.
512 */
513 showSwiftKeyModal(index: number) {
514 const modalRef = this.bsModalService.show(RgwUserSwiftKeyModalComponent);
515 const key = this.swiftKeys[index];
516 modalRef.content.setValues(key.user, key.secret_key);
517 }
518
519 /**
520 * Show the specified capability in a modal dialog.
521 * @param {number | undefined} index The S3 key to show.
522 */
523 showCapabilityModal(index?: number) {
524 const modalRef = this.bsModalService.show(RgwUserCapabilityModalComponent);
525 if (_.isNumber(index)) {
526 // Edit
527 const cap = this.capabilities[index];
528 modalRef.content.setEditing();
529 modalRef.content.setValues(cap.type, cap.perm);
530 } else {
531 // Add
532 modalRef.content.setEditing(false);
533 modalRef.content.setCapabilities(this.capabilities);
534 }
535 modalRef.content.submitAction.subscribe((cap: RgwUserCapability) => {
536 this.setCapability(cap, index);
537 });
538 }
539
540 /**
541 * Check if the general user settings (display name, email, ...) have been modified.
542 * @return {Boolean} Returns TRUE if the general user settings have been modified.
543 */
544 private _isGeneralDirty(): boolean {
545 return ['display_name', 'email', 'max_buckets_mode', 'max_buckets', 'suspended'].some(
546 (path) => {
547 return this.userForm.get(path).dirty;
548 }
549 );
550 }
551
552 /**
553 * Check if the user quota has been modified.
554 * @return {Boolean} Returns TRUE if the user quota has been modified.
555 */
556 private _isUserQuotaDirty(): boolean {
557 return [
558 'user_quota_enabled',
559 'user_quota_max_size_unlimited',
560 'user_quota_max_size',
561 'user_quota_max_objects_unlimited',
562 'user_quota_max_objects'
563 ].some((path) => {
564 return this.userForm.get(path).dirty;
565 });
566 }
567
568 /**
569 * Check if the bucket quota has been modified.
570 * @return {Boolean} Returns TRUE if the bucket quota has been modified.
571 */
572 private _isBucketQuotaDirty(): boolean {
573 return [
574 'bucket_quota_enabled',
575 'bucket_quota_max_size_unlimited',
576 'bucket_quota_max_size',
577 'bucket_quota_max_objects_unlimited',
578 'bucket_quota_max_objects'
579 ].some((path) => {
580 return this.userForm.get(path).dirty;
581 });
582 }
583
584 /**
585 * Helper function to get the arguments of the API request when a new
586 * user is created.
587 */
588 private _getCreateArgs() {
589 const result = {
590 uid: this.userForm.getValue('uid'),
591 display_name: this.userForm.getValue('display_name'),
592 suspended: this.userForm.getValue('suspended'),
593 email: '',
594 max_buckets: this.userForm.getValue('max_buckets'),
595 generate_key: this.userForm.getValue('generate_key'),
596 access_key: '',
597 secret_key: ''
598 };
599 const email = this.userForm.getValue('email');
600 if (_.isString(email) && email.length > 0) {
601 _.merge(result, { email: email });
602 }
603 const generateKey = this.userForm.getValue('generate_key');
604 if (!generateKey) {
605 _.merge(result, {
606 generate_key: false,
607 access_key: this.userForm.getValue('access_key'),
608 secret_key: this.userForm.getValue('secret_key')
609 });
610 }
611 const maxBucketsMode = parseInt(this.userForm.getValue('max_buckets_mode'), 10);
612 if (_.includes([-1, 0], maxBucketsMode)) {
613 // -1 => Disable bucket creation.
614 // 0 => Unlimited bucket creation.
615 _.merge(result, { max_buckets: maxBucketsMode });
616 }
617 return result;
618 }
619
620 /**
621 * Helper function to get the arguments for the API request when the user
622 * configuration has been modified.
623 */
624 private _getUpdateArgs() {
625 const result: Record<string, any> = {};
626 const keys = ['display_name', 'email', 'max_buckets', 'suspended'];
627 for (const key of keys) {
628 result[key] = this.userForm.getValue(key);
629 }
630 const maxBucketsMode = parseInt(this.userForm.getValue('max_buckets_mode'), 10);
631 if (_.includes([-1, 0], maxBucketsMode)) {
632 // -1 => Disable bucket creation.
633 // 0 => Unlimited bucket creation.
634 result['max_buckets'] = maxBucketsMode;
635 }
636 return result;
637 }
638
639 /**
640 * Helper function to get the arguments for the API request when the user
641 * quota configuration has been modified.
642 */
643 private _getUserQuotaArgs(): Record<string, any> {
644 const result = {
645 quota_type: 'user',
646 enabled: this.userForm.getValue('user_quota_enabled'),
647 max_size_kb: -1,
648 max_objects: -1
649 };
650 if (!this.userForm.getValue('user_quota_max_size_unlimited')) {
651 // Convert the given value to bytes.
652 const bytes = new FormatterService().toBytes(this.userForm.getValue('user_quota_max_size'));
653 // Finally convert the value to KiB.
654 result['max_size_kb'] = (bytes / 1024).toFixed(0) as any;
655 }
656 if (!this.userForm.getValue('user_quota_max_objects_unlimited')) {
657 result['max_objects'] = this.userForm.getValue('user_quota_max_objects');
658 }
659 return result;
660 }
661
662 /**
663 * Helper function to get the arguments for the API request when the bucket
664 * quota configuration has been modified.
665 */
666 private _getBucketQuotaArgs(): Record<string, any> {
667 const result = {
668 quota_type: 'bucket',
669 enabled: this.userForm.getValue('bucket_quota_enabled'),
670 max_size_kb: -1,
671 max_objects: -1
672 };
673 if (!this.userForm.getValue('bucket_quota_max_size_unlimited')) {
674 // Convert the given value to bytes.
675 const bytes = new FormatterService().toBytes(this.userForm.getValue('bucket_quota_max_size'));
676 // Finally convert the value to KiB.
677 result['max_size_kb'] = (bytes / 1024).toFixed(0) as any;
678 }
679 if (!this.userForm.getValue('bucket_quota_max_objects_unlimited')) {
680 result['max_objects'] = this.userForm.getValue('bucket_quota_max_objects');
681 }
682 return result;
683 }
684
685 /**
686 * Helper method to get the user candidates for S3 keys.
687 * @returns {Array} Returns a list of user identifiers.
688 */
689 private _getS3KeyUserCandidates() {
690 let result = [];
691 // Add the current user id.
692 const uid = this.userForm.getValue('uid');
693 if (_.isString(uid) && !_.isEmpty(uid)) {
694 result.push(uid);
695 }
696 // Append the subusers.
697 this.subusers.forEach((subUser) => {
698 result.push(subUser.id);
699 });
700 // Note that it's possible to create multiple S3 key pairs for a user,
701 // thus we append already configured users, too.
702 this.s3Keys.forEach((key) => {
703 result.push(key.user);
704 });
705 result = _.uniq(result);
706 return result;
707 }
708 }