diff --git a/src/backend/src/services/auth/PermissionService.js b/src/backend/src/services/auth/PermissionService.js index b5f2d39f4..9195117ed 100644 --- a/src/backend/src/services/auth/PermissionService.js +++ b/src/backend/src/services/auth/PermissionService.js @@ -77,14 +77,15 @@ class PermissionRewriter { * The actor and permission are passed to checker({ actor, permission }) as an object. */ class PermissionImplicator { - static create ({ id, matcher, checker }) { - return new PermissionImplicator({ id, matcher, checker }); + static create ({ id, matcher, checker, ...options }) { + return new PermissionImplicator({ id, matcher, checker, options }); } - constructor ({ id, matcher, checker }) { + constructor ({ id, matcher, checker, options }) { this.id = id; this.matcher = matcher; this.checker = checker; + this.options = options; } matches (permission) { diff --git a/src/backend/src/structured/sequence/scan-permission.js b/src/backend/src/structured/sequence/scan-permission.js index be3e93ae1..573574952 100644 --- a/src/backend/src/structured/sequence/scan-permission.js +++ b/src/backend/src/structured/sequence/scan-permission.js @@ -75,6 +75,42 @@ module.exports = new Sequence([ } a.set('permission_options', permission_options.flat()); }, + async function handle_shortcuts (a) { + const reading = a.get('reading'); + const { actor, permission_options } = a.values(); + + const _permission_implicators = a.iget('_permission_implicators'); + + for ( const permission of permission_options ) + for ( const implicator of _permission_implicators ) { + if ( ! implicator.options?.shortcut ) continue; + + // TODO: is it possible to DRY this with concurrent implicators in permission-scanners.js? + if ( ! implicator.matches(permission) ) { + continue; + } + const implied = await implicator.check({ + actor, + permission, + }); + if ( implied ) { + reading.push({ + $: 'option', + permission, + source: 'implied', + by: implicator.id, + data: implied, + ...((!!actor.type.user) + ? { holder_username: actor.type.user.username } + : {}), + }); + if ( implicator.options?.shortcut ) { + a.stop(); + return; + } + } + } + }, async function run_scanners (a) { const scanners = PERMISSION_SCANNERS; const ps = []; diff --git a/src/backend/src/unstructured/permission-scanners.js b/src/backend/src/unstructured/permission-scanners.js index 6e3c2b88e..907c16561 100644 --- a/src/backend/src/unstructured/permission-scanners.js +++ b/src/backend/src/unstructured/permission-scanners.js @@ -61,6 +61,8 @@ const PERMISSION_SCANNERS = [ for ( const permission of permission_options ) for ( const implicator of _permission_implicators ) { + if ( implicator.options?.shortcut ) continue; + if ( ! implicator.matches(permission) ) { continue; } @@ -75,7 +77,14 @@ const PERMISSION_SCANNERS = [ source: 'implied', by: implicator.id, data: implied, + ...((!!actor.type.user) + ? { holder_username: actor.type.user.username } + : {}), }); + if ( implicator.options?.shortcut ) { + a.stop(); + return; + } } } }