mirror of
https://github.com/HeyPuter/puter.git
synced 2025-01-23 22:40:20 +08:00
doc: document permission scanning
This commit is contained in:
parent
4e47c8bf9d
commit
651076a39b
@ -37,10 +37,16 @@ const implicit_user_permissions = {
|
||||
|
||||
|
||||
/**
|
||||
* The PermissionService class manages the core functionality for handling permissions within the Puter ecosystem.
|
||||
* It provides methods for granting, revoking, and checking permissions for various entities such as users and applications.
|
||||
* This service interacts with the database to store, retrieve, and audit permission changes, and also handles complex permission logic like rewriting, implication, and explosion of permissions.
|
||||
*/
|
||||
* Permission rewriters are used to map one set of permission strings to another.
|
||||
* These are invoked during permission scanning and when permissions are granted or revoked.
|
||||
*
|
||||
* For example, Puter's filesystem uses this to map 'fs:/some/path:mode' to
|
||||
* 'fs:SOME-UUID:mode'.
|
||||
*
|
||||
* A rewriter is constructed using the static method PermissionRewriter.create({ matcher, rewriter }).
|
||||
* The matcher is a function that takes a permission string and returns true if the rewriter should be applied.
|
||||
* The rewriter is a function that takes a permission string and returns the rewritten permission string.
|
||||
*/
|
||||
class PermissionRewriter {
|
||||
static create ({ id, matcher, rewriter }) {
|
||||
return new PermissionRewriter({ id, matcher, rewriter });
|
||||
@ -70,11 +76,17 @@ class PermissionRewriter {
|
||||
|
||||
|
||||
/**
|
||||
* The PermissionImplicator class is used to manage implicit permissions.
|
||||
* It defines methods to match and check if a given permission is implicitly granted to an actor.
|
||||
* @class
|
||||
* @name PermissionImplicator
|
||||
*/
|
||||
* Permission implicators are used to manage implicit permissions.
|
||||
* It defines a method to check if a given permission is implicitly granted to an actor.
|
||||
*
|
||||
* For example, Puter's filesystem uses this to grant permission to a file if the specified
|
||||
* 'actor' is the owner of the file.
|
||||
*
|
||||
* An implicator is constructed using the static method PermissionImplicator.create({ matcher, checker }).
|
||||
* `matcher is a function that takes a permission string and returns true if the implicator should be applied.
|
||||
* `checker` is a function that takes an actor and a permission string and returns true if the permission is implied.
|
||||
* 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 });
|
||||
@ -108,12 +120,17 @@ class PermissionImplicator {
|
||||
|
||||
|
||||
/**
|
||||
* The PermissionExploder class is responsible for expanding permissions into a set of more granular permissions.
|
||||
* It uses a matcher function to determine if a permission should be exploded and an exploder function to perform the expansion.
|
||||
* This class is part of the permission management system, allowing for dynamic and complex permission structures.
|
||||
*
|
||||
* @class PermissionExploder
|
||||
*/
|
||||
* Permission exploders are used to map any permission to a list of permissions
|
||||
* which are considered to imply the specified permission.
|
||||
*
|
||||
* It uses a matcher function to determine if a permission should be exploded
|
||||
* and an exploder function to perform the expansion.
|
||||
*
|
||||
* The exploder is constructed using the static method PermissionExploder.create({ matcher, explode }).
|
||||
* The `matcher` is a function that takes a permission string and returns true if the exploder should be applied.
|
||||
* The `explode` is a function that takes an actor and a permission string and returns a list of implied permissions.
|
||||
* The actor and permission are passed to explode({ actor, permission }) as an object.
|
||||
*/
|
||||
class PermissionExploder {
|
||||
static create ({ id, matcher, exploder }) {
|
||||
return new PermissionExploder({ id, matcher, exploder });
|
||||
@ -952,14 +969,26 @@ class PermissionService extends BaseService {
|
||||
}
|
||||
|
||||
|
||||
register_rewriter (translator) {
|
||||
if ( ! (translator instanceof PermissionRewriter) ) {
|
||||
throw new Error('translator must be a PermissionRewriter');
|
||||
/**
|
||||
* Register a permission rewriter. For details see the documentation on the
|
||||
* PermissionRewriter class.
|
||||
*
|
||||
* @param {PermissionRewriter} rewriter - The permission rewriter to register
|
||||
*/
|
||||
register_rewriter (rewriter) {
|
||||
if ( ! (rewriter instanceof PermissionRewriter) ) {
|
||||
throw new Error('rewriter must be a PermissionRewriter');
|
||||
}
|
||||
|
||||
this._permission_rewriters.push(translator);
|
||||
this._permission_rewriters.push(rewriter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a permission implicator. For details see the documentation on the
|
||||
* PermissionImplicator class.
|
||||
*
|
||||
* @param {PermissionImplicator} implicator - The permission implicator to register
|
||||
*/
|
||||
register_implicator (implicator) {
|
||||
if ( ! (implicator instanceof PermissionImplicator) ) {
|
||||
throw new Error('implicator must be a PermissionImplicator');
|
||||
@ -968,6 +997,12 @@ class PermissionService extends BaseService {
|
||||
this._permission_implicators.push(implicator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a permission exploder. For details see the documentation on the
|
||||
* PermissionExploder class.
|
||||
*
|
||||
* @param {PermissionExploder} exploder - The permission exploder to register
|
||||
*/
|
||||
register_exploder (exploder) {
|
||||
if ( ! (exploder instanceof PermissionExploder) ) {
|
||||
throw new Error('exploder must be a PermissionExploder');
|
||||
|
@ -21,6 +21,14 @@ class VirtualGroupService extends BaseService {
|
||||
this.membership_implicators_ = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a function that reports one or more groups that an actor
|
||||
* should be considered a member of.
|
||||
*
|
||||
* @note this only applies to virtual groups, not persistent groups.
|
||||
*
|
||||
* @param {*} implicator
|
||||
*/
|
||||
register_membership_implicator (implicator) {
|
||||
this.membership_implicators_.push(implicator);
|
||||
}
|
||||
|
@ -10,11 +10,7 @@
|
||||
const remove_paths_through_user = ({ reading, user }) => {
|
||||
const no_cycle_reading = [];
|
||||
|
||||
for ( let i = 0 ; i < reading.length ; i++ ) {
|
||||
const node = reading[i];
|
||||
|
||||
console.log('checking node...', node);
|
||||
|
||||
for ( const node of reading ) {
|
||||
if ( node.$ === 'path' ) {
|
||||
if (
|
||||
node.issuer_username === user.username
|
||||
@ -33,14 +29,11 @@ const remove_paths_through_user = ({ reading, user }) => {
|
||||
no_cycle_reading.push(node);
|
||||
}
|
||||
|
||||
console.log('\x1B[36;1m ====', reading.length - no_cycle_reading.length, 'nodes filtered out ====\x1B[0m');
|
||||
|
||||
return no_cycle_reading;
|
||||
};
|
||||
|
||||
const reading_has_terminal = ({ reading }) => {
|
||||
for ( let i = 0 ; i < reading.length ; i++ ) {
|
||||
const node = reading[i];
|
||||
for ( const node of reading ) {
|
||||
if ( node.has_terminal ) {
|
||||
return true;
|
||||
}
|
||||
|
@ -14,9 +14,26 @@ const { reading_has_terminal } = require("./permission-scan-lib");
|
||||
"Ctrl+K, Ctrl+J" or "⌘K, ⌘J";
|
||||
*/
|
||||
|
||||
/**
|
||||
* Permission Scanners
|
||||
* @usedBy scan-permission.js
|
||||
*
|
||||
* These are all the different ways an entity (user or app) can have a permission.
|
||||
* This list of scanners is iterated over and invoked by scan-permission.js.
|
||||
*
|
||||
* Each `scan` function is passed a sequence scope. The instance attached to the
|
||||
* sequence scope is PermissionService itself, so any `a.iget('something')` is
|
||||
* accessing the member 'something' of the PermissionService instance.
|
||||
*/
|
||||
const PERMISSION_SCANNERS = [
|
||||
{
|
||||
name: 'implied',
|
||||
documentation: `
|
||||
Scans for permissions that are implied by "permission implicators".
|
||||
|
||||
Permission implicators are added by other services via
|
||||
PermissionService's \`register_implicator\` method.
|
||||
`,
|
||||
async scan (a) {
|
||||
const reading = a.get('reading');
|
||||
const { actor, permission_options } = a.values();
|
||||
@ -46,6 +63,9 @@ const PERMISSION_SCANNERS = [
|
||||
},
|
||||
{
|
||||
name: 'user-user',
|
||||
documentation: `
|
||||
User-to-User permissions are permission granted form one user to another.
|
||||
`,
|
||||
async scan (a) {
|
||||
const { reading, actor, permission_options, state } = a.values();
|
||||
if ( !(actor.type instanceof UserActorType) ) {
|
||||
@ -96,7 +116,6 @@ const PERMISSION_SCANNERS = [
|
||||
|
||||
if ( should_continue ) continue;
|
||||
|
||||
// const issuer_perm = await this.check(issuer_actor, row.permission);
|
||||
const issuer_reading = await a.icall(
|
||||
'scan', issuer_actor, row.permission, undefined, state);
|
||||
|
||||
@ -117,6 +136,13 @@ const PERMISSION_SCANNERS = [
|
||||
},
|
||||
{
|
||||
name: 'hc-user-group-user',
|
||||
documentation: `
|
||||
These are user-to-group permissions that are defined in the
|
||||
hardcoded_user_group_permissions section of "hardcoded-permissions.js".
|
||||
|
||||
These are typically used to grant permissions from the system user to
|
||||
the default groups: "admin", "user", and "temp".
|
||||
`,
|
||||
async scan (a) {
|
||||
const { reading, actor, permission_options } = a.values();
|
||||
if ( !(actor.type instanceof UserActorType) ) {
|
||||
@ -167,6 +193,11 @@ const PERMISSION_SCANNERS = [
|
||||
},
|
||||
{
|
||||
name: 'user-group-user',
|
||||
documentation: `
|
||||
This scans for permissions that are granted to the user because a
|
||||
group they are a member of was granted this permission by another
|
||||
user.
|
||||
`,
|
||||
async scan (a) {
|
||||
const { reading, actor, permission_options } = a.values();
|
||||
if ( !(actor.type instanceof UserActorType) ) {
|
||||
@ -223,6 +254,16 @@ const PERMISSION_SCANNERS = [
|
||||
},
|
||||
{
|
||||
name: 'user-virtual-group-user',
|
||||
documentation: `
|
||||
These are groups with computed membership. Permissions are not granted
|
||||
to these groups; instead the groups are defined with a list of
|
||||
permissions that are granted to the group members.
|
||||
|
||||
Services can define "virtual groups" via the "virtual-group" service.
|
||||
Services can also register membership implicators for virtual groups
|
||||
which will compute on the fly whether or not an actor should be
|
||||
considered a member of the group.
|
||||
`,
|
||||
async scan (a) {
|
||||
const svc_virtualGroup = await a.iget('services').get('virtual-group');
|
||||
const { reading, actor, permission_options } = a.values();
|
||||
@ -248,6 +289,10 @@ const PERMISSION_SCANNERS = [
|
||||
},
|
||||
{
|
||||
name: 'user-app',
|
||||
documentation: `
|
||||
If the actor is an app, this scans for permissions granted to the app
|
||||
because the user has the permission and granted it to the app.
|
||||
`,
|
||||
async scan (a) {
|
||||
const { reading, actor, permission_options } = a.values();
|
||||
if ( !(actor.type instanceof AppUnderUserActorType) ) {
|
||||
|
Loading…
Reference in New Issue
Block a user