Просмотр исходного кода

fix: fix permission cascade properly this time

KernelDeimos 11 месяцев назад
Родитель
Сommit
de5886698e

+ 20 - 24
packages/backend/src/filesystem/FilesystemService.js

@@ -34,7 +34,7 @@ const APIError = require('../api/APIError.js');
 const { LLMkdir } = require('./ll_operations/ll_mkdir.js');
 const { LLCWrite, LLOWrite } = require('./ll_operations/ll_write.js');
 const { LLCopy } = require('./ll_operations/ll_copy.js');
-const { PermissionUtil, PermissionRewriter, PermissionImplicator } = require('../services/auth/PermissionService.js');
+const { PermissionUtil, PermissionRewriter, PermissionImplicator, PermissionExploder } = require('../services/auth/PermissionService.js');
 const { DB_WRITE } = require("../services/database/consts");
 const { UserActorType } = require('../services/auth/Actor');
 const { get_user } = require('../helpers');
@@ -165,38 +165,34 @@ class FilesystemService extends AdvancedBase {
                 return undefined;
             },
         }));
-        svc_permission.register_implicator(PermissionImplicator.create({
+        svc_permission.register_exploder(PermissionExploder.create({
             matcher: permission => {
-                return permission.startsWith('fs:');
+                return permission.startsWith('fs:') &&
+                    PermissionUtil.split(permission).length >= 3;
             },
-            checker: async ({ actor, permission, recurse }) => {
+            exploder: async ({ permission }) => {
+                const permissions = [permission];
                 const parts = PermissionUtil.split(permission);
-                if ( parts.length < 3 ) return undefined;
 
                 const specified_mode = parts[2];
                 
-                const mode = {
-                    write: 'read',
-                    read: 'list',
-                    list: 'see',
-                }[specified_mode];
+                const rules = {
+                    see: ['list', 'read', 'write'],
+                    list: ['read', 'write'],
+                    read: ['write'],
+                };
                 
-                if ( ! mode ) return undefined;
-
-                const perm = await recurse(actor,
-                    PermissionUtil.join(
-                        parts[0],
-                        parts[1],
-                        mode,
-                        ...parts.slice(3),
-                    )
-                )
-                if ( perm ) {
-                    console.log('RETURNING IT!', perm);
-                    return perm;
+                if ( rules.hasOwnProperty(specified_mode) ) {
+                    permissions.push(...rules[specified_mode].map(
+                        mode => PermissionUtil.join(
+                            parts[0], parts[1],
+                            mode,
+                            ...parts.slice(3),
+                        )
+                    ));
                 }
                 
-                return undefined;
+                return permissions;
             },
         }));
     }

+ 2 - 1
packages/backend/src/services/auth/ACLService.js

@@ -108,7 +108,8 @@ class ACLService extends BaseService {
 
         const svc_permission = await context.get('services').get('permission');
 
-        const modes = this._higher_modes(mode);
+        // const modes = this._higher_modes(mode);
+        const modes = [mode];
         let perm_fsNode = fsNode;
         while ( ! await perm_fsNode.get('is-root') ) {
             for ( const mode of modes ) {

+ 77 - 4
packages/backend/src/services/auth/PermissionService.js

@@ -135,6 +135,32 @@ class PermissionImplicator {
     }
 }
 
+class PermissionExploder {
+    static create ({ id, matcher, exploder }) {
+        return new PermissionExploder({ id, matcher, exploder });
+    }
+
+    constructor ({ id, matcher, exploder }) {
+        this.id = id;
+        this.matcher = matcher;
+        this.exploder = exploder;
+    }
+
+    matches (permission) {
+        return this.matcher(permission);
+    }
+
+    /**
+     * Check if the permission is implied by this implicator
+     * @param  {Actor} actor
+     * @param  {string} permission
+     * @returns 
+     */
+    async explode ({ actor, permission }) {
+        return await this.exploder({ actor, permission });
+    }
+}
+
 class PermissionUtil {
     static unescape_permission_component (component) {
         let unescaped_str = '';
@@ -194,6 +220,7 @@ class PermissionService extends BaseService {
 
         this._permission_rewriters = [];
         this._permission_implicators = [];
+        this._permission_exploders = [];
     }
 
     async _rewrite_permission (permission) {
@@ -220,6 +247,17 @@ class PermissionService extends BaseService {
             actor: actor.uid,
             permission,
         });
+        
+        // for ( const implicator of this._permission_implicators ) {
+        //     if ( ! implicator.matches(permission) ) continue;
+        //     const implied = await implicator.check({
+        //         actor,
+        //         permission,
+        //         recurse: this.check.bind(this),
+        //     });
+        //     if ( implied ) return implied;
+        // }
+
         // For now we're only checking driver permissions, and users have all of them
         if ( actor.type instanceof UserActorType ) {
             return await this.check_user_permission(actor, permission);
@@ -240,8 +278,17 @@ class PermissionService extends BaseService {
             // NEXT:
             const app_uid = actor.type.app.uid;
             const user_actor = actor.get_related_actor(UserActorType);
-            const user_has_permission = await this.check_user_permission(user_actor, permission);
+            // const user_has_permission = await this.check_user_permission(user_actor, permission);
+            const user_has_permission = await this.check__(
+                user_actor, permission,
+            );
             if ( ! user_has_permission ) return undefined;
+            
+            // This was a useful log so I'm keeping it here
+            // console.log('\x1B[36;1m>=== THIS IS HERE ===<\x1B[0m',
+            //     app_uid,
+            //     permission,
+            // )
 
             return await this.check_user_app_permission(actor, app_uid, permission);
         }
@@ -252,7 +299,8 @@ class PermissionService extends BaseService {
     // TODO: context meta for cycle detection
     async check_user_permission (actor, permission) {
         permission = await this._rewrite_permission(permission);
-        const parent_perms = this.get_parent_permissions(permission);
+        // const parent_perms = this.get_parent_permissions(permission);
+        const parent_perms = await this.get_higher_permissions(permission);
 
         // Check implicit permissions
         for ( const parent_perm of parent_perms ) {
@@ -329,7 +377,8 @@ class PermissionService extends BaseService {
         if ( ! app ) app = await get_app({ name: app_uid });
         const app_id = app.id;
 
-        const parent_perms = this.get_parent_permissions(permission);
+        // const parent_perms = this.get_parent_permissions(permission);
+        const parent_perms = await this.get_higher_permissions(permission);
 
         for ( const permission of parent_perms ) {
             // Check hardcoded permissions
@@ -348,7 +397,7 @@ class PermissionService extends BaseService {
                 return implicit_permissions[permission];
             }
         }
-
+        
         // My biggest gripe with SQL is doing string manipulation for queries.
         // If the grammar for SQL was simpler we could model it, write this as
         // data, and even implement macros for common patterns.
@@ -665,6 +714,21 @@ class PermissionService extends BaseService {
         
         return retval;
     }
+    
+    async get_higher_permissions (permission) {
+        const higher_perms = [];
+        const parent_perms = this.get_parent_permissions(permission);
+        for ( const parent_perm of parent_perms ) {
+            for ( const exploder of this._permission_exploders ) {
+                if ( ! exploder.matches(parent_perm) ) continue;
+                const perms = await exploder.explode({
+                    permission: parent_perm,
+                });
+                higher_perms.push(...perms);
+            }
+        }
+        return higher_perms;
+    }
 
     get_parent_permissions (permission) {
         const parent_perms = [];
@@ -699,6 +763,14 @@ class PermissionService extends BaseService {
         this._permission_implicators.push(implicator);
     }
 
+    register_exploder (exploder) {
+        if ( ! (exploder instanceof PermissionExploder) ) {
+            throw new Error('exploder must be a PermissionExploder');
+        }
+
+        this._permission_exploders.push(exploder);
+    }
+
     _register_commands (commands) {
         commands.registerCommands('perms', [
             {
@@ -723,6 +795,7 @@ class PermissionService extends BaseService {
 module.exports = {
     PermissionRewriter,
     PermissionImplicator,
+    PermissionExploder,
     PermissionUtil,
     PermissionService,
 };