fix: fix permission cascade properly this time

This commit is contained in:
KernelDeimos 2024-06-18 03:56:19 -04:00
parent 10cbf08233
commit de5886698e
3 changed files with 99 additions and 29 deletions

View File

@ -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;
},
}));
}

View File

@ -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 ) {

View File

@ -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,
};