Skip to content

Commit

Permalink
NC | Multi Protocol Access | List object with conflicting ownership
Browse files Browse the repository at this point in the history
Signed-off-by: naveenpaul1 <[email protected]>
  • Loading branch information
naveenpaul1 committed Feb 3, 2025
1 parent 65e2df2 commit 9d96e8d
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 13 deletions.
27 changes: 20 additions & 7 deletions src/sdk/namespace_fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ function _get_filename(file_name) {
}
return file_name;
}

/**
* @param {fs.Dirent} first_entry
* @param {fs.Dirent} second_entry
Expand Down Expand Up @@ -877,13 +878,7 @@ class NamespaceFS {

const prefix_dir_key = prefix.slice(0, prefix.lastIndexOf('/') + 1);
await process_dir(prefix_dir_key);
await Promise.all(results.map(async r => {
if (r.common_prefix) return;
const entry_path = path.join(this.bucket_path, r.key);
//If entry is outside of bucket, returns stat of symbolic link
const use_lstat = !(await this._is_path_in_bucket_boundaries(fs_context, entry_path));
r.stat = await nb_native().fs.stat(fs_context, entry_path, { use_lstat });
}));
await this.validate_results(results, fs_context);
const res = {
objects: [],
common_prefixes: [],
Expand Down Expand Up @@ -924,6 +919,24 @@ class NamespaceFS {
}
}

async validate_results(results, fs_context) {
await Promise.all(results.map(async r => {
if (r.common_prefix) return;
const entry_path = path.join(this.bucket_path, r.key);
//If entry is outside of bucket, returns stat of symbolic link
const use_lstat = !(await this._is_path_in_bucket_boundaries(fs_context, entry_path));
try {
r.stat = await nb_native().fs.stat(fs_context, entry_path, { use_lstat });
} catch (err) {
dbg.warn('NamespaceFS: validate_results : couldnt access file entry_path', entry_path, ", skipping...");
const index = results.indexOf(r);
if (index > -1) {
results.splice(index, 1);
}
}
}));
}

/////////////////
// OBJECT READ //
/////////////////
Expand Down
135 changes: 129 additions & 6 deletions src/test/unit_tests/test_ns_list_objects.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,15 @@

const mocha = require('mocha');
const assert = require('assert');

const fs = require('fs');
const path = require('path');
const NamespaceFS = require('../../sdk/namespace_fs');
const { TMP_PATH } = require('../system_tests/test_utils');
const endpoint_stats_collector = require('../../sdk/endpoint_stats_collector');
const buffer_utils = require('../../util/buffer_utils');
const fs_utils = require('../../util/fs_utils');
const SensitiveString = require('../../util/sensitive_string');
const config = require('../../../config');

// eslint-disable-next-line max-lines-per-function
function test_ns_list_objects(ns, object_sdk, bucket) {
Expand All @@ -16,11 +23,7 @@ function test_ns_list_objects(ns, object_sdk, bucket) {
const max_keys_objects = make_keys(2604, i => `max_keys_test${i}`);
const files_in_inner_folders_to_upload_post = make_keys(264, i => `folder1/inner_folder/file${i}`);
const files_in_inner_folders_to_upload_pre = make_keys(264, i => `folder1/ainner_folder/file${i}`);
// const files_in_multipart_folders_to_upload = make_keys(264, i => `multipart/file${i}`);
// const same_multipart_file1 = make_keys(10, i => `multipart1`);
// const same_multipart_file2 = make_keys(10, i => `multipart3`);
// const small_folder_with_multipart = make_keys(10, i => `multipart2/file${i}`);
// const prefix_infinite_loop_test = Object.freeze([`d/d/d/`, `d/d/f`, `d/f`].sort());
const small_key_files_set = make_keys(10, i => `small_key_files_set${i}`);

mocha.describe('basic tests', function() {
this.timeout(10 * 60 * 1000); // eslint-disable-line no-invalid-this
Expand Down Expand Up @@ -363,6 +366,89 @@ function test_ns_list_objects(ns, object_sdk, bucket) {

});

mocha.describe('list object access check', function() {
this.timeout(10 * 60 * 1000); // eslint-disable-line no-invalid-this

const access_src_bkt = 'access_src';
const tmp_fs_path = path.join(TMP_PATH, 'test_namespace_access_fs');
const ns_tmp_bucket_path = path.join(tmp_fs_path, access_src_bkt);
const first_file_path = path.join(ns_tmp_bucket_path, 'file_without_folder1');
const bucket1 = 'access_bucket1';

const ns_src = new NamespaceFS({
bucket_path: ns_tmp_bucket_path,
bucket_id: '5',
namespace_resource_id: undefined,
access_mode: undefined,
versioning: undefined,
force_md5_etag: false,
stats: endpoint_stats_collector.instance(),
});
const custom_dummy_object_sdk1 = make_custom_dummy_object_sdk(200, 200);
const custom_dummy_object_sdk2 = make_custom_dummy_object_sdk(300, 200);
const custom_dummy_object_sdk3 = make_custom_dummy_object_sdk(400, 400);
mocha.before(async function() {
await fs_utils.create_fresh_path(tmp_fs_path, 0o777);
await fs_utils.file_must_exist(tmp_fs_path);
await fs_utils.create_fresh_path(ns_tmp_bucket_path, 0o770);
await fs_utils.file_must_exist(ns_tmp_bucket_path);
await fs.promises.chmod(tmp_fs_path, 0o777);
await fs.promises.chmod(ns_tmp_bucket_path, 0o770);

await fs.promises.chown(ns_tmp_bucket_path, custom_dummy_object_sdk1.requesting_account.nsfs_account_config.uid,
custom_dummy_object_sdk1.requesting_account.nsfs_account_config.gid);
});
mocha.after(async function() {
fs_utils.folder_delete(ns_tmp_bucket_path);
fs_utils.folder_delete(tmp_fs_path);
});

mocha.it('list object with inaccessible item, different UI and GID', async function() {
await upload_objects(files_without_folders_to_upload, custom_dummy_object_sdk1, bucket1, ns_src);
await fs.promises.chown(first_file_path, 400, 400);
await fs.promises.chmod(path.join(ns_tmp_bucket_path, config.NSFS_TEMP_DIR_NAME + '_' + ns_src.bucket_id), 0o770);
await fs.promises.chmod(path.join(ns_tmp_bucket_path, config.NSFS_TEMP_DIR_NAME + '_' + ns_src.bucket_id, 'uploads'), 0o770);
const r = await ns_src.list_objects({
bucket,
}, custom_dummy_object_sdk1);
assert.deepStrictEqual(r.objects.length, 263);
const index = files_without_folders_to_upload.indexOf('file_without_folder1');
const splice_array = [...files_without_folders_to_upload];
splice_array.splice(index, 1);
assert.deepStrictEqual(r.objects.map(it => it.key), splice_array.sort());
});

mocha.it('list object with different account and same GID', async function() {
await upload_objects(small_key_files_set, custom_dummy_object_sdk2, bucket1, ns_src);
const r = await ns_src.list_objects({
bucket,
}, custom_dummy_object_sdk2);
assert.deepStrictEqual(r.objects.length, 273);
const splice_array = [...files_without_folders_to_upload, ...small_key_files_set];
const index = splice_array.indexOf('file_without_folder1');
splice_array.splice(index, 1);
assert.deepStrictEqual(r.objects.map(it => it.key), splice_array.sort());
});

mocha.it('list object with different account and different GID', async function() {
try {
await upload_objects(["Banana"], custom_dummy_object_sdk3, bucket1, ns_src);
} catch (err) {
assert.strictEqual(err instanceof Error, true);
assert.strictEqual(err.code, 'EACCES');
}
const r = await ns_src.list_objects({
bucket,
}, custom_dummy_object_sdk2);
assert.deepStrictEqual(r.objects.length, 273);
const splice_array = [...files_without_folders_to_upload, ...small_key_files_set];
const index = splice_array.indexOf('file_without_folder1');
splice_array.splice(index, 1);
assert.deepStrictEqual(r.objects.map(it => it.key), splice_array.sort());
});
});


/**
* @param {number} count
* @param {(i:number)=>string} gen
Expand Down Expand Up @@ -431,6 +517,43 @@ function test_ns_list_objects(ns, object_sdk, bucket) {

return res;
}

async function upload_objects(keys, custom_object_sdk, user_bucket, user_ns) {
return Promise.all(keys.map(async key => {
await user_ns.upload_object({
bucket: user_bucket,
key,
content_type: 'application/octet-stream',
source_stream: buffer_utils.buffer_to_read_stream(null),
size: 0
}, custom_object_sdk);
}));
}

function make_custom_dummy_object_sdk(uid, gid) {
return {
requesting_account: {
force_md5_etag: false,
nsfs_account_config: {
uid: uid,
gid: gid,
}
},
abort_controller: new AbortController(),
throw_if_aborted() {
if (this.abort_controller.signal.aborted) throw new Error('request aborted signal');
},

read_bucket_sdk_config_info(name) {
return {
bucket_owner: new SensitiveString('dummy-owner'),
owner_account: {
id: 'dummy-id-123',
}
};
}
};
}
}

module.exports = test_ns_list_objects;

0 comments on commit 9d96e8d

Please sign in to comment.