mirror of
https://github.com/HeyPuter/puter.git
synced 2026-05-04 08:30:39 +00:00
dev: document auth and permissions
This commit is contained in:
@@ -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.
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user