From 3290440f4bf7a263f37bc5233565f8fec146f17b Mon Sep 17 00:00:00 2001 From: KernelDeimos Date: Wed, 14 May 2025 17:59:01 -0400 Subject: [PATCH] dev: document auth and permissions --- src/backend/doc/A-and-A/auth.md | 63 +++++++++ src/backend/doc/A-and-A/permission.md | 179 ++++++++++++++++++++++++++ 2 files changed, 242 insertions(+) create mode 100644 src/backend/doc/A-and-A/auth.md create mode 100644 src/backend/doc/A-and-A/permission.md diff --git a/src/backend/doc/A-and-A/auth.md b/src/backend/doc/A-and-A/auth.md new file mode 100644 index 000000000..5c88949b3 --- /dev/null +++ b/src/backend/doc/A-and-A/auth.md @@ -0,0 +1,63 @@ +# Authentication Documentation + +## Concepts + +### Actor + +An "Actor" is an entity that can be authenticated. The following types of +actors are currently supported by Puter: +- **UserActorType** - represents a user and is identified by a user's UUID +- **AppUnderUserActorType** - represents an app running in an iframe from a + `puter.site` domain or another origin and is identified by a user's UUID + and an app's UUID together. +- **AccessTokenActorType** - not widely currently, but Puter supports + a concept called "access tokens". Any user can create an access token and + then grant any permissions they want to that access token. The access + token will have those permissions granted provided that the user who + created the access token does as well (via permission cascade) +- **SiteActorType** - represents a `puter.site` website accessing Puter's API. +- **SystemActorType** - internal representation of the actor during a privileged + backend operation. This actor cannot be authenticated in a request. + This actor does not represent the `system` user. + +### Token + +- **Legacy** - legacy tokens result in an error response +- **Session** - this token is a JWT with a claim for the UUID of an entry in + server memory or the database that we call a "session". This entry associates + the token to a user and some metadata for security auditing purposes. + Revoking the session entry disables the token. + This type of token resolves to an actor with **UserActorType**. +- **AppUnderUser** - this token is a JWT with a claim for an app UUID and a + claim for a session UUID. + Revoking the session entry disables the token. + This type of token resolves to an actor with **AppUnderUserActorType**. +- **AccessToken** - this token is a JWT with three claims: + - A session UUID + - An optional App UUID + - A UUID representing the access token for permission associations + The session or session+app creates a **UserActorType** or + **AppUnderUserActorType** actor respectively. This actor is called + the "authorizor". This actor is aggregated by an **AccessTokenActorType** + actor which becomes the effective actor for a request. +- **ActorSite** - this token is a JWT with a claim for a site UID. + The site UID is associated with an origin, generally a `puter.site` + subdomain. + +## Components + +### Auth Middleware + +There have so far been three iterations of the authentication middleware: +- `src/backend/src/middleware/auth.js` +- `src/backend/src/middleware/auth2.js` +- `src/backend/src/middleware/configurable_auth.js` + +The newest implementation is `configurable_auth` and eventually the other +two will be removed. There is no legacy behavior involved: +- `auth` was rewritten to use `auth2` +- `auth2` was rewritten to use `configurable_auth` + +The `configurable_auth` middleware accepts a parameter that can be specified +if an endpoint is optionally authenticated. In this case, the request's +`actor` will be `undefined` if there was no information for authentication. diff --git a/src/backend/doc/A-and-A/permission.md b/src/backend/doc/A-and-A/permission.md new file mode 100644 index 000000000..ee4b758b0 --- /dev/null +++ b/src/backend/doc/A-and-A/permission.md @@ -0,0 +1,179 @@ +# Permission Documentation + +## Concepts + +### Permission + +A permission is a string composed of colon-delimited components which identifies +a resource or functionality to which access can be controlled. + +For example, `fs:e8ac2973-287b-4121-a75d-7e0619eb8e87:read` is a permission which +represents reading the file or directory with UUID `e8ac2973-287b-4121-a75d-7e0619eb8e87`. + +### Group + +A group has an owner and several member users. An owner decides what users are in the +group and what users are not. Any user can grant permissions to the group. + +### Granting & Revoking + +Granting is the act of creating a permission association to a user or group from +the current user. A permission association also holds an object called `extra` +which holds additional claims associated with the permission association. +These are arbitrary and can be used in any way by the subsystem or extension that +is checking the permission. `extra` is usually just an empty object. + +Revoking is the act of removing a permission association. + +### Permission Options + +Permission options are an association between a permission and an actor that can not +be revoked by another actor. For example, the user `ed` always has access to files +under `/ed`. The user `system` always has all permissions granted. These can also be +considered "terminals" because they will always be at +the end of a pathway through granted permissions between users. +This are also called "implied" permissions because they are implied by the system. + +### Permission Pathways + +A permission pathway is the path between users or groups that leads to a permission. + +For example, `ed` can grant the permission `a:b` to `fred`, then `fred` can grant +that permission to the group `cool_group`, and then `alice` may be in the group +`cool_group`. Assuming `ed` holds the implied permission `a:b`, a permission path +exists between `alice` and `ed` via `cool_group` and `fred`: + +``` +alice <--<> cool_group <-- fred <-- ed (a:b) +``` + +If any link in this chain breaks the permission is effectively revoked from `alice` +unless there is another pathway leading to a valid permission option for `a:b`. + +### Reading - AKA Permission Scan Result + +A permission reading is a JSON-serializable object which contains all the pathways +a specified actor has to permissions options matching the specified permission strings. + +The following is an example reading for the user `ed3` on the permission +`fs:24729b88-a4c5-4990-ad4e-272b87895732:read`. This file is owned by the +user `admin` who shared it with `ed3`. + +``` +[ + { + "$": "explode", + "from": "fs:24729b88-a4c5-4990-ad4e-272b87895732:read", + "to": [ + "fs:24729b88-a4c5-4990-ad4e-272b87895732:read", + "fs:24729b88-a4c5-4990-ad4e-272b87895732:write", + "fs:24729b88-a4c5-4990-ad4e-272b87895732", + "fs" + ] + }, + { + "$": "path", + "via": "user", + "has_terminal": true, + "permission": "fs:24729b88-a4c5-4990-ad4e-272b87895732:read", + "data": {}, + "holder_username": "ed3", + "issuer_username": "admin", + "reading": [ + { + "$": "explode", + "from": "fs:24729b88-a4c5-4990-ad4e-272b87895732:read", + "to": [ + "fs:24729b88-a4c5-4990-ad4e-272b87895732:read", + "fs:24729b88-a4c5-4990-ad4e-272b87895732:write", + "fs:24729b88-a4c5-4990-ad4e-272b87895732", + "fs" + ] + }, + { + "$": "option", + "permission": "fs:24729b88-a4c5-4990-ad4e-272b87895732:read", + "source": "implied", + "by": "is-owner", + "data": {} + }, + { + "$": "option", + "permission": "fs:24729b88-a4c5-4990-ad4e-272b87895732:write", + "source": "implied", + "by": "is-owner", + "data": {} + }, + { + "$": "option", + "permission": "fs:24729b88-a4c5-4990-ad4e-272b87895732", + "source": "implied", + "by": "is-owner", + "data": {} + }, + { + "$": "time", + "value": 19 + } + ] + }, + { + "$": "time", + "value": 20 + } +] +``` + +Each object in the reading has a property named `$` which is the type for the object. +The most fundamental types for permission readings are `path` and `option`. A path +always contains another reading, which contains more paths or options. An option +specifies the permission string, the name of the rule that granted the permission, +and a data object which may hold additional claims. + +Readings begin with an `explode` if there are multiple strings that may grant the +permission. + +Readings end with a `time` that repots how long the reading took to help manage +the potential performance impact of complex permission graphs. + +## Permission Service + +### check(actor, permissions) + +Returns true if the current actor has a path to any permission options matching +any of the permission strings specified by `permissions`. This is done by invoking +`scan()` and returning `true` if there are more than 0 permission options. + +### scan(actor, permissions) + +Returns a "reading". A permission reading is a JSON-serializable structure. +Readings are described above. + +## Permission Scan Sequence + +The `scan()` method of **PermissionService** invokes the permission scan sequence. +The permission scan sequence is a [Sequence](https://github.com/HeyPuter/puter/blob/0e0bfd6d7c92eed5080518a099c9a66a2f2dc9ec/src/backend/src/codex/Sequence.js) +that is defined in [scan-permission.js](src/backend/src/structured/sequence/scan-permission.js). +It invokes many "permission scanners" which are defined in +[permission-scanners.js](src/backend/src/unstructured/permission-scanners.js) + +The Permission Scan Sequence is as follows: +- `grant_if_system` - if system user, push an option to the reading and stop +- `rewrite_permission` - process the permission through any permission string + rewriters that were registered with PermissionService by other services. + For example, since path-based file permissions aren't currently supported + the FilesystemService regsiters a rewriter that converts any `fs:/` + permission into a corresponding UUID permission. +- `explode_permission` - break the permission into multiple permissions + than are sufficient to grant the permission being scanned. For example if + there are multiple components, like `a.b.c`, having either permission `a.b` or + `a` granted implis having `a.b.c` granted. Other services can also register + "permission exploders" which handle non-hierarchical cases such as + `fs:AAAA:write` implying `fs:AAAA:read`. +- `run_scanners` - run the permission scanners. + +Each permission scanner has a name, documentation text, and a scan function. +The scan function has access to the scan sequence's context and can push +objects onto the permission reading. + +For information on individual scanners, refer to permission-scanners.js.