dev: document auth and permissions

This commit is contained in:
KernelDeimos
2025-05-14 17:59:01 -04:00
parent 0e0bfd6d7c
commit 3290440f4b
2 changed files with 242 additions and 0 deletions
+63
View File
@@ -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.
+179
View File
@@ -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.