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