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