From 8c49ba2553ce6bee20eb5b6f2721bc80f639e98a Mon Sep 17 00:00:00 2001 From: KernelDeimos Date: Sun, 16 Jun 2024 01:54:14 -0400 Subject: [PATCH] feat: add share list to stat --- .../backend/src/filesystem/FSNodeContext.js | 54 +++++++++++++++-- .../src/services/auth/PermissionService.js | 59 +++++++++++++++++++ 2 files changed, 108 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/filesystem/FSNodeContext.js b/packages/backend/src/filesystem/FSNodeContext.js index a53ea06d..8061a315 100644 --- a/packages/backend/src/filesystem/FSNodeContext.js +++ b/packages/backend/src/filesystem/FSNodeContext.js @@ -25,6 +25,8 @@ const { Context } = require("../util/context"); const { MultiDetachable } = require("../util/listenerutil"); const { NodeRawEntrySelector } = require("./node/selectors"); const { DB_READ } = require("../services/database/consts"); +const { UserActorType } = require("../services/auth/Actor"); +const { PermissionUtil } = require("../services/auth/PermissionService"); /** * Container for information collected about a node @@ -387,13 +389,55 @@ module.exports = class FSNodeContext { * then, stores them on the `permissions` property * of the fsentry. * @param {bool} force fetch shares if they were already fetched - * - * @deprecated sharing will use user-to-user permissions */ async fetchShares (force) { - // NOOP: this was for legacy sharing functionality; - // this is being re-implemented with permissions - return; + if (this.entry.shares && ! force ) return; + + const actor = Context.get('actor'); + if ( ! actor ) { + this.entry.shares = { users: [], apps: [] }; + return; + } + + if ( ! (actor.type instanceof UserActorType) ) { + this.entry.shares = { users: [], apps: [] }; + return; + } + + const svc_permission = this.services.get('permission'); + + const permissions = + await svc_permission.query_issuer_permissions_by_prefix( + actor.type.user, `fs:${await this.get('uid')}:`); + + this.entry.shares = { users: [], apps: [] }; + + for ( const user_perm of permissions.users ) { + const access = + PermissionUtil.split(user_perm.permission).slice(-1)[0]; + this.entry.shares.users.push({ + user: { + uid: user_perm.user.uuid, + username: user_perm.user.username, + }, + access, + permission: user_perm.permission, + }); + } + + for ( const app_perm of permissions.apps ) { + const access = + PermissionUtil.split(app_perm.permission).slice(-1)[0]; + this.entry.shares.apps.push({ + app: { + icon: app_perm.app.icon, + uid: app_perm.app.uid, + name: app_perm.app.name, + }, + access, + permission: app_perm.permission, + }); + } } /** diff --git a/packages/backend/src/services/auth/PermissionService.js b/packages/backend/src/services/auth/PermissionService.js index c4a9daee..913e2729 100644 --- a/packages/backend/src/services/auth/PermissionService.js +++ b/packages/backend/src/services/auth/PermissionService.js @@ -587,6 +587,13 @@ class PermissionService extends BaseService { /** * List the users that have any permissions granted to the * specified user. + * + * This is a "flat" (non-cascading) view. + * + * Use History: + * - This was written for use in ll_listusers to display + * home directories of users that shared files with the + * current user. */ async list_user_permission_issuers (user) { const rows = await this.db.read( @@ -602,6 +609,58 @@ class PermissionService extends BaseService { return users; } + + /** + * List the permissions that the specified actor (the "issuer") + * has granted to all other users which have some specified + * prefix in the permission key (ex: "fs:FILE-UUID") + * + * Note that if the prefix contains a literal '%' character + * the behavior may not be as expected. + * + * This is a "flat" (non-cascading) view. + * + * Use History: + * - This was written for FSNodeContext.fetchShares to query + * all the "shares" associated with a file. + */ + async query_issuer_permissions_by_prefix (issuer, prefix) { + const user_perms = await this.db.read( + 'SELECT DISTINCT holder_user_id, permission ' + + 'FROM `user_to_user_permissions` ' + + 'WHERE issuer_user_id = ? ' + + 'AND permission LIKE ?', + [issuer.id, prefix + '%'], + ); + + const app_perms = await this.db.read( + 'SELECT DISTINCT app_id, permission ' + + 'FROM `user_to_app_permissions` ' + + 'WHERE user_id = ? ' + + 'AND permission LIKE ?', + [issuer.id, prefix + '%'], + ); + + const retval = { users: [], apps: [] }; + + for ( const user_perm of user_perms ) { + const { holder_user_id, permission } = user_perm; + retval.users.push({ + user: await get_user({ id: holder_user_id }), + permission, + }); + } + + for ( const app_perm of app_perms ) { + const { app_id, permission } = app_perm; + retval.apps.push({ + app: await get_app({ id: app_id }), + permission, + }); + } + + return retval; + } get_parent_permissions (permission) { const parent_perms = [];