Skip to content

Commit 31d4eb0

Browse files
committed
feat: implicit access from apps to shared appdata dirs
1 parent a2a10b9 commit 31d4eb0

File tree

3 files changed

+49
-11
lines changed

3 files changed

+49
-11
lines changed

packages/backend/src/filesystem/FSNodeContext.js

+15-6
Original file line numberDiff line numberDiff line change
@@ -165,17 +165,26 @@ module.exports = class FSNodeContext {
165165
return ! this.entry.parent_uid;
166166
}
167167

168-
async getUserPart () {
169-
if ( this.isRoot ) return;
168+
async getPathComponents () {
169+
if ( this.isRoot ) return [];
170170

171171
let path = await this.get('path');
172172
if ( path.startsWith('/') ) path = path.slice(1);
173-
const components = path.split('/');
174-
const userpart = components[0];
175-
176-
return userpart;
173+
return path.split('/');
174+
}
175+
176+
async getUserPart () {
177+
if ( this.isRoot ) return;
178+
const components = await this.getPathComponents();
179+
return components[0];
177180
}
178181

182+
async getPathSize () {
183+
if ( this.isRoot ) return;
184+
const components = await this.getPathComponents();
185+
return components.length;
186+
}
187+
179188
async exists (fetch_options = {}) {
180189
await this.fetchEntry();
181190
if ( ! this.found ) {

packages/backend/src/filesystem/hl_operations/hl_write.js

+16-4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const { RootNodeSelector, NodePathSelector } = require("../node/selectors");
3131
const { is_valid_node_name } = require("../validation");
3232
const { HLFilesystemOperation } = require("./definitions");
3333
const { MkTree } = require("./hl_mkdir");
34+
const { Actor } = require("../../services/auth/Actor");
3435

3536
class WriteCommonTrait {
3637
install_in_instance (instance) {
@@ -186,10 +187,6 @@ class HLWrite extends HLFilesystemOperation {
186187
throw APIError.create('cannot_write_to_root');
187188
}
188189

189-
if ( values.user && ! await chkperm(await parent.get('entry'), values.user.id, 'write') ) {
190-
throw APIError.create('forbidden');
191-
}
192-
193190
try {
194191
// old validator is kept here to avoid changing the
195192
// error messages; eventually is_valid_node_name
@@ -222,6 +219,21 @@ class HLWrite extends HLFilesystemOperation {
222219
if ( values.offset !== undefined && ! dest_exists ) {
223220
throw APIError.create('offset_without_existing_file');
224221
}
222+
223+
// The correct ACL check here depends on context.
224+
// ll_write checks ACL, but we need to shortcut it here
225+
// or else we might send the user too much information.
226+
{
227+
const node_to_check =
228+
( dest_exists && overwrite && ! dedupe_name )
229+
? destination : parent;
230+
231+
const actor = values.actor ?? Actor.adapt(values.user);
232+
const svc_acl = context.get('services').get('acl');
233+
if ( ! await svc_acl.check(actor, node_to_check, 'write') ) {
234+
throw await svc_acl.get_safe_acl_error(actor, node_to_check, 'write');
235+
}
236+
}
225237

226238
if ( dest_exists ) {
227239
console.log('DESTINATION EXISTS', dedupe_name)

packages/backend/src/services/auth/ACLService.js

+18-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ class ACLService extends BaseService {
7878
return true;
7979
}
8080
}
81-
81+
8282
// app-under-user only works if the user also has permission
8383
if ( actor.type instanceof AppUnderUserActorType ) {
8484
const user_actor = new Actor({
@@ -88,6 +88,23 @@ class ACLService extends BaseService {
8888

8989
if ( ! user_perm ) return false;
9090
}
91+
92+
// Hard rule: if app-under-user is accessing appdata directory
93+
// under a **different user**, allow,
94+
// IFF that appdata directory is shared with user
95+
// (by "user also has permission" check above)
96+
if (await (async () => {
97+
if ( ! (actor.type instanceof AppUnderUserActorType) ) {
98+
return false;
99+
}
100+
if ( await fsNode.getUserPart() === actor.type.user.username ) {
101+
return false;
102+
}
103+
const components = await fsNode.getPathComponents();
104+
if ( components[1] !== 'AppData' ) return false;
105+
if ( components[2] !== actor.type.app.uid ) return false;
106+
return true;
107+
})()) return true;
91108

92109
const svc_permission = await context.get('services').get('permission');
93110

0 commit comments

Comments
 (0)