Skip to content

Commit 0669471

Browse files
authored
Merge pull request #8751 from naveenpaul1/list_object_conflicting_files
NC | Multi Protocol Access | List object with conflicting ownership
2 parents ea0b9cd + 5dcd3a8 commit 0669471

File tree

3 files changed

+160
-1
lines changed

3 files changed

+160
-1
lines changed

src/sdk/namespace_fs.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,10 @@ class NamespaceFS {
721721
if (pos < results.length) {
722722
results.splice(pos, 0, r);
723723
} else {
724-
results.push(r);
724+
const stat = await native_fs_utils.stat_ignore_eacces(this.bucket_path, r, fs_context);
725+
if (stat) {
726+
results.push(r);
727+
}
725728
}
726729
if (results.length > limit) {
727730
results.length = limit;

src/test/unit_tests/test_nsfs_access.js

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ const fs_utils = require('../../util/fs_utils');
99
const nb_native = require('../../util/nb_native');
1010
const test_utils = require('../system_tests/test_utils');
1111
const fs = require('fs');
12+
const { TMP_PATH } = require('../system_tests/test_utils');
13+
const NamespaceFS = require('../../sdk/namespace_fs');
14+
const buffer_utils = require('../../util/buffer_utils');
15+
const endpoint_stats_collector = require('../../sdk/endpoint_stats_collector');
16+
const SensitiveString = require('../../util/sensitive_string');
1217

1318
const new_umask = process.env.NOOBAA_ENDPOINT_UMASK || 0o000;
1419
const old_umask = process.umask(new_umask);
@@ -175,5 +180,145 @@ mocha.describe('new tests check', async function() {
175180
});
176181
});
177182

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

186+
const key_files_set_first = make_keys(10, i => `small_key_files_set_first${i}`);
187+
const key_files_set_second = make_keys(10, i => `small_key_files_set_second${i}`);
188+
const max_keys_files_set = make_keys(981, i => `max_keys_files_set${i}`);
189+
const access_src_bkt = 'access_src';
190+
const tmp_fs_path = path.join(TMP_PATH, 'test_namespace_access_fs');
191+
const ns_tmp_bucket_path = path.join(tmp_fs_path, access_src_bkt);
192+
const first_file_path = path.join(ns_tmp_bucket_path, 'small_key_files_set_first1');
193+
const bucket1 = 'access_bucket1';
194+
const ns_src = new NamespaceFS({
195+
bucket_path: ns_tmp_bucket_path,
196+
bucket_id: '5',
197+
namespace_resource_id: undefined,
198+
access_mode: undefined,
199+
versioning: undefined,
200+
force_md5_etag: false,
201+
stats: endpoint_stats_collector.instance(),
202+
});
203+
const custom_dummy_object_sdk1 = make_custom_dummy_object_sdk(200, 200);
204+
const custom_dummy_object_sdk2 = make_custom_dummy_object_sdk(300, 200);
205+
const custom_dummy_object_sdk3 = make_custom_dummy_object_sdk(400, 400);
206+
mocha.before(async function() {
207+
await fs_utils.create_fresh_path(tmp_fs_path, 0o777);
208+
await fs_utils.file_must_exist(tmp_fs_path);
209+
await fs_utils.create_fresh_path(ns_tmp_bucket_path, 0o770);
210+
await fs_utils.file_must_exist(ns_tmp_bucket_path);
211+
await fs.promises.chmod(tmp_fs_path, 0o777);
212+
await fs.promises.chmod(ns_tmp_bucket_path, 0o770);
213+
await fs.promises.chown(ns_tmp_bucket_path, custom_dummy_object_sdk1.requesting_account.nsfs_account_config.uid,
214+
custom_dummy_object_sdk1.requesting_account.nsfs_account_config.gid);
215+
});
216+
mocha.after(async function() {
217+
fs_utils.folder_delete(ns_tmp_bucket_path);
218+
fs_utils.folder_delete(tmp_fs_path);
219+
});
220+
221+
mocha.it('list object with inaccessible item, smae UI and GID', async function() {
222+
await upload_objects(key_files_set_first, custom_dummy_object_sdk1, bucket1, ns_src);
223+
// change ownership for one file, and account can not access this file
224+
await fs.promises.chown(first_file_path, 999, 999);
225+
const r = await ns_src.list_objects({
226+
bucket: bucket1,
227+
}, custom_dummy_object_sdk1);
228+
// skipping inaccessible file, list rest of the files
229+
assert_list_items(r, [...key_files_set_first], 9);
230+
});
231+
232+
mocha.it('list object with different account and same GID', async function() {
233+
await upload_objects(key_files_set_second, custom_dummy_object_sdk2, bucket1, ns_src);
234+
const r = await ns_src.list_objects({
235+
bucket: bucket1,
236+
}, custom_dummy_object_sdk2);
237+
assert_list_items(r, [...key_files_set_first, ...key_files_set_second], 19);
238+
});
239+
240+
mocha.it('list object with different account and different GID', async function() {
241+
try {
242+
await upload_objects(["Banana"], custom_dummy_object_sdk3, bucket1, ns_src);
243+
} catch (err) {
244+
assert.strictEqual(err instanceof Error, true);
245+
assert.strictEqual(err.code, 'EACCES');
246+
}
247+
const r = await ns_src.list_objects({
248+
bucket: bucket1,
249+
}, custom_dummy_object_sdk2);
250+
assert_list_items(r, [...key_files_set_first, ...key_files_set_second], 19);
251+
});
252+
253+
mocha.it('max - list object with different account and same GID', async function() {
254+
await upload_objects(max_keys_files_set, custom_dummy_object_sdk1, bucket1, ns_src);
255+
const r = await ns_src.list_objects({
256+
bucket: bucket1,
257+
}, custom_dummy_object_sdk1);
258+
// Total number of object would be 9+10+981 = 1000
259+
assert_list_items(r, [...key_files_set_first, ...key_files_set_second, ...max_keys_files_set], 1000);
260+
});
261+
});
262+
263+
async function upload_objects(keys, custom_object_sdk, user_bucket, user_ns) {
264+
return Promise.all(keys.map(async key => {
265+
await user_ns.upload_object({
266+
bucket: user_bucket,
267+
key,
268+
content_type: 'application/octet-stream',
269+
source_stream: buffer_utils.buffer_to_read_stream(null),
270+
size: 0
271+
}, custom_object_sdk);
272+
}));
273+
}
274+
275+
function make_custom_dummy_object_sdk(uid, gid) {
276+
return {
277+
requesting_account: {
278+
force_md5_etag: false,
279+
nsfs_account_config: {
280+
uid: uid,
281+
gid: gid,
282+
}
283+
},
284+
abort_controller: new AbortController(),
285+
throw_if_aborted() {
286+
if (this.abort_controller.signal.aborted) throw new Error('request aborted signal');
287+
},
288+
289+
read_bucket_sdk_config_info(name) {
290+
return {
291+
bucket_owner: new SensitiveString('dummy-owner'),
292+
owner_account: {
293+
id: 'dummy-id-123',
294+
}
295+
};
296+
}
297+
};
298+
}
299+
300+
/**
301+
* validate list objects counts and items
302+
* @param {Object} r
303+
* @param {string[]} splice_array
304+
* @param {number} object_items_count
305+
*/
306+
function assert_list_items(r, splice_array, object_items_count) {
307+
assert.equal(r.objects.length, object_items_count);
308+
const index = splice_array.indexOf('small_key_files_set_first1');
309+
splice_array.splice(index, 1);
310+
assert.deepStrictEqual(r.objects.map(it => it.key), splice_array.sort());
311+
}
179312

313+
/**
314+
* @param {number} count
315+
* @param {(i:number)=>string} gen
316+
* @returns {string[]}
317+
*/
318+
function make_keys(count, gen) {
319+
const arr = new Array(count);
320+
for (let i = 0; i < count; ++i) arr[i] = gen(i);
321+
arr.sort();
322+
Object.freeze(arr);
323+
return arr;
324+
}

src/util/native_fs_utils.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,16 @@ function translate_error_codes(err, entity) {
696696
return err;
697697
}
698698

699+
async function stat_ignore_eacces(bucket_path, result, fs_context) {
700+
const entry_path = path.join(bucket_path, result.key);
701+
try {
702+
return await nb_native().fs.stat(fs_context, entry_path);
703+
} catch (err) {
704+
if (err.code !== 'EACCES') throw err;
705+
dbg.log0('NamespaceFS: check_stats_for_object : couldnt access file entry_path', entry_path, ", skipping...");
706+
}
707+
}
708+
699709
exports.get_umasked_mode = get_umasked_mode;
700710
exports._make_path_dirs = _make_path_dirs;
701711
exports._create_path = _create_path;
@@ -737,3 +747,4 @@ exports.get_bucket_tmpdir_full_path = get_bucket_tmpdir_full_path;
737747
exports.get_bucket_tmpdir_name = get_bucket_tmpdir_name;
738748
exports.entity_enum = entity_enum;
739749
exports.translate_error_codes = translate_error_codes;
750+
exports.stat_ignore_eacces = stat_ignore_eacces;

0 commit comments

Comments
 (0)