fix(security): always use actor

This commit is contained in:
KernelDeimos
2024-10-18 19:15:40 -04:00
parent 94e15dbfa8
commit 1954f86680
9 changed files with 67 additions and 58 deletions
+11 -12
View File
@@ -213,17 +213,16 @@ class FilesystemService extends BaseService {
return this.systemfs_;
}
async owrite ({
node, user, immutable,
file, tmp, fsentry_tmp,
message,
}) {
// NOTE: these are the parameters being passed
// (assuming this comment is up-to-date)
// {
// node, actor, immutable,
// file, tmp, fsentry_tmp,
// message,
// }
async owrite (parameters) {
const ll_owrite = new LLOWrite();
return await ll_owrite.run({
node, user, immutable,
file, tmp, fsentry_tmp,
message,
});
return await ll_owrite.run(parameters);
}
// REMINDER: There was an idea that FilesystemService implements
@@ -235,9 +234,9 @@ class FilesystemService extends BaseService {
return await ll_cwrite.run(parameters);
}
async mkdir_2 ({parent, name, user, immutable}) {
async mkdir_2 ({parent, name, actor, immutable}) {
const ll_mkdir = new LLMkdir();
return await ll_mkdir.run({ parent, name, user, immutable });
return await ll_mkdir.run({ parent, name, actor, immutable });
}
async mkshortcut ({ parent, name, user, target }) {
@@ -26,11 +26,11 @@ const config = require('../../config');
const { TeePromise } = require('../../util/promise');
class BatchExecutor extends AdvancedBase {
constructor (x, { user, log, errors }) {
constructor (x, { actor, log, errors }) {
super();
this.x = x;
this.user = user;
this.pathResolver = new PathResolver({ user });
this.actor = actor
this.pathResolver = new PathResolver({ actor });
this.expectations = x.get('services').get('expectations');
this.log = log;
this.errors = errors;
@@ -100,7 +100,7 @@ class BatchExecutor extends AdvancedBase {
const command_ins = await command_cls.run({
getFile: () => file,
pathResolver: this.pathResolver,
user: this.user
actor: this.actor,
}, op);
workUnit.checkpoint('operation invoked');
+8 -5
View File
@@ -102,7 +102,7 @@ class MkdirCommand extends BatchCommand {
parameters.create_missing_parents ??
false,
shortcut_to: parameters.shortcut_to,
user: executor.user,
actor: executor.actor,
});
if ( parameters.as ) {
executor.pathResolver.putSelector(
@@ -133,6 +133,9 @@ class WriteCommand extends BatchCommand {
}
const hl_write = new HLWrite();
if ( ! executor.actor ) {
throw new Error('Actor is missing here');
}
const response = await hl_write.run({
destination_or_parent: destinationOrParent,
specified_name: parameters.name,
@@ -145,7 +148,7 @@ class WriteCommand extends BatchCommand {
parameters.create_missing_ancestors ??
parameters.create_missing_parents ??
false,
user: executor.user,
actor: executor.actor,
file: uploaded_file,
offset: parameters.offset,
@@ -208,7 +211,7 @@ class ShortcutCommand extends BatchCommand {
const response = await hl_mkShortcut.run({
parent: destinationOrParent,
name: parameters.name,
user: executor.user,
actor: executor.actor,
target: shortcut_to,
// TODO: handle these with event service instead
@@ -241,7 +244,7 @@ class SymlinkCommand extends BatchCommand {
const response = await hl_mkLink.run({
parent: destinationOrParent,
name: parameters.name,
user: executor.user,
actor: executor.actor,
target: parameters.target,
// TODO: handle these with event service instead
@@ -266,7 +269,7 @@ class DeleteCommand extends BatchCommand {
const hl_remove = new HLRemove();
const response = await hl_remove.run({
target,
user: executor.user,
actor: executor.actor,
recursive: parameters.recursive ?? false,
descendants_only: parameters.descendants_only ?? false,
});
@@ -76,7 +76,7 @@ class MkTree extends HLFilesystemOperation {
const { context, values } = this;
const { _path } = this.modules;
const fs = context.get('services').get('filesystem');
const user = context.get('user');
const actor = context.get('actor');
const trunk = tree[0];
const branches = tree.slice(1);
@@ -124,7 +124,7 @@ class MkTree extends HLFilesystemOperation {
if ( parent_did_exist && ! parent_exists ) {
const node = await fs.node(current);
const has_perm = await chkperm(await node.get('entry'), user.id, 'write');
const has_perm = await chkperm(await node.get('entry'), actor.type.user.id, 'write');
if ( ! has_perm ) throw APIError.create('permission_denied');
}
@@ -147,7 +147,7 @@ class MkTree extends HLFilesystemOperation {
const node = await fs.mkdir_2({
parent: await fs.node(currentParent),
name: current.name,
user,
actor,
})
current = node.selector;
@@ -179,7 +179,7 @@ class QuickMkdir extends HLFilesystemOperation {
let { parent, path } = values;
const { _path } = this.modules;
const fs = context.get('services').get('filesystem');
const user = context.get('user');
const actor = context.get('actor');
parent = parent || await fs.node(new RootNodeSelector());
@@ -206,7 +206,7 @@ class QuickMkdir extends HLFilesystemOperation {
const node = await fs.mkdir_2({
parent: await fs.node(currentParent),
name: current.name,
user,
actor,
})
current = node.selector;
@@ -285,10 +285,12 @@ class HLMkdir extends HLFilesystemOperation {
// specified under `path`.
parent_node = await this._create_parents({
parent_node: top_parent,
user: values.user,
actor: values.actor,
});
const has_perm = await chkperm(await parent_node.get('entry'), values.user.id, 'write');
const user_id = values.actor.type.user.id;
const has_perm = await chkperm(await parent_node.get('entry'), user_id, 'write');
if ( ! has_perm ) throw APIError.create('permission_denied');
const existing = await fs.node(
@@ -301,12 +303,12 @@ class HLMkdir extends HLFilesystemOperation {
const { overwrite, dedupe_name, create_missing_parents } = values;
if ( overwrite ) {
// TODO: tag rm operation somehow
const has_perm = await chkperm(await existing.get('entry'), values.user.id, 'write');
const has_perm = await chkperm(await existing.get('entry'), user_id, 'write');
if ( ! has_perm ) throw APIError.create('permission_denied');
const hl_remove = new HLRemove();
await hl_remove.run({
target: existing,
user: values.user,
actor: values.actor,
recursive: true,
});
}
@@ -345,13 +347,13 @@ class HLMkdir extends HLFilesystemOperation {
if ( ! shortcut_to.entry.is_dir ) {
throw APIError.create('shortcut_target_is_a_directory');
}
const has_perm = await chkperm(shortcut_to.entry, values.user.id, 'read');
const has_perm = await chkperm(shortcut_to.entry, user_id, 'read');
if ( ! has_perm ) throw APIError.create('forbidden');
this.created = await fs.mkshortcut({
parent: parent_node,
name: target_basename,
user: values.user,
actor: values.actor,
target: shortcut_to,
});
@@ -362,7 +364,7 @@ class HLMkdir extends HLFilesystemOperation {
this.created = await fs.mkdir_2({
parent: parent_node,
name: target_basename,
user: values.user,
actor: values.actor,
});
const all_nodes = [
@@ -382,7 +384,7 @@ class HLMkdir extends HLFilesystemOperation {
return response;
}
async _create_parents ({ parent_node, user }) {
async _create_parents ({ parent_node }) {
const { context, values } = this;
const { _path } = this.modules;
const fs = context.get('services').get('filesystem');
@@ -59,7 +59,10 @@ class WriteCommonFeature {
if ( ! this.values.file ) return;
const sizeService = this.context.get('services').get('sizeService');
const { file, user } = this.values;
const { file, user: user_let } = this.values;
let user = user_let;
if ( ! user ) user = this.values.actor.type.user;
const usage = await sizeService.get_usage(user.id);
let capacity = config.is_storage_limited ? user.free_storage == undefined
@@ -281,13 +284,14 @@ class HLWrite extends HLFilesystemOperation {
if ( await shortcut_to.get('type') === TYPE_DIRECTORY ) {
throw APIError.create('shortcut_target_is_a_directory');
}
const has_perm = await chkperm(shortcut_to.entry, values.user.id, 'read');
// TODO: legacy check - likely not needed
const has_perm = await chkperm(shortcut_to.entry, values.actor.type.user.id, 'read');
if ( ! has_perm ) throw APIError.create('permission_denied');
this.created = await fs.mkshortcut({
parent,
name: target_name,
user: values.user,
actor: values.actor,
target: shortcut_to,
});
@@ -381,7 +385,7 @@ class HLWrite extends HLFilesystemOperation {
if ( is_overwrite ) {
this.written = await fs.owrite({
node: destination,
user: values.user,
actor: values.actor,
file: values.file,
tmp: {
socket_id: values.socket_id,
@@ -397,7 +401,7 @@ class HLWrite extends HLFilesystemOperation {
this.written = await fs.cwrite({
parent,
name: target_name,
user: values.user,
actor: values.actor,
file: values.file,
tmp: {
socket_id: values.socket_id,
@@ -55,7 +55,7 @@ class LLMkdir extends LLFilesystemOperation {
async _locked_run () {
const { _path, uuidv4 } = this.modules;
const { context } = this;
const { parent, name, user, immutable, actor } = this.values;
const { parent, name, immutable, actor } = this.values;
const ts = Math.round(Date.now() / 1000);
const uid = uuidv4();
@@ -95,7 +95,7 @@ class LLMkdir extends LLFilesystemOperation {
uuid: uid,
parent_uid: await parent.get('uid'),
path: _path.join(await parent.get('path'), name),
user_id: user.id,
user_id: actor.type.user.id,
name,
created: ts,
accessed: ts,
@@ -128,13 +128,11 @@ class LLWriteBase extends LLFilesystemOperation {
class LLOWrite extends LLWriteBase {
async _run () {
const {
node, user, immutable,
node, actor, immutable,
file, tmp, fsentry_tmp,
message,
} = this.values;
let { actor } = this.values;
const svc = Context.get('services');
const sizeService = svc.get('sizeService');
const resourceService = svc.get('resourceService');
@@ -152,8 +150,6 @@ class LLOWrite extends LLWriteBase {
throw APIError.create('subject_does_not_exist');
}
actor = actor ?? Actor.adapt(user);
const svc_acl = this.context.get('services').get('acl');
if ( ! await svc_acl.check(actor, node, 'write') ) {
throw await svc_acl.get_safe_acl_error(actor, node, 'write');
@@ -190,7 +186,7 @@ class LLOWrite extends LLWriteBase {
});
const filesize = file.size;
sizeService.change_usage(user.id, filesize);
sizeService.change_usage(actor.type.user.id, filesize);
const entryOp = await systemFSEntryService.update(uid, raw_fsentry_delta);
@@ -202,7 +198,7 @@ class LLOWrite extends LLWriteBase {
})();
state_upload.post_insert({
db, user, node, uid, message, ts,
db, user: actor.type.user, node, uid, message, ts,
});
const svc_fileCache = this.context.get('services').get('file-cache');
@@ -227,7 +223,7 @@ class LLCWrite extends LLWriteBase {
async _run () {
const { _path, uuidv4, config } = this.modules;
const {
parent, name, user, immutable,
parent, name, immutable,
file, tmp, fsentry_tmp,
message,
@@ -261,7 +257,7 @@ class LLCWrite extends LLWriteBase {
}
const svc_acl = this.context.get('services').get('acl');
actor = actor ?? Actor.adapt(user) ?? Context.get('actor');
actor = actor ?? Context.get('actor');
if ( ! await svc_acl.check(actor, parent, 'write') ) {
throw await svc_acl.get_safe_acl_error(actor, parent, 'write');
}
@@ -288,7 +284,7 @@ class LLCWrite extends LLWriteBase {
const raw_fsentry = {
uuid: uid,
is_dir: 0,
user_id: user.id,
user_id: actor.type.user.id,
created: ts,
accessed: ts,
modified: ts,
@@ -317,7 +313,7 @@ class LLCWrite extends LLWriteBase {
});
const filesize = file.size;
sizeService.change_usage(user.id, filesize);
sizeService.change_usage(actor.type.user.id, filesize);
this.checkpoint('after change_usage');
@@ -338,7 +334,7 @@ class LLCWrite extends LLWriteBase {
db.write(
"INSERT INTO `fsentry_versions` (`user_id`, `fsentry_id`, `fsentry_uuid`, `version_id`, `message`, `ts_epoch`) VALUES (?, ?, ?, ?, ?, ?)",
[
user.id,
actor.type.user.id,
new_item.id,
new_item.uuid,
store_version_id,
@@ -33,11 +33,11 @@ const ERR_UNKNOWN_PATHREF = 'Unknown path reference in path: ';
* that was returned by the `mkdir` operation.
*/
module.exports = class PathResolver {
constructor ({ user }) {
constructor ({ actor }) {
this.references = {};
this.selectors = {};
this.meta = {};
this.user = user;
this.actor = actor;
this.listeners = {};
@@ -88,11 +88,13 @@ module.exports = class PathResolver {
}
async awaitSelector (inputPath) {
// TODO: I feel like there's a better way to get username
const username = this.actor.type.user.username;
if ( inputPath.startsWith('~/') ) {
return `/${this.user.username}/${inputPath.substring(2)}`;
return `/${username}/${inputPath.substring(2)}`;
}
if ( inputPath === '~' ) {
return `/${this.user.username}`;
return `/${username}`;
}
const refName = this.getReferenceUsed(inputPath);
if ( refName === null ) return inputPath;
@@ -127,9 +127,12 @@ module.exports = eggspress('/batch', {
if ( op_spec.op === 'write' ) return true;
return false;
}
if ( ! req.actor ) {
throw new Error('Actor is missing here');
}
const batch_exe = new BatchExecutor(x, {
log, errors,
user: req.user,
actor: req.actor,
});
// --- state
const pending_operations = [];