src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.ts
selector | cd-rgw-user-form |
styleUrls | ./rgw-user-form.component.scss |
templateUrl | ./rgw-user-form.component.html |
Properties |
Methods |
|
constructor(formBuilder: CdFormBuilder, route: ActivatedRoute, router: Router, rgwUserService: RgwUserService, bsModalService: BsModalService, notificationService: NotificationService, i18n: I18n, actionLabels: ActionLabelsI18n)
|
|||||||||||||||||||||||||||
Parameters :
|
Private _getBucketQuotaArgs |
_getBucketQuotaArgs()
|
Helper function to get the arguments for the API request when the bucket quota configuration has been modified.
Returns :
object
|
Private _getS3KeyUserCandidates |
_getS3KeyUserCandidates()
|
Helper method to get the user candidates for S3 keys.
Returns :
{}
Returns a list of user identifiers. |
Private _getUpdateArgs |
_getUpdateArgs()
|
Helper function to get the arguments for the API request when the user configuration has been modified.
Returns :
{}
|
Private _getUserQuotaArgs |
_getUserQuotaArgs()
|
Helper function to get the arguments for the API request when the user quota configuration has been modified.
Returns :
object
|
Private _isBucketQuotaDirty |
_isBucketQuotaDirty()
|
Check if the bucket quota has been modified.
Returns :
boolean
Returns TRUE if the bucket quota has been modified. |
Private _isGeneralDirty |
_isGeneralDirty()
|
Check if the general user settings (display name, email, ...) have been modified.
Returns :
boolean
Returns TRUE if the general user settings have been modified. |
Private _isUserQuotaDirty |
_isUserQuotaDirty()
|
Check if the user quota has been modified.
Returns :
boolean
Returns TRUE if the user quota has been modified. |
createForm |
createForm()
|
Returns :
void
|
deleteCapability | ||||||||
deleteCapability(index: number)
|
||||||||
Delete the given capability: - Delete it from the local array to update the UI - Create an observable that will be executed on form submit
Parameters :
Returns :
void
|
deleteS3Key | ||||||||
deleteS3Key(index: number)
|
||||||||
Delete a S3 key.
Parameters :
Returns :
void
|
deleteSubuser | ||||||||
deleteSubuser(index: number)
|
||||||||
Delete a subuser.
Parameters :
Returns :
void
|
goToListView |
goToListView()
|
Returns :
void
|
listenToChanges |
listenToChanges()
|
Returns :
void
|
ngOnInit |
ngOnInit()
|
Returns :
void
|
onSubmit |
onSubmit()
|
Returns :
void
|
quotaMaxSizeValidator | ||||||
quotaMaxSizeValidator(control: AbstractControl)
|
||||||
Validate the quota maximum size, e.g. 1096, 1K, 30M. Only integer numbers are valid, something like 1.9M is not recognized as valid.
Parameters :
Returns :
ValidationErrors | null
|
setCapability | |||||||||
setCapability(cap: RgwUserCapability, index?: number)
|
|||||||||
Add/Update a capability.
Parameters :
Returns :
void
|
setS3Key | |||||||||
setS3Key(key: RgwUserS3Key, index?: number)
|
|||||||||
Add/Update a S3 key.
Parameters :
Returns :
void
|
setSubuser | |||||||||
setSubuser(subuser: RgwUserSubuser, index?: number)
|
|||||||||
Add/Update a subuser.
Parameters :
Returns :
void
|
showCapabilityModal | ||||||||
showCapabilityModal(index?: number)
|
||||||||
Show the specified capability in a modal dialog.
Parameters :
Returns :
void
|
showS3KeyModal | ||||||||
showS3KeyModal(index?: number)
|
||||||||
Show the specified S3 key in a modal dialog.
Parameters :
Returns :
void
|
showSubuserModal | ||||||||
showSubuserModal(index?: number)
|
||||||||
Show the specified subuser in a modal dialog.
Parameters :
Returns :
void
|
showSwiftKeyModal | ||||||||
showSwiftKeyModal(index: number)
|
||||||||
Show the specified Swift key in a modal dialog.
Parameters :
Returns :
void
|
action |
Type : string
|
Public actionLabels |
Type : ActionLabelsI18n
|
capabilities |
Type : RgwUserCapability[]
|
Default value : []
|
capabilityLabel |
Type : string
|
editing |
Default value : false
|
error |
Default value : false
|
loading |
Default value : false
|
resource |
Type : string
|
s3keyLabel |
Type : string
|
s3Keys |
Type : RgwUserS3Key[]
|
Default value : []
|
submitObservables |
Type : Observable<Object>[]
|
Default value : []
|
subuserLabel |
Type : string
|
subusers |
Type : RgwUserSubuser[]
|
Default value : []
|
swiftKeys |
Type : RgwUserSwiftKey[]
|
Default value : []
|
userForm |
Type : CdFormGroup
|
import { Component, OnInit } from '@angular/core';
import { AbstractControl, ValidationErrors, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { I18n } from '@ngx-translate/i18n-polyfill';
import * as _ from 'lodash';
import { BsModalService } from 'ngx-bootstrap/modal';
import { forkJoin as observableForkJoin, Observable } from 'rxjs';
import { RgwUserService } from '../../../shared/api/rgw-user.service';
import { ActionLabelsI18n, URLVerbs } from '../../../shared/constants/app.constants';
import { NotificationType } from '../../../shared/enum/notification-type.enum';
import { CdFormBuilder } from '../../../shared/forms/cd-form-builder';
import { CdFormGroup } from '../../../shared/forms/cd-form-group';
import { CdValidators, isEmptyInputValue } from '../../../shared/forms/cd-validators';
import { FormatterService } from '../../../shared/services/formatter.service';
import { NotificationService } from '../../../shared/services/notification.service';
import { RgwUserCapability } from '../models/rgw-user-capability';
import { RgwUserS3Key } from '../models/rgw-user-s3-key';
import { RgwUserSubuser } from '../models/rgw-user-subuser';
import { RgwUserSwiftKey } from '../models/rgw-user-swift-key';
import { RgwUserCapabilityModalComponent } from '../rgw-user-capability-modal/rgw-user-capability-modal.component';
import { RgwUserS3KeyModalComponent } from '../rgw-user-s3-key-modal/rgw-user-s3-key-modal.component';
import { RgwUserSubuserModalComponent } from '../rgw-user-subuser-modal/rgw-user-subuser-modal.component';
import { RgwUserSwiftKeyModalComponent } from '../rgw-user-swift-key-modal/rgw-user-swift-key-modal.component';
@Component({
selector: 'cd-rgw-user-form',
templateUrl: './rgw-user-form.component.html',
styleUrls: ['./rgw-user-form.component.scss']
})
export class RgwUserFormComponent implements OnInit {
userForm: CdFormGroup;
editing = false;
error = false;
loading = false;
submitObservables: Observable<Object>[] = [];
subusers: RgwUserSubuser[] = [];
s3Keys: RgwUserS3Key[] = [];
swiftKeys: RgwUserSwiftKey[] = [];
capabilities: RgwUserCapability[] = [];
action: string;
resource: string;
subuserLabel: string;
s3keyLabel: string;
capabilityLabel: string;
constructor(
private formBuilder: CdFormBuilder,
private route: ActivatedRoute,
private router: Router,
private rgwUserService: RgwUserService,
private bsModalService: BsModalService,
private notificationService: NotificationService,
private i18n: I18n,
public actionLabels: ActionLabelsI18n
) {
this.resource = this.i18n('user');
this.subuserLabel = this.i18n('subuser');
this.s3keyLabel = this.i18n('S3 Key');
this.capabilityLabel = this.i18n('capability');
this.createForm();
this.listenToChanges();
}
createForm() {
this.userForm = this.formBuilder.group({
// General
uid: [
null,
[Validators.required],
[CdValidators.unique(this.rgwUserService.exists, this.rgwUserService)]
],
display_name: [null, [Validators.required]],
email: [
null,
[CdValidators.email],
[CdValidators.unique(this.rgwUserService.emailExists, this.rgwUserService)]
],
max_buckets: [1000, [Validators.required, Validators.min(0)]],
suspended: [false],
// S3 key
generate_key: [true],
access_key: [null, [CdValidators.requiredIf({ generate_key: false })]],
secret_key: [null, [CdValidators.requiredIf({ generate_key: false })]],
// User quota
user_quota_enabled: [false],
user_quota_max_size_unlimited: [true],
user_quota_max_size: [
null,
[
CdValidators.requiredIf({
user_quota_enabled: true,
user_quota_max_size_unlimited: false
}),
this.quotaMaxSizeValidator
]
],
user_quota_max_objects_unlimited: [true],
user_quota_max_objects: [
null,
[
Validators.min(0),
CdValidators.requiredIf({
user_quota_enabled: true,
user_quota_max_objects_unlimited: false
})
]
],
// Bucket quota
bucket_quota_enabled: [false],
bucket_quota_max_size_unlimited: [true],
bucket_quota_max_size: [
null,
[
CdValidators.requiredIf({
bucket_quota_enabled: true,
bucket_quota_max_size_unlimited: false
}),
this.quotaMaxSizeValidator
]
],
bucket_quota_max_objects_unlimited: [true],
bucket_quota_max_objects: [
null,
[
Validators.min(0),
CdValidators.requiredIf({
bucket_quota_enabled: true,
bucket_quota_max_objects_unlimited: false
})
]
]
});
}
listenToChanges() {
// Reset the validation status of various controls, especially those that are using
// the 'requiredIf' validator. This is necessary because the controls itself are not
// validated again if the status of their prerequisites have been changed.
this.userForm.get('generate_key').valueChanges.subscribe(() => {
['access_key', 'secret_key'].forEach((path) => {
this.userForm.get(path).updateValueAndValidity({ onlySelf: true });
});
});
this.userForm.get('user_quota_enabled').valueChanges.subscribe(() => {
['user_quota_max_size', 'user_quota_max_objects'].forEach((path) => {
this.userForm.get(path).updateValueAndValidity({ onlySelf: true });
});
});
this.userForm.get('user_quota_max_size_unlimited').valueChanges.subscribe(() => {
this.userForm.get('user_quota_max_size').updateValueAndValidity({ onlySelf: true });
});
this.userForm.get('user_quota_max_objects_unlimited').valueChanges.subscribe(() => {
this.userForm.get('user_quota_max_objects').updateValueAndValidity({ onlySelf: true });
});
this.userForm.get('bucket_quota_enabled').valueChanges.subscribe(() => {
['bucket_quota_max_size', 'bucket_quota_max_objects'].forEach((path) => {
this.userForm.get(path).updateValueAndValidity({ onlySelf: true });
});
});
this.userForm.get('bucket_quota_max_size_unlimited').valueChanges.subscribe(() => {
this.userForm.get('bucket_quota_max_size').updateValueAndValidity({ onlySelf: true });
});
this.userForm.get('bucket_quota_max_objects_unlimited').valueChanges.subscribe(() => {
this.userForm.get('bucket_quota_max_objects').updateValueAndValidity({ onlySelf: true });
});
}
ngOnInit() {
this.editing = this.router.url.startsWith(`/rgw/user/${URLVerbs.EDIT}`);
this.action = this.editing ? this.actionLabels.EDIT : this.actionLabels.CREATE;
// Process route parameters.
this.route.params.subscribe((params: { uid: string }) => {
if (!params.hasOwnProperty('uid')) {
return;
}
const uid = decodeURIComponent(params.uid);
this.loading = true;
// Load the user and quota information.
const observables = [];
observables.push(this.rgwUserService.get(uid));
observables.push(this.rgwUserService.getQuota(uid));
observableForkJoin(observables).subscribe(
(resp: any[]) => {
this.loading = false;
// Get the default values.
const defaults = _.clone(this.userForm.value);
// Extract the values displayed in the form.
let value = _.pick(resp[0], _.keys(this.userForm.value));
// Map the quota values.
['user', 'bucket'].forEach((type) => {
const quota = resp[1][type + '_quota'];
value[type + '_quota_enabled'] = quota.enabled;
if (quota.max_size < 0) {
value[type + '_quota_max_size_unlimited'] = true;
value[type + '_quota_max_size'] = null;
} else {
value[type + '_quota_max_size_unlimited'] = false;
value[type + '_quota_max_size'] = `${quota.max_size} B`;
}
if (quota.max_objects < 0) {
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;
}
});
// Merge with default values.
value = _.merge(defaults, value);
// Update the form.
this.userForm.setValue(value);
// Get the sub users.
this.subusers = resp[0].subusers;
// Get the keys.
this.s3Keys = resp[0].keys;
this.swiftKeys = resp[0].swift_keys;
// Process the capabilities.
const mapPerm = { 'read, write': '*' };
resp[0].caps.forEach((cap) => {
if (cap.perm in mapPerm) {
cap.perm = mapPerm[cap.perm];
}
});
this.capabilities = resp[0].caps;
},
(error) => {
this.error = error;
}
);
});
}
goToListView() {
this.router.navigate(['/rgw/user']);
}
onSubmit() {
let notificationTitle: string;
// Exit immediately if the form isn't dirty.
if (this.userForm.pristine) {
this.goToListView();
return;
}
const uid = this.userForm.getValue('uid');
if (this.editing) {
// Edit
if (this._isGeneralDirty()) {
const args = this._getUpdateArgs();
this.submitObservables.push(this.rgwUserService.update(uid, args));
}
notificationTitle = this.i18n('Updated Object Gateway user "{{uid}}"', { uid: uid });
} else {
// Add
const args = this._getCreateArgs();
this.submitObservables.push(this.rgwUserService.create(args));
notificationTitle = this.i18n('Created Object Gateway user "{{uid}}"', { uid: uid });
}
// Check if user quota has been modified.
if (this._isUserQuotaDirty()) {
const userQuotaArgs = this._getUserQuotaArgs();
this.submitObservables.push(this.rgwUserService.updateQuota(uid, userQuotaArgs));
}
// Check if bucket quota has been modified.
if (this._isBucketQuotaDirty()) {
const bucketQuotaArgs = this._getBucketQuotaArgs();
this.submitObservables.push(this.rgwUserService.updateQuota(uid, bucketQuotaArgs));
}
// Finally execute all observables.
observableForkJoin(this.submitObservables).subscribe(
() => {
this.notificationService.show(NotificationType.success, notificationTitle);
this.goToListView();
},
() => {
// Reset the 'Submit' button.
this.userForm.setErrors({ cdSubmitButton: true });
}
);
}
/**
* Validate the quota maximum size, e.g. 1096, 1K, 30M. Only integer numbers are valid,
* something like 1.9M is not recognized as valid.
*/
quotaMaxSizeValidator(control: AbstractControl): ValidationErrors | null {
if (isEmptyInputValue(control.value)) {
return null;
}
const m = RegExp('^(\\d+)\\s*(B|K(B|iB)?|M(B|iB)?|G(B|iB)?|T(B|iB)?)?$', 'i').exec(
control.value
);
if (m === null) {
return { quotaMaxSize: true };
}
const bytes = new FormatterService().toBytes(control.value);
return bytes < 1024 ? { quotaMaxSize: true } : null;
}
/**
* Add/Update a subuser.
*/
setSubuser(subuser: RgwUserSubuser, index?: number) {
const mapPermissions = {
'full-control': 'full',
'read-write': 'readwrite'
};
const uid = this.userForm.getValue('uid');
const args = {
subuser: subuser.id,
access:
subuser.permissions in mapPermissions
? mapPermissions[subuser.permissions]
: subuser.permissions,
key_type: 'swift',
secret_key: subuser.secret_key,
generate_secret: subuser.generate_secret ? 'true' : 'false'
};
this.submitObservables.push(this.rgwUserService.createSubuser(uid, args));
if (_.isNumber(index)) {
// Modify
// Create an observable to modify the subuser when the form is submitted.
this.subusers[index] = subuser;
} else {
// Add
// Create an observable to add the subuser when the form is submitted.
this.subusers.push(subuser);
// Add a Swift key. If the secret key is auto-generated, then visualize
// this to the user by displaying a notification instead of the key.
this.swiftKeys.push({
user: subuser.id,
secret_key: subuser.generate_secret ? 'Apply your changes first...' : subuser.secret_key
});
}
// Mark the form as dirty to be able to submit it.
this.userForm.markAsDirty();
}
/**
* Delete a subuser.
* @param {number} index The subuser to delete.
*/
deleteSubuser(index: number) {
const subuser = this.subusers[index];
// Create an observable to delete the subuser when the form is submitted.
this.submitObservables.push(
this.rgwUserService.deleteSubuser(this.userForm.getValue('uid'), subuser.id)
);
// Remove the associated S3 keys.
this.s3Keys = this.s3Keys.filter((key) => {
return key.user !== subuser.id;
});
// Remove the associated Swift keys.
this.swiftKeys = this.swiftKeys.filter((key) => {
return key.user !== subuser.id;
});
// Remove the subuser to update the UI.
this.subusers.splice(index, 1);
// Mark the form as dirty to be able to submit it.
this.userForm.markAsDirty();
}
/**
* Add/Update a capability.
*/
setCapability(cap: RgwUserCapability, index?: number) {
const uid = this.userForm.getValue('uid');
if (_.isNumber(index)) {
// Modify
const oldCap = this.capabilities[index];
// Note, the RadosGW Admin OPS API does not support the modification of
// user capabilities. Because of that it is necessary to delete it and
// then to re-add the capability with its new value/permission.
this.submitObservables.push(
this.rgwUserService.deleteCapability(uid, oldCap.type, oldCap.perm)
);
this.submitObservables.push(this.rgwUserService.addCapability(uid, cap.type, cap.perm));
this.capabilities[index] = cap;
} else {
// Add
// Create an observable to add the capability when the form is submitted.
this.submitObservables.push(this.rgwUserService.addCapability(uid, cap.type, cap.perm));
this.capabilities.push(cap);
}
// Mark the form as dirty to be able to submit it.
this.userForm.markAsDirty();
}
/**
* Delete the given capability:
* - Delete it from the local array to update the UI
* - Create an observable that will be executed on form submit
* @param {number} index The capability to delete.
*/
deleteCapability(index: number) {
const cap = this.capabilities[index];
// Create an observable to delete the capability when the form is submitted.
this.submitObservables.push(
this.rgwUserService.deleteCapability(this.userForm.getValue('uid'), cap.type, cap.perm)
);
// Remove the capability to update the UI.
this.capabilities.splice(index, 1);
// Mark the form as dirty to be able to submit it.
this.userForm.markAsDirty();
}
/**
* Add/Update a S3 key.
*/
setS3Key(key: RgwUserS3Key, index?: number) {
if (_.isNumber(index)) {
// Modify
// Nothing to do here at the moment.
} else {
// Add
// Split the key's user name into its user and subuser parts.
const userMatches = key.user.match(/([^:]+)(:(.+))?/);
// Create an observable to add the S3 key when the form is submitted.
const uid = userMatches[1];
const args = {
subuser: userMatches[2] ? userMatches[3] : '',
generate_key: key.generate_key ? 'true' : 'false',
access_key: key.access_key,
secret_key: key.secret_key
};
this.submitObservables.push(this.rgwUserService.addS3Key(uid, args));
// If the access and the secret key are auto-generated, then visualize
// this to the user by displaying a notification instead of the key.
this.s3Keys.push({
user: key.user,
access_key: key.generate_key ? 'Apply your changes first...' : key.access_key,
secret_key: key.generate_key ? 'Apply your changes first...' : key.secret_key
});
}
// Mark the form as dirty to be able to submit it.
this.userForm.markAsDirty();
}
/**
* Delete a S3 key.
* @param {number} index The S3 key to delete.
*/
deleteS3Key(index: number) {
const key = this.s3Keys[index];
// Create an observable to delete the S3 key when the form is submitted.
this.submitObservables.push(
this.rgwUserService.deleteS3Key(this.userForm.getValue('uid'), key.access_key)
);
// Remove the S3 key to update the UI.
this.s3Keys.splice(index, 1);
// Mark the form as dirty to be able to submit it.
this.userForm.markAsDirty();
}
/**
* Show the specified subuser in a modal dialog.
* @param {number | undefined} index The subuser to show.
*/
showSubuserModal(index?: number) {
const uid = this.userForm.getValue('uid');
const modalRef = this.bsModalService.show(RgwUserSubuserModalComponent);
if (_.isNumber(index)) {
// Edit
const subuser = this.subusers[index];
modalRef.content.setEditing();
modalRef.content.setValues(uid, subuser.id, subuser.permissions);
} else {
// Add
modalRef.content.setEditing(false);
modalRef.content.setValues(uid);
modalRef.content.setSubusers(this.subusers);
}
modalRef.content.submitAction.subscribe((subuser: RgwUserSubuser) => {
this.setSubuser(subuser, index);
});
}
/**
* Show the specified S3 key in a modal dialog.
* @param {number | undefined} index The S3 key to show.
*/
showS3KeyModal(index?: number) {
const modalRef = this.bsModalService.show(RgwUserS3KeyModalComponent);
if (_.isNumber(index)) {
// View
const key = this.s3Keys[index];
modalRef.content.setViewing();
modalRef.content.setValues(key.user, key.access_key, key.secret_key);
} else {
// Add
const candidates = this._getS3KeyUserCandidates();
modalRef.content.setViewing(false);
modalRef.content.setUserCandidates(candidates);
modalRef.content.submitAction.subscribe((key: RgwUserS3Key) => {
this.setS3Key(key);
});
}
}
/**
* Show the specified Swift key in a modal dialog.
* @param {number} index The Swift key to show.
*/
showSwiftKeyModal(index: number) {
const modalRef = this.bsModalService.show(RgwUserSwiftKeyModalComponent);
const key = this.swiftKeys[index];
modalRef.content.setValues(key.user, key.secret_key);
}
/**
* Show the specified capability in a modal dialog.
* @param {number | undefined} index The S3 key to show.
*/
showCapabilityModal(index?: number) {
const modalRef = this.bsModalService.show(RgwUserCapabilityModalComponent);
if (_.isNumber(index)) {
// Edit
const cap = this.capabilities[index];
modalRef.content.setEditing();
modalRef.content.setValues(cap.type, cap.perm);
} else {
// Add
modalRef.content.setEditing(false);
modalRef.content.setCapabilities(this.capabilities);
}
modalRef.content.submitAction.subscribe((cap: RgwUserCapability) => {
this.setCapability(cap, index);
});
}
/**
* Check if the general user settings (display name, email, ...) have been modified.
* @return {Boolean} Returns TRUE if the general user settings have been modified.
*/
private _isGeneralDirty(): boolean {
return ['display_name', 'email', 'max_buckets', 'suspended'].some((path) => {
return this.userForm.get(path).dirty;
});
}
/**
* Check if the user quota has been modified.
* @return {Boolean} Returns TRUE if the user quota has been modified.
*/
private _isUserQuotaDirty(): boolean {
return [
'user_quota_enabled',
'user_quota_max_size_unlimited',
'user_quota_max_size',
'user_quota_max_objects_unlimited',
'user_quota_max_objects'
].some((path) => {
return this.userForm.get(path).dirty;
});
}
/**
* Check if the bucket quota has been modified.
* @return {Boolean} Returns TRUE if the bucket quota has been modified.
*/
private _isBucketQuotaDirty(): boolean {
return [
'bucket_quota_enabled',
'bucket_quota_max_size_unlimited',
'bucket_quota_max_size',
'bucket_quota_max_objects_unlimited',
'bucket_quota_max_objects'
].some((path) => {
return this.userForm.get(path).dirty;
});
}
/**
* Helper function to get the arguments of the API request when a new
* user is created.
*/
private _getCreateArgs() {
const result = {
uid: this.userForm.getValue('uid'),
display_name: this.userForm.getValue('display_name'),
suspended: this.userForm.getValue('suspended'),
email: '',
max_buckets: this.userForm.getValue('max_buckets'),
generate_key: this.userForm.getValue('generate_key'),
access_key: '',
secret_key: ''
};
const email = this.userForm.getValue('email');
if (_.isString(email) && email.length > 0) {
_.merge(result, { email: email });
}
const generateKey = this.userForm.getValue('generate_key');
if (!generateKey) {
_.merge(result, {
generate_key: false,
access_key: this.userForm.getValue('access_key'),
secret_key: this.userForm.getValue('secret_key')
});
}
return result;
}
/**
* Helper function to get the arguments for the API request when the user
* configuration has been modified.
*/
private _getUpdateArgs() {
const result = {};
const keys = ['display_name', 'email', 'max_buckets', 'suspended'];
for (const key of keys) {
result[key] = this.userForm.getValue(key);
}
return result;
}
/**
* Helper function to get the arguments for the API request when the user
* quota configuration has been modified.
*/
private _getUserQuotaArgs(): object {
const result = {
quota_type: 'user',
enabled: this.userForm.getValue('user_quota_enabled'),
max_size_kb: -1,
max_objects: -1
};
if (!this.userForm.getValue('user_quota_max_size_unlimited')) {
// Convert the given value to bytes.
const bytes = new FormatterService().toBytes(this.userForm.getValue('user_quota_max_size'));
// Finally convert the value to KiB.
result['max_size_kb'] = (bytes / 1024).toFixed(0) as any;
}
if (!this.userForm.getValue('user_quota_max_objects_unlimited')) {
result['max_objects'] = this.userForm.getValue('user_quota_max_objects');
}
return result;
}
/**
* Helper function to get the arguments for the API request when the bucket
* quota configuration has been modified.
*/
private _getBucketQuotaArgs(): object {
const result = {
quota_type: 'bucket',
enabled: this.userForm.getValue('bucket_quota_enabled'),
max_size_kb: -1,
max_objects: -1
};
if (!this.userForm.getValue('bucket_quota_max_size_unlimited')) {
// Convert the given value to bytes.
const bytes = new FormatterService().toBytes(this.userForm.getValue('bucket_quota_max_size'));
// Finally convert the value to KiB.
result['max_size_kb'] = (bytes / 1024).toFixed(0) as any;
}
if (!this.userForm.getValue('bucket_quota_max_objects_unlimited')) {
result['max_objects'] = this.userForm.getValue('bucket_quota_max_objects');
}
return result;
}
/**
* Helper method to get the user candidates for S3 keys.
* @returns {Array} Returns a list of user identifiers.
*/
private _getS3KeyUserCandidates() {
let result = [];
// Add the current user id.
const uid = this.userForm.getValue('uid');
if (_.isString(uid) && !_.isEmpty(uid)) {
result.push(uid);
}
// Append the subusers.
this.subusers.forEach((subUser) => {
result.push(subUser.id);
});
// Note that it's possible to create multiple S3 key pairs for a user,
// thus we append already configured users, too.
this.s3Keys.forEach((key) => {
result.push(key.user);
});
result = _.uniq(result);
return result;
}
}
<cd-loading-panel *ngIf="editing && loading && !error"
i18n>Loading user data...</cd-loading-panel>
<cd-error-panel *ngIf="editing && error"
(backAction)="goToListView()"
i18n>The user data could not be loaded.</cd-error-panel>
<div class="col-sm-12 col-lg-6"
*ngIf="!loading && !error">
<form class="form-horizontal"
#frm="ngForm"
[formGroup]="userForm"
novalidate>
<div class="panel panel-default">
<div class="panel-heading">
<h3 i18n="form title|Example: Create Pool@@formTitle"
class="panel-title">{{ action | titlecase }} {{ resource | upperFirst }}</h3>
</div>
<div class="panel-body">
<!-- Username -->
<div class="form-group"
[ngClass]="{'has-error': userForm.showError('uid', frm)}">
<label class="control-label col-sm-3"
for="uid">
<ng-container i18n>Username</ng-container>
<span class="required"
*ngIf="!editing">
</span>
</label>
<div class="col-sm-9">
<input id="uid"
class="form-control"
type="text"
formControlName="uid"
[readonly]="editing"
autofocus>
<span class="help-block"
*ngIf="userForm.showError('uid', frm, 'required')"
i18n>This field is required.</span>
<span class="help-block"
*ngIf="userForm.showError('uid', frm, 'notUnique')"
i18n>The chosen user ID is already in use.</span>
</div>
</div>
<!-- Full name -->
<div class="form-group"
[ngClass]="{'has-error': userForm.showError('display_name', frm)}">
<label class="control-label col-sm-3"
for="display_name">
<ng-container i18n>Full name</ng-container>
<span class="required"
*ngIf="!editing">
</span>
</label>
<div class="col-sm-9">
<input id="display_name"
class="form-control"
type="text"
formControlName="display_name">
<span class="help-block"
*ngIf="userForm.showError('display_name', frm, 'required')"
i18n>This field is required.</span>
</div>
</div>
<!-- Email address -->
<div class="form-group"
[ngClass]="{'has-error': userForm.showError('email', frm)}">
<label class="control-label col-sm-3"
for="email"
i18n>Email address</label>
<div class="col-sm-9">
<input id="email"
class="form-control"
type="text"
formControlName="email">
<span class="help-block"
*ngIf="userForm.showError('email', frm, 'email')"
i18n>This is not a valid email address.</span>
<span class="help-block"
*ngIf="userForm.showError('email', frm, 'notUnique')"
i18n>The chosen email address is already in use.</span>
</div>
</div>
<!-- Max. buckets -->
<div class="form-group"
[ngClass]="{'has-error': userForm.showError('max_buckets', frm)}">
<label class="control-label col-sm-3"
for="max_buckets">
<ng-container i18n>Max. buckets</ng-container>
<span class="required"></span>
</label>
<div class="col-sm-9">
<input id="max_buckets"
class="form-control"
type="number"
formControlName="max_buckets">
<span class="help-block"
*ngIf="userForm.showError('max_buckets', frm, 'required')"
i18n>This field is required.</span>
<span class="help-block"
*ngIf="userForm.showError('max_buckets', frm, 'min')"
i18n>The entered value must be >= 0.</span>
</div>
</div>
<!-- Suspended -->
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<div class="checkbox checkbox-primary">
<input id="suspended"
type="checkbox"
formControlName="suspended">
<label for="suspended"
i18n>Suspended</label>
</div>
</div>
</div>
<!-- S3 key -->
<fieldset *ngIf="!editing">
<legend i18n>S3 key</legend>
<!-- Auto-generate key -->
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<div class="checkbox checkbox-primary">
<input id="generate_key"
type="checkbox"
formControlName="generate_key">
<label for="generate_key"
i18n>Auto-generate key</label>
</div>
</div>
</div>
<!-- Access key -->
<div class="form-group"
[ngClass]="{'has-error': userForm.showError('access_key', frm)}"
*ngIf="!editing && !userForm.getValue('generate_key')">
<label class="control-label col-sm-3"
for="access_key">
<ng-container i18n>Access key</ng-container>
<span class="required"></span>
</label>
<div class="col-sm-9">
<div class="input-group">
<input id="access_key"
class="form-control"
type="password"
formControlName="access_key">
<span class="input-group-btn">
<button type="button"
class="btn btn-default"
cdPasswordButton="access_key">
</button>
<button type="button"
class="btn btn-default"
cdCopy2ClipboardButton="access_key">
</button>
</span>
</div>
<span class="help-block"
*ngIf="userForm.showError('access_key', frm, 'required')"
i18n>This field is required.</span>
</div>
</div>
<!-- Secret key -->
<div class="form-group"
[ngClass]="{'has-error': userForm.showError('secret_key', frm)}"
*ngIf="!editing && !userForm.getValue('generate_key')">
<label class="control-label col-sm-3"
for="secret_key">
<ng-container i18n>Secret key</ng-container>
<span class="required"></span>
</label>
<div class="col-sm-9">
<div class="input-group">
<input id="secret_key"
class="form-control"
type="password"
formControlName="secret_key">
<span class="input-group-btn">
<button type="button"
class="btn btn-default"
cdPasswordButton="secret_key">
</button>
<button type="button"
class="btn btn-default"
cdCopy2ClipboardButton="secret_key">
</button>
</span>
</div>
<span class="help-block"
*ngIf="userForm.showError('secret_key', frm, 'required')"
i18n>This field is required.</span>
</div>
</div>
</fieldset>
<!-- Subusers -->
<fieldset *ngIf="editing">
<legend i18n>Subusers</legend>
<div class="col-sm-offset-3 col-sm-9">
<span *ngIf="subusers.length === 0"
class="form-control no-border">
<span class="text-muted"
i18n>There are no subusers.</span>
</span>
<span *ngFor="let subuser of subusers; let i=index;">
<div class="input-group">
<span class="input-group-addon">
<i class="icon-prepend fa fa-user"></i>
</span>
<input type="text"
class="form-control"
value="{{ subuser.id }}"
readonly>
<span class="input-group-addon"
style="border-left: 0; border-right: 0;">
<i class="icon-prepend fa fa-share-alt"></i>
</span>
<input type="text"
class="form-control"
value="{{ ('full-control' === subuser.permissions) ? 'full' : subuser.permissions }}"
readonly>
<span class="input-group-btn">
<button type="button"
class="btn btn-default tc_showSubuserButton"
i18n-tooltip
tooltip="Edit"
(click)="showSubuserModal(i)">
<i class="fa fa-cogs"></i>
</button>
<button type="button"
class="btn btn-default tc_deleteSubuserButton"
i18n-tooltip
tooltip="Delete"
(click)="deleteSubuser(i)">
<i class="fa fa-times"></i>
</button>
</span>
</div>
<span class="help-block"></span>
</span>
<span class="form-control no-border">
<button type="button"
class="btn btn-sm btn-default btn-label pull-right tc_addSubuserButton"
(click)="showSubuserModal()">
<i class="fa fa-fw fa-plus"></i>
<ng-container i18n>{{ actionLabels.CREATE | titlecase }} {{ subuserLabel | upperFirst }}</ng-container>
</button>
</span>
</div>
</fieldset>
<!-- Keys -->
<fieldset *ngIf="editing">
<legend i18n>Keys</legend>
<!-- S3 keys -->
<label class="col-sm-3 control-label"
i18n>S3</label>
<div class="col-sm-9">
<span *ngIf="s3Keys.length === 0"
class="form-control no-border">
<span class="text-muted"
i18n>There are no keys.</span>
</span>
<span *ngFor="let key of s3Keys; let i=index;">
<div class="input-group">
<span class="input-group-addon">
<i class="icon-prepend fa fa-key"></i>
</span>
<input type="text"
class="form-control"
value="{{ key.user }}"
readonly>
<span class="input-group-btn">
<button type="button"
class="btn btn-default tc_showS3KeyButton"
i18n-tooltip
tooltip="Show"
(click)="showS3KeyModal(i)">
<i class="fa fa-eye"></i>
</button>
<button type="button"
class="btn btn-default tc_deleteS3KeyButton"
i18n-tooltip
tooltip="Delete"
(click)="deleteS3Key(i)">
<i class="fa fa-times"></i>
</button>
</span>
</div>
<span class="help-block"></span>
</span>
<span class="form-control no-border">
<button type="button"
class="btn btn-sm btn-default btn-label pull-right tc_addS3KeyButton"
(click)="showS3KeyModal()">
<i class="fa fa-fw fa-plus"></i>
<ng-container i18n>{{ actionLabels.CREATE | titlecase }} {{ s3keyLabel | upperFirst }}</ng-container>
</button>
</span>
<hr>
</div>
<!-- Swift keys -->
<label class="col-sm-3 control-label"
i18n>Swift</label>
<div class="col-sm-9">
<span *ngIf="swiftKeys.length === 0"
class="form-control no-border">
<span class="text-muted"
i18n>There are no keys.</span>
</span>
<span *ngFor="let key of swiftKeys; let i=index;">
<div class="input-group">
<span class="input-group-addon">
<i class="icon-prepend fa fa-key"></i>
</span>
<input type="text"
class="form-control"
value="{{ key.user }}"
readonly>
<span class="input-group-btn">
<button type="button"
class="btn btn-default tc_showSwiftKeyButton"
i18n-tooltip
tooltip="Show"
(click)="showSwiftKeyModal(i)">
<i class="fa fa-eye"></i>
</button>
</span>
</div>
<span class="help-block"></span>
</span>
</div>
</fieldset>
<!-- Capabilities -->
<fieldset *ngIf="editing">
<legend i18n>Capabilities</legend>
<div class="col-sm-offset-3 col-sm-9">
<span *ngIf="capabilities.length === 0"
class="form-control no-border">
<span class="text-muted"
i18n>There are no capabilities.</span>
</span>
<span *ngFor="let cap of capabilities; let i=index;">
<div class="input-group">
<span class="input-group-addon">
<i class="icon-prepend fa fa-share-alt"></i>
</span>
<input type="text"
class="form-control"
value="{{ cap.type }}:{{ cap.perm }}"
readonly>
<span class="input-group-btn">
<button type="button"
class="btn btn-default tc_editCapButton"
i18n-tooltip
tooltip="Edit"
(click)="showCapabilityModal(i)">
<i class="fa fa-cogs"></i>
</button>
<button type="button"
class="btn btn-default tc_deleteCapButton"
i18n-tooltip
tooltip="Delete"
(click)="deleteCapability(i)">
<i class="fa fa-times"></i>
</button>
</span>
</div>
<span class="help-block"></span>
</span>
<span class="form-control no-border">
<button type="button"
class="btn btn-sm btn-default btn-label pull-right tc_addCapButton"
(click)="showCapabilityModal()">
<i class="fa fa-fw fa-plus"></i>
<ng-container i18n>{{ actionLabels.ADD | titlecase }} {{ capabilityLabel | upperFirst }}</ng-container>
</button>
</span>
</div>
</fieldset>
<!-- User quota -->
<fieldset>
<legend i18n>User quota</legend>
<!-- Enabled -->
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<div class="checkbox checkbox-primary">
<input id="user_quota_enabled"
type="checkbox"
formControlName="user_quota_enabled">
<label for="user_quota_enabled"
i18n>Enabled</label>
</div>
</div>
</div>
<!-- Unlimited size -->
<div class="form-group"
*ngIf="userForm.controls.user_quota_enabled.value">
<div class="col-sm-offset-3 col-sm-9">
<div class="checkbox checkbox-primary">
<input id="user_quota_max_size_unlimited"
type="checkbox"
formControlName="user_quota_max_size_unlimited">
<label for="user_quota_max_size_unlimited"
i18n>Unlimited size</label>
</div>
</div>
</div>
<!-- Maximum size -->
<div class="form-group"
[ngClass]="{'has-error': userForm.showError('user_quota_max_size', frm)}"
*ngIf="!userForm.getValue('user_quota_max_size_unlimited')">
<label class="control-label col-sm-3"
for="user_quota_max_size">
<ng-container i18n>Max. size</ng-container>
<span class="required"></span>
</label>
<div class="col-sm-9">
<input id="user_quota_max_size"
class="form-control"
type="text"
formControlName="user_quota_max_size"
cdDimlessBinary>
<span class="help-block"
*ngIf="userForm.showError('user_quota_max_size', frm, 'required')"
i18n>This field is required.</span>
<span class="help-block"
*ngIf="userForm.showError('user_quota_max_size', frm, 'quotaMaxSize')"
i18n>The value is not valid.</span>
</div>
</div>
<!-- Unlimited objects -->
<div class="form-group"
*ngIf="userForm.controls.user_quota_enabled.value">
<div class="col-sm-offset-3 col-sm-9">
<div class="checkbox checkbox-primary">
<input id="user_quota_max_objects_unlimited"
type="checkbox"
formControlName="user_quota_max_objects_unlimited">
<label for="user_quota_max_objects_unlimited"
i18n>Unlimited objects</label>
</div>
</div>
</div>
<!-- Maximum objects -->
<div class="form-group"
[ngClass]="{'has-error': userForm.showError('user_quota_max_objects', frm)}"
*ngIf="!userForm.getValue('user_quota_max_objects_unlimited')">
<label class="control-label col-sm-3"
for="user_quota_max_objects">
<ng-container i18n>Max. objects</ng-container>
<span class="required"></span>
</label>
<div class="col-sm-9">
<input id="user_quota_max_objects"
class="form-control"
type="number"
formControlName="user_quota_max_objects">
<span class="help-block"
*ngIf="userForm.showError('user_quota_max_objects', frm, 'required')"
i18n>This field is required.</span>
<span class="help-block"
*ngIf="userForm.showError('user_quota_max_objects', frm, 'min')"
i18n>The entered value must be >= 0.</span>
</div>
</div>
</fieldset>
<!-- Bucket quota -->
<fieldset>
<legend i18n>Bucket quota</legend>
<!-- Enabled -->
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<div class="checkbox checkbox-primary">
<input id="bucket_quota_enabled"
type="checkbox"
formControlName="bucket_quota_enabled">
<label for="bucket_quota_enabled"
i18n>Enabled</label>
</div>
</div>
</div>
<!-- Unlimited size -->
<div class="form-group"
*ngIf="userForm.controls.bucket_quota_enabled.value">
<div class="col-sm-offset-3 col-sm-9">
<div class="checkbox checkbox-primary">
<input id="bucket_quota_max_size_unlimited"
type="checkbox"
formControlName="bucket_quota_max_size_unlimited">
<label for="bucket_quota_max_size_unlimited"
i18n>Unlimited size</label>
</div>
</div>
</div>
<!-- Maximum size -->
<div class="form-group"
[ngClass]="{'has-error': userForm.showError('bucket_quota_max_size', frm)}"
*ngIf="!userForm.getValue('bucket_quota_max_size_unlimited')">
<label class="control-label col-sm-3"
for="bucket_quota_max_size">
<ng-container i18n>Max. size</ng-container>
<span class="required"></span>
</label>
<div class="col-sm-9">
<input id="bucket_quota_max_size"
class="form-control"
type="text"
formControlName="bucket_quota_max_size"
cdDimlessBinary>
<span class="help-block"
*ngIf="userForm.showError('bucket_quota_max_size', frm, 'required')"
i18n>This field is required.</span>
<span class="help-block"
*ngIf="userForm.showError('bucket_quota_max_size', frm, 'quotaMaxSize')"
i18n>The value is not valid.</span>
</div>
</div>
<!-- Unlimited objects -->
<div class="form-group"
*ngIf="userForm.controls.bucket_quota_enabled.value">
<div class="col-sm-offset-3 col-sm-9">
<div class="checkbox checkbox-primary">
<input id="bucket_quota_max_objects_unlimited"
type="checkbox"
formControlName="bucket_quota_max_objects_unlimited">
<label for="bucket_quota_max_objects_unlimited"
i18n>Unlimited objects</label>
</div>
</div>
</div>
<!-- Maximum objects -->
<div class="form-group"
[ngClass]="{'has-error': userForm.showError('bucket_quota_max_objects', frm)}"
*ngIf="!userForm.getValue('bucket_quota_max_objects_unlimited')">
<label class="control-label col-sm-3"
for="bucket_quota_max_objects">
<ng-container i18n>Max. objects</ng-container>
<span class="required"></span>
</label>
<div class="col-sm-9">
<input id="bucket_quota_max_objects"
class="form-control"
type="number"
formControlName="bucket_quota_max_objects">
<span class="help-block"
*ngIf="userForm.showError('bucket_quota_max_objects', frm, 'required')"
i18n>This field is required.</span>
<span class="help-block"
*ngIf="userForm.showError('bucket_quota_max_objects', frm, 'min')"
i18n>The entered value must be >= 0.</span>
</div>
</div>
</fieldset>
</div>
<div class="panel-footer">
<div class="button-group text-right">
<cd-submit-button
(submitAction)="onSubmit()"
[form]="userForm"
i18n="form action button|Example: Create Pool@@formActionButton"
type="button">{{ action | titlecase }} {{ resource | upperFirst }}</cd-submit-button>
<cd-back-button></cd-back-button>
</div>
</div>
</div>
</form>
</div>
./rgw-user-form.component.scss
@import '../../../../defaults';
form .input-group-addon {
color: $color-rgw-icon !important;
background-color: transparent;
}