anbaric 1.31.0 → 1.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/README.md CHANGED
@@ -26,6 +26,7 @@ What the framework gives you, one capability at a time.
26
26
  - [Documents and secrets](features/documents-and-secrets.md) — the JSON and secret stores
27
27
  - [The SQL store](features/sql-store.md) — a relational database for structured data
28
28
  - [Auditing](features/auditing.md) — the record of who changed what
29
+ - [Entitlements](features/entitlements.md) — what a user has been granted, checked per request
29
30
  - [Serving a web UI](features/serving-a-web-ui.md) — putting your data on a page
30
31
  - [The admin console and widgets](features/admin-console-and-widgets.md) — dashboards and plugins
31
32
  - [Deploying](features/deploying.md) — from laptop to Anbaric Cloud
@@ -35,6 +35,7 @@ defaulting to a local implementation otherwise).
35
35
  | `ANBARIC_SECRET_STORE_TYPE` | the secret store | in-memory (encrypted) |
36
36
  | `ANBARIC_SQL_STORE_TYPE` | the SQL store (`sqlite` / `cloud`\|`postgres`) | SQLite |
37
37
  | `ANBARIC_SESSION_RESOLVER_TYPE` | how `Human.fromSession` resolves sessions | in-memory |
38
+ | `ANBARIC_ENTITLEMENTS_TYPE` | how `hasEntitlement` is answered (`cloud` → platform) | permissive (always `true`) |
38
39
 
39
40
  ## Store configuration
40
41
 
@@ -10,6 +10,7 @@ import {
10
10
  Job, PropertyDefinition,
11
11
  Code, Human, SystemActor, Agent, OpenAIAgent, RemoteLLMAgenticAction,
12
12
  JsonStoreFactory, SecretStoreFactory, SqlStoreFactory,
13
+ registerEntitlement, hasEntitlement,
13
14
  } from "anbaric";
14
15
 
15
16
  import type {Actor, ActorType, JsonSchema} from "anbaric";
@@ -0,0 +1,79 @@
1
+ # Entitlements
2
+
3
+ Roles say what kind of user someone is. **Entitlements** say what a particular
4
+ user has been given: access to a beta feature, an export capability, a paid
5
+ tier. Your app declares the entitlements it cares about and asks whether the
6
+ signed-in user holds one; administrators hand them out from the console.
7
+
8
+ ## Declare what you check
9
+
10
+ Register each entitlement once, at startup. That makes it visible in the
11
+ console so it can be granted, and gives it a note explaining what it unlocks.
12
+
13
+ ```ts
14
+ import {registerEntitlement} from "anbaric";
15
+
16
+ await registerEntitlement("export", "Can export reports as CSV");
17
+ await registerEntitlement("beta", "Sees features still in preview");
18
+ ```
19
+
20
+ Registration is idempotent and scoped to your app - `export` in your app is a
21
+ different entitlement from `export` in another.
22
+
23
+ ## Check the signed-in user
24
+
25
+ `hasEntitlement` takes the incoming request (it reads the `anbaric_session`
26
+ cookie, exactly as `Human.fromSession` does) or a raw session token, resolves
27
+ the user, and answers for **your app**:
28
+
29
+ ```ts
30
+ import {hasEntitlement} from "anbaric";
31
+
32
+ server.on("request", async (request, response) => {
33
+ if (request.url === "/export") {
34
+ if (! await hasEntitlement(request, "export")) {
35
+ response.writeHead(403).end("You don't have the export entitlement");
36
+ return;
37
+ }
38
+ ...
39
+ }
40
+ });
41
+ ```
42
+
43
+ A request with no session, or one the platform can't resolve, is simply not
44
+ entitled - `hasEntitlement` returns `false` rather than throwing.
45
+
46
+ ## Global entitlements
47
+
48
+ Some entitlements aren't about one app. A **global entitlement** belongs to no
49
+ app and is created in the console, never by `registerEntitlement`. Every tenant
50
+ starts with one, `access`.
51
+
52
+ A grant can be scoped too. Granting a user `access` **for your app** and
53
+ granting them `access` **globally** both make `hasEntitlement(request, "access")`
54
+ return `true` in your app; only the global grant also satisfies every other app.
55
+
56
+ | Definition | Grant | `hasEntitlement(request, "access")` in `my-app` |
57
+ | --- | --- | --- |
58
+ | global `access` | `my-app` / `access` | true |
59
+ | global `access` | global / `access` | true (and in every other app) |
60
+ | global `access` | `other-app` / `access` | false |
61
+
62
+ ## Granting
63
+
64
+ Apps can't grant. An administrator opens **Entitlements** in the console, picks
65
+ the user, the entitlement and its scope, and adds a note; the same page revokes
66
+ grants and creates global entitlements. Every grant records who made it.
67
+
68
+ ## Locally
69
+
70
+ There's no console on your laptop, so the local implementation is
71
+ **permissive**: `hasEntitlement` always returns `true`. Your code paths run
72
+ unchanged; deployed, the platform answers for real. As with every other
73
+ service, the switch is the environment (`ANBARIC_ENTITLEMENTS_TYPE`), set by the
74
+ platform - don't set it yourself.
75
+
76
+ ## Next
77
+
78
+ - [Authorization with actors and roles](../patterns/authorization.md) - roles, predicates and attribution
79
+ - [Environment and factories](../api/environment.md)
@@ -53,6 +53,27 @@ if (!user.roles.includes("manager")) {
53
53
  await machine.updateJob(jobId, new Map([["approved", true]]), user);
54
54
  ```
55
55
 
56
+ ## Grant capabilities with entitlements
57
+
58
+ Roles describe a kind of user; an **entitlement** is something a specific user
59
+ has been given - a beta feature, an export capability, a paid tier. Declare the
60
+ ones your app checks at startup and test them against the request, the same way
61
+ you resolve the actor:
62
+
63
+ ```ts
64
+ import {hasEntitlement, registerEntitlement} from "anbaric";
65
+
66
+ await registerEntitlement("export", "Can export reports as CSV"); // at startup
67
+
68
+ if (! await hasEntitlement(request, "export")) { // per request
69
+ response.writeHead(403).end("You don't have the export entitlement");
70
+ return;
71
+ }
72
+ ```
73
+
74
+ Administrators grant and revoke them from the console's **Entitlements** page;
75
+ locally every check passes. See [Entitlements](../features/entitlements.md).
76
+
56
77
  ## Design tips
57
78
 
58
79
  - **Least privilege.** Give actors the narrowest roles that let them do their job;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anbaric",
3
- "version": "1.31.0",
3
+ "version": "1.33.0",
4
4
  "description": "Everything needed to write an Anbaric app: state machines, jobs, document and secret stores, local in-memory implementations and the Anbaric Cloud clients",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -24,9 +24,9 @@
24
24
  "prepublishOnly": "npm run build"
25
25
  },
26
26
  "dependencies": {
27
- "anbaric-impl-cloud": "^1.31.0",
28
- "anbaric-data-store": "^1.31.0",
29
- "anbaric-state-machine": "^1.31.0",
30
- "anbaric-tsapi": "^1.31.0"
27
+ "anbaric-impl-cloud": "^1.33.0",
28
+ "anbaric-data-store": "^1.33.0",
29
+ "anbaric-state-machine": "^1.33.0",
30
+ "anbaric-tsapi": "^1.33.0"
31
31
  }
32
32
  }