});
describe('bucketNameValidator', () => {
- it('should validate name (1/4)', () => {
+ const testValidator = (name, valid) => {
const validatorFn = component.bucketNameValidator();
- const ctrl = new FormControl('');
+ const ctrl = new FormControl(name);
+ ctrl.markAsDirty();
const validatorPromise = validatorFn(ctrl);
expect(validatorPromise instanceof Promise).toBeTruthy();
if (validatorPromise instanceof Promise) {
validatorPromise.then((resp) => {
- expect(resp).toBe(null);
+ if (valid) {
+ expect(resp).toBe(null);
+ } else {
+ expect(resp instanceof Object).toBeTruthy();
+ expect(resp.bucketNameInvalid).toBeTruthy();
+ }
});
}
+ };
+
+ it('should validate empty name', () => {
+ testValidator('', true);
});
- it('should validate name (2/4)', () => {
- const validatorFn = component.bucketNameValidator();
- const ctrl = new FormControl('ab');
- ctrl.markAsDirty();
- const validatorPromise = validatorFn(ctrl);
- expect(validatorPromise instanceof Promise).toBeTruthy();
- if (validatorPromise instanceof Promise) {
- validatorPromise.then((resp) => {
- expect(resp.bucketNameInvalid).toBeTruthy();
- });
- }
+ it('bucket names cannot be formatted as IP address', () => {
+ testValidator('172.10.4.51', false);
});
- it('should validate name (3/4)', () => {
- const validatorFn = component.bucketNameValidator();
- const ctrl = new FormControl('abc');
- ctrl.markAsDirty();
- const validatorPromise = validatorFn(ctrl);
- expect(validatorPromise instanceof Promise).toBeTruthy();
- if (validatorPromise instanceof Promise) {
- validatorPromise.then((resp) => {
- expect(resp).toBe(null);
- });
- }
+ it('bucket name must be >= 3 characters long (1/2)', () => {
+ testValidator('ab', false);
+ });
+
+ it('bucket name must be >= 3 characters long (2/2)', () => {
+ testValidator('abc', true);
+ });
+
+ it('bucket name must be <= than 63 characters long (1/2)', () => {
+ testValidator(_.repeat('a', 64), false);
+ });
+
+ it('bucket name must be <= than 63 characters long (2/2)', () => {
+ testValidator(_.repeat('a', 63), true);
+ });
+
+ it('bucket names must not contain uppercase characters or underscores (1/2)', () => {
+ testValidator('iAmInvalid', false);
+ });
+
+ it('bucket names must not contain uppercase characters or underscores (2/2)', () => {
+ testValidator('i_am_invalid', false);
+ });
+
+ it('bucket names with invalid labels (1/3)', () => {
+ testValidator('abc.1def.Ghi2', false);
+ });
+
+ it('bucket names with invalid labels (2/3)', () => {
+ testValidator('abc.1-xy', false);
+ });
+
+ it('bucket names with invalid labels (3/3)', () => {
+ testValidator('abc.*def', false);
+ });
+
+ it('bucket names must be a series of one or more labels and can contain lowercase letters, numbers, and hyphens (1/3)', () => {
+ testValidator('xyz.abc', true);
+ });
+
+ it('bucket names must be a series of one or more labels and can contain lowercase letters, numbers, and hyphens (2/3)', () => {
+ testValidator('abc.1-def', true);
+ });
+
+ it('bucket names must be a series of one or more labels and can contain lowercase letters, numbers, and hyphens (3/3)', () => {
+ testValidator('abc.ghi2', true);
});
- it('should validate name (4/4)', () => {
+ it('bucket names must be unique', () => {
spyOn(rgwBucketService, 'enumerate').and.returnValue(observableOf(['abcd']));
const validatorFn = component.bucketNameValidator();
const ctrl = new FormControl('abcd');
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 } from '../../../shared/forms/cd-validators';
import { NotificationService } from '../../../shared/services/notification.service';
@Component({
}
}
+ /**
+ * Validate the bucket name. In general, bucket names should follow domain
+ * name constraints:
+ * - Bucket names must be unique.
+ * - Bucket names cannot be formatted as IP address.
+ * - Bucket names can be between 3 and 63 characters long.
+ * - Bucket names must not contain uppercase characters or underscores.
+ * - Bucket names must start with a lowercase letter or number.
+ * - Bucket names must be a series of one or more labels. Adjacent
+ * labels are separated by a single period (.). Bucket names can
+ * contain lowercase letters, numbers, and hyphens. Each label must
+ * start and end with a lowercase letter or a number.
+ */
bucketNameValidator(): AsyncValidatorFn {
const rgwBucketService = this.rgwBucketService;
return (control: AbstractControl): Promise<ValidationErrors | null> => {
resolve(null);
return;
}
- // Validate the bucket name.
- const nameRe = /^[0-9A-Za-z][\w-\.]{2,254}$/;
- if (!nameRe.test(control.value)) {
+ const constraints = [];
+ // - Bucket names cannot be formatted as IP address.
+ constraints.push((name) => {
+ const validatorFn = CdValidators.ip();
+ return !validatorFn(name);
+ });
+ // - Bucket names can be between 3 and 63 characters long.
+ constraints.push((name) => _.inRange(name.length, 3, 64));
+ // - Bucket names must not contain uppercase characters or underscores.
+ // - Bucket names must start with a lowercase letter or number.
+ // - Bucket names must be a series of one or more labels. Adjacent
+ // labels are separated by a single period (.). Bucket names can
+ // contain lowercase letters, numbers, and hyphens. Each label must
+ // start and end with a lowercase letter or a number.
+ constraints.push((name) => {
+ const labels = _.split(name, '.');
+ return _.every(labels, (label) => {
+ // Bucket names must not contain uppercase characters or underscores.
+ if (label !== _.toLower(label) || label.includes('_')) {
+ return false;
+ }
+ // Bucket names can contain lowercase letters, numbers, and hyphens.
+ if (!/[0-9a-z-]/.test(label)) {
+ return false;
+ }
+ // Each label must start and end with a lowercase letter or a number.
+ return _.every([0, label.length], (index) => {
+ return /[a-z]/.test(label[index]) || _.isInteger(_.parseInt(label[index]));
+ });
+ });
+ });
+ if (!_.every(constraints, (func) => func(control.value))) {
resolve({ bucketNameInvalid: true });
return;
}
- // Does any bucket with the given name already exist?
+ // - Bucket names must be unique.
rgwBucketService.exists(control.value).subscribe((resp: boolean) => {
if (!resp) {
resolve(null);