Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NSFS | NC | Add a Flag to Change the Value of allow_bucket_creation #7827

Merged
merged 1 commit into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 60 additions & 13 deletions src/cmd/manage_nsfs.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const manage_nsfs_glacier = require('../manage_nsfs/manage_nsfs_glacier');
const bucket_policy_utils = require('../endpoint/s3/s3_bucket_policy_utils');
const nsfs_schema_utils = require('../manage_nsfs/nsfs_schema_utils');
const { print_usage } = require('../manage_nsfs/manage_nsfs_help_utils');
const { TYPES, ACTIONS, VALID_OPTIONS, OPTION_TYPE,
const { TYPES, ACTIONS, VALID_OPTIONS, OPTION_TYPE, BOOLEAN_STRING_VALUES,
LIST_ACCOUNT_FILTERS, LIST_BUCKET_FILTERS, GLACIER_ACTIONS } = require('../manage_nsfs/manage_nsfs_constants');
const NoobaaEvent = require('../manage_nsfs/manage_nsfs_events_utils').NoobaaEvent;

Expand Down Expand Up @@ -146,7 +146,7 @@ async function fetch_bucket_data(argv, from_file) {
owner_account: undefined,
system_owner: argv.owner, // GAP - needs to be the system_owner (currently it is the account name)
bucket_owner: argv.owner,
wide: argv.wide,
wide: _.isUndefined(argv.wide) ? undefined : get_boolean_or_string_value(argv.wide),
tag: undefined, // if we would add the option to tag a bucket using CLI, this should be changed
versioning: action === ACTIONS.ADD ? 'DISABLED' : undefined,
creation_date: action === ACTIONS.ADD ? new Date().toISOString() : undefined,
Expand Down Expand Up @@ -239,7 +239,9 @@ async function verify_bucket_owner(bucket_owner, action) {
}
// check if bucket owner has the permission to create bucket (for bucket add only)
if (action === ACTIONS.ADD && !account.allow_bucket_creation) {
throw_cli_error(ManageCLIError.BucketCreationNotAllowed, bucket_owner);
const detail_msg = `${bucket_owner} account not allowed to create new buckets. ` +
`Please make sure to have a valid new_buckets_path and enable the flag allow_bucket_creation`;
throw_cli_error(ManageCLIError.BucketCreationNotAllowed, detail_msg);
shirady marked this conversation as resolved.
Show resolved Hide resolved
}
return account._id;
}
Expand Down Expand Up @@ -333,7 +335,7 @@ async function manage_bucket_operations(action, data, argv) {

async function account_management(argv, from_file) {
const action = argv._[1] || '';
const show_secrets = Boolean(argv.show_secrets) || false;
const show_secrets = get_boolean_or_string_value(argv.show_secrets);
const data = await fetch_account_data(argv, from_file);
await manage_account_operations(action, data, show_secrets, argv);
}
Expand Down Expand Up @@ -379,7 +381,8 @@ async function fetch_account_data(argv, from_file) {
const regenerate = action === ACTIONS.ADD;
access_keys = set_access_keys(argv, regenerate);
} else if (action === ACTIONS.UPDATE) {
access_keys = set_access_keys(argv, Boolean(argv.regenerate));
const regenerate = get_boolean_or_string_value(argv.regenerate);
access_keys = set_access_keys(argv, regenerate);
new_access_key = access_keys[0].access_key;
access_keys[0].access_key = undefined; //Setting it as undefined so we can replace the symlink
}
Expand All @@ -389,7 +392,7 @@ async function fetch_account_data(argv, from_file) {
name: _.isUndefined(argv.name) ? undefined : String(argv.name),
email: _.isUndefined(argv.name) ? undefined : String(argv.name), // temp, keep the email internally
creation_date: action === ACTIONS.ADD ? new Date().toISOString() : undefined,
wide: argv.wide,
wide: _.isUndefined(argv.wide) ? undefined : get_boolean_or_string_value(argv.wide),
new_name: _.isUndefined(argv.new_name) ? undefined : String(argv.new_name),
new_access_key,
access_keys,
Expand All @@ -414,8 +417,14 @@ async function fetch_account_data(argv, from_file) {
// secret_key as SensitiveString
data.access_keys[0].secret_key = _.isUndefined(data.access_keys[0].secret_key) ? undefined :
new SensitiveString(String(data.access_keys[0].secret_key));
// allow_bucket_creation infer from new_buckets_path
data.allow_bucket_creation = !_.isUndefined(data.nsfs_account_config.new_buckets_path);
// allow_bucket_creation either set by user or infer from new_buckets_path
if (_.isUndefined(argv.allow_bucket_creation)) {
data.allow_bucket_creation = !_.isUndefined(data.nsfs_account_config.new_buckets_path);
} else if (typeof argv.allow_bucket_creation === 'boolean') {
data.allow_bucket_creation = Boolean(argv.allow_bucket_creation);
shirady marked this conversation as resolved.
Show resolved Hide resolved
} else { // string of true or false
data.allow_bucket_creation = argv.allow_bucket_creation.toLowerCase() === 'true';
}
// fs_backend deletion specified with empty string '' (but it is not part of the schema)
data.nsfs_account_config.fs_backend = data.nsfs_account_config.fs_backend || undefined;

Expand Down Expand Up @@ -811,7 +820,7 @@ function validate_flags_arguments(type, action, argv) {
validate_no_extra_options(type, action, input_options);
const input_options_with_data = { ...argv };
delete input_options_with_data._;
validate_options_type_by_value(type, input_options_with_data);
validate_options_type_by_value(input_options_with_data);
}

/**
Expand Down Expand Up @@ -861,10 +870,10 @@ function validate_no_extra_options(type, action, input_options) {
}
/**
* validate_options_type_by_value check the type of the value that match what we expect.
* @param {string} type
* @param {object} input_options_with_data object with flag (key) and value
*/
function validate_options_type_by_value(type, input_options_with_data) {
function validate_options_type_by_value(input_options_with_data) {
let details;
for (const [option, value] of Object.entries(input_options_with_data)) {
const type_of_option = OPTION_TYPE[option];
const type_of_value = typeof value;
Expand All @@ -873,12 +882,32 @@ function validate_options_type_by_value(type, input_options_with_data) {
if ((option === 'name' || option === 'new_name') && (type_of_value === 'number')) {
continue;
}
const err_msg = `type of flag ${option} should be ${type_of_option}`;
throw_cli_error(ManageCLIError.InvalidArgumentType, err_msg);
// special case for boolean values
if (['allow_bucket_creation', 'regenerate', 'wide', 'show_secrets'].includes(option) && validate_boolean_string_value(value)) {
continue;
}
details = `type of flag ${option} should be ${type_of_option}`;
throw_cli_error(ManageCLIError.InvalidArgumentType, details);
}
}
}

/**
* validate_boolean_string_value is used when the option type is boolean
* and we wish to allow the command also to to accept 'true' and 'false' values.
* @param {boolean|string} value
*/
function validate_boolean_string_value(value) {
if (value && typeof value === 'string') {
const check_allowed_boolean_value = BOOLEAN_STRING_VALUES.includes(value.toLowerCase());
if (!check_allowed_boolean_value) {
throw_cli_error(ManageCLIError.InvalidBooleanValue);
}
return true;
}
return false;
}

///////////////////////////////
/// UTILS ///
///////////////////////////////
Expand Down Expand Up @@ -949,6 +978,24 @@ function _validate_access_keys(argv) {
})) throw_cli_error(ManageCLIError.AccountSecretKeyFlagComplexity);

}

/**
* get_boolean_or_string_value will check if the value
* 1. if the value is undefined - it returns false.
* 2. (the value is defined) if it a string 'true' or 'false' = then we set boolean respectively.
* 3. (the value is defined) then we set true (Boolean convert of this case will be true).
* @param {boolean|string} value
*/
function get_boolean_or_string_value(value) {
if (_.isUndefined(value)) {
return false;
} else if (typeof value === 'string' && BOOLEAN_STRING_VALUES.includes(value.toLowerCase())) {
return value.toLowerCase() === 'true';
} else { // boolean type
return Boolean(value);
}
}

async function glacier_management(argv) {
const action = argv._[1] || '';
await manage_glacier_operations(action, argv);
Expand Down
6 changes: 6 additions & 0 deletions src/manage_nsfs/manage_nsfs_cli_errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,12 @@ ManageCLIError.InvalidAccountNewBucketsPath = Object.freeze({
http_code: 400,
});

ManageCLIError.InvalidBooleanValue = Object.freeze({
code: 'InvalidBooleanValue',
message: 'supported values are true and false',
http_code: 400,
});

ManageCLIError.InvalidNewNameAccountIdentifier = Object.freeze({
code: 'InvalidNewNameAccountIdentifier',
message: 'Account new_name can not be used on add command, please remove the --new_name flag',
Expand Down
8 changes: 6 additions & 2 deletions src/manage_nsfs/manage_nsfs_constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ const GLOBAL_CONFIG_ROOT = 'config_root';
const GLOBAL_CONFIG_OPTIONS = new Set(['from_file', GLOBAL_CONFIG_ROOT, 'config_root_backend']);

const VALID_OPTIONS_ACCOUNT = {
'add': new Set(['name', 'uid', 'gid', 'new_buckets_path', 'user', 'access_key', 'secret_key', 'fs_backend', ...GLOBAL_CONFIG_OPTIONS]),
'update': new Set(['name', 'uid', 'gid', 'new_buckets_path', 'user', 'access_key', 'secret_key', 'fs_backend', 'new_name', 'regenerate', ...GLOBAL_CONFIG_OPTIONS]),
'add': new Set(['name', 'uid', 'gid', 'new_buckets_path', 'user', 'access_key', 'secret_key', 'fs_backend', 'allow_bucket_creation', ...GLOBAL_CONFIG_OPTIONS]),
'update': new Set(['name', 'uid', 'gid', 'new_buckets_path', 'user', 'access_key', 'secret_key', 'fs_backend', 'allow_bucket_creation', 'new_name', 'regenerate', ...GLOBAL_CONFIG_OPTIONS]),
'delete': new Set(['name', GLOBAL_CONFIG_ROOT]),
'list': new Set(['wide', 'show_secrets', GLOBAL_CONFIG_ROOT, 'gid', 'uid', 'user', 'name', 'access_key']),
'status': new Set(['name', 'access_key', 'show_secrets', GLOBAL_CONFIG_ROOT]),
Expand Down Expand Up @@ -72,6 +72,7 @@ const OPTION_TYPE = {
access_key: 'string',
secret_key: 'string',
fs_backend: 'string',
allow_bucket_creation: 'boolean',
config_root: 'string',
from_file: 'string',
config_root_backend: 'string',
Expand All @@ -84,6 +85,8 @@ const OPTION_TYPE = {
ips: 'string',
};

const BOOLEAN_STRING_VALUES = ['true', 'false'];

const LIST_ACCOUNT_FILTERS = ['uid', 'gid', 'user', 'name', 'access_key'];
const LIST_BUCKET_FILTERS = ['name'];

Expand All @@ -94,6 +97,7 @@ exports.GLACIER_ACTIONS = GLACIER_ACTIONS;
exports.CONFIG_SUBDIRS = CONFIG_SUBDIRS;
exports.VALID_OPTIONS = VALID_OPTIONS;
exports.OPTION_TYPE = OPTION_TYPE;
exports.BOOLEAN_STRING_VALUES = BOOLEAN_STRING_VALUES;

exports.LIST_ACCOUNT_FILTERS = LIST_ACCOUNT_FILTERS;
exports.LIST_BUCKET_FILTERS = LIST_BUCKET_FILTERS;
Loading