@lenso/authorization 0.2.0 → 0.3.1

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/README.md CHANGED
@@ -9,6 +9,7 @@ Lenso runtime dependency. RBAC is one module, not the engine's only model.
9
9
  | `@lenso/authorization` | Decisions, conditions, scoped RBAC, bounded lists, role management |
10
10
  | `@lenso/authorization/auth` | Adapter to one exact existing Auth `Access.enforce` chain |
11
11
  | `@lenso/authorization/plugin` | Thin Lenso setup wrapper |
12
+ | `@lenso/authorization/manage` | Authorized exact-scope role/binding inspection |
12
13
  | `@lenso/authorization/drizzle/pg` | Borrowed native Drizzle PostgreSQL role store |
13
14
  | `@lenso/authorization/drizzle/d1` | Borrowed native Drizzle D1 role store |
14
15
  | `@lenso/authorization/drizzle/sqlite` | Borrowed Bun SQLite role store |
@@ -1,7 +1,7 @@
1
- import"../index-qjqy8v4k.js";
1
+ import"../index-td6tfw1g.js";
2
2
  import {
3
3
  sqliteStore2
4
- } from "../index-pmwmdjss.js";
4
+ } from "../index-4z7d1j8a.js";
5
5
 
6
6
  // src/drizzle/d1.ts
7
7
  function d1RoleStore(db, namespace, actions) {
@@ -1,6 +1,6 @@
1
1
  import {
2
- authorizationRoleGraphs2
3
- } from "../index-4znahyqj.js";
2
+ authorizationRoleGraphs3
3
+ } from "../index-c6f5nt4t.js";
4
4
  import {
5
5
  createStore
6
6
  } from "../index-t6xdqvqs.js";
@@ -9,20 +9,20 @@ import {
9
9
  import { and, eq, sql } from "drizzle-orm";
10
10
  function postgresRoleStore(db, namespace, actions) {
11
11
  return createStore(namespace, actions, async (_evaluation) => {
12
- const [row] = await db.select().from(authorizationRoleGraphs2).where(eq(authorizationRoleGraphs2.namespace, namespace)).limit(1);
12
+ const [row] = await db.select().from(authorizationRoleGraphs3).where(eq(authorizationRoleGraphs3.namespace, namespace)).limit(1);
13
13
  return row;
14
14
  }, async (snapshot) => {
15
- await db.insert(authorizationRoleGraphs2).values({
15
+ await db.insert(authorizationRoleGraphs3).values({
16
16
  namespace,
17
17
  revision: snapshot.revision,
18
18
  graph: sql`${JSON.stringify(snapshot.graph)}::text::jsonb`
19
19
  }).onConflictDoNothing();
20
20
  }, async (expectedRevision, snapshot) => {
21
- const rows = await db.update(authorizationRoleGraphs2).set({
21
+ const rows = await db.update(authorizationRoleGraphs3).set({
22
22
  revision: snapshot.revision,
23
23
  graph: sql`${JSON.stringify(snapshot.graph)}::text::jsonb`
24
- }).where(and(eq(authorizationRoleGraphs2.namespace, namespace), eq(authorizationRoleGraphs2.revision, expectedRevision))).returning({
25
- namespace: authorizationRoleGraphs2.namespace
24
+ }).where(and(eq(authorizationRoleGraphs3.namespace, namespace), eq(authorizationRoleGraphs3.revision, expectedRevision))).returning({
25
+ namespace: authorizationRoleGraphs3.namespace
26
26
  });
27
27
  return rows.length === 1;
28
28
  });
@@ -1,6 +1,6 @@
1
1
  import {
2
- authorizationRoleGraphs2
3
- } from "../index-4znahyqj.js";
2
+ authorizationRoleGraphs3
3
+ } from "../index-c6f5nt4t.js";
4
4
  export {
5
- authorizationRoleGraphs2 as authorizationRoleGraphs
5
+ authorizationRoleGraphs3 as authorizationRoleGraphs
6
6
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
- authorizationRoleGraphs3
3
- } from "../index-qjqy8v4k.js";
2
+ authorizationRoleGraphs2
3
+ } from "../index-td6tfw1g.js";
4
4
  export {
5
- authorizationRoleGraphs3 as authorizationRoleGraphs
5
+ authorizationRoleGraphs2 as authorizationRoleGraphs
6
6
  };
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  sqliteStore2,
3
3
  sqliteRoleStore2
4
- } from "../index-pmwmdjss.js";
5
- import"../index-qjqy8v4k.js";
4
+ } from "../index-4z7d1j8a.js";
5
+ import"../index-td6tfw1g.js";
6
6
  export {
7
7
  sqliteRoleStore2 as sqliteRoleStore,
8
8
  sqliteStore2 as sqliteStore
@@ -1,6 +1,6 @@
1
1
  import {
2
- authorizationRoleGraphs3
3
- } from "./index-qjqy8v4k.js";
2
+ authorizationRoleGraphs2
3
+ } from "./index-td6tfw1g.js";
4
4
  import {
5
5
  createStore
6
6
  } from "./index-t6xdqvqs.js";
@@ -9,19 +9,19 @@ import {
9
9
  import { and, eq } from "drizzle-orm";
10
10
  function sqliteStore2(db, namespace, actions) {
11
11
  return createStore(namespace, actions, async (_evaluation) => {
12
- const [row] = await db.select().from(authorizationRoleGraphs3).where(eq(authorizationRoleGraphs3.namespace, namespace)).limit(1).all();
12
+ const [row] = await db.select().from(authorizationRoleGraphs2).where(eq(authorizationRoleGraphs2.namespace, namespace)).limit(1).all();
13
13
  return row;
14
14
  }, async (snapshot) => {
15
- await db.insert(authorizationRoleGraphs3).values({
15
+ await db.insert(authorizationRoleGraphs2).values({
16
16
  namespace,
17
17
  revision: snapshot.revision,
18
18
  graph: snapshot.graph
19
19
  }).onConflictDoNothing().run();
20
20
  }, async (expectedRevision, snapshot) => {
21
- const rows = await db.update(authorizationRoleGraphs3).set({
21
+ const rows = await db.update(authorizationRoleGraphs2).set({
22
22
  revision: snapshot.revision,
23
23
  graph: snapshot.graph
24
- }).where(and(eq(authorizationRoleGraphs3.namespace, namespace), eq(authorizationRoleGraphs3.revision, expectedRevision))).returning().all();
24
+ }).where(and(eq(authorizationRoleGraphs2.namespace, namespace), eq(authorizationRoleGraphs2.revision, expectedRevision))).returning().all();
25
25
  return rows.length === 1;
26
26
  });
27
27
  }
@@ -1,9 +1,9 @@
1
1
  // src/drizzle/schema-pg.ts
2
2
  import { jsonb, pgTable, text } from "drizzle-orm/pg-core";
3
- var authorizationRoleGraphs2 = pgTable("authorization_role_graphs", {
3
+ var authorizationRoleGraphs3 = pgTable("authorization_role_graphs", {
4
4
  namespace: text("namespace").primaryKey(),
5
5
  revision: text("revision").notNull(),
6
6
  graph: jsonb("graph").$type().notNull()
7
7
  });
8
8
 
9
- export { authorizationRoleGraphs2 };
9
+ export { authorizationRoleGraphs3 };
@@ -1,9 +1,9 @@
1
1
  // src/drizzle/schema-sqlite.ts
2
2
  import { sqliteTable, text } from "drizzle-orm/sqlite-core";
3
- var authorizationRoleGraphs3 = sqliteTable("authorization_role_graphs", {
3
+ var authorizationRoleGraphs2 = sqliteTable("authorization_role_graphs", {
4
4
  namespace: text("namespace").primaryKey(),
5
5
  revision: text("revision").notNull(),
6
6
  graph: text("graph", { mode: "json" }).$type().notNull()
7
7
  });
8
8
 
9
- export { authorizationRoleGraphs3 };
9
+ export { authorizationRoleGraphs2 };
@@ -0,0 +1,73 @@
1
+ import { type Plugin } from "@lenso/core/plugin";
2
+ import { z } from "zod";
3
+ import type { Awaitable, Binding, Evaluation, RoleSnapshot, RoleStore, Scope } from "./types";
4
+ export interface AuthorizationInspection<C, A extends string = string> {
5
+ inspect(scope: Scope, caller: C, evaluation: Evaluation): Promise<RoleSnapshot<A>>;
6
+ }
7
+ /** The repository stays private; authorization is repeated after the asynchronous read. */
8
+ export declare function createAuthorizationInspection<C, A extends string>(options: {
9
+ readonly store: RoleStore<A>;
10
+ readonly actions: readonly A[];
11
+ readonly authorize: (caller: C, scope: Scope, evaluation: Evaluation) => Awaitable<boolean>;
12
+ readonly authorizeBinding: (caller: C, binding: Binding, evaluation: Evaluation) => Awaitable<boolean>;
13
+ }): AuthorizationInspection<C, A>;
14
+ /** Explicit metadata-only exposure; role mutation requires a separate audited revision contract. */
15
+ export declare function createAuthorizationManage<C, A extends string>(inspection: Plugin<AuthorizationInspection<C, A>>, id: string): {
16
+ plugin: Plugin<{
17
+ inspect: (input: {
18
+ scope: Scope;
19
+ }, invocation: {
20
+ caller: C;
21
+ evaluation: Evaluation;
22
+ }) => Promise<RoleSnapshot<A>>;
23
+ }>;
24
+ operations: (import("@lenso/engine/operations").Operation<{
25
+ caller: C;
26
+ evaluation: Evaluation;
27
+ }> & {
28
+ readonly plugin: Plugin<{
29
+ inspect: (input: {
30
+ scope: Scope;
31
+ }, invocation: {
32
+ caller: C;
33
+ evaluation: Evaluation;
34
+ }) => Promise<RoleSnapshot<A>>;
35
+ }>;
36
+ readonly method: "inspect";
37
+ readonly input: z.ZodObject<{
38
+ scope: z.ZodObject<{
39
+ type: z.ZodString;
40
+ id: z.ZodString;
41
+ }, z.core.$strict>;
42
+ }, z.core.$strict>;
43
+ readonly context: true;
44
+ })[];
45
+ manage: import("@lenso/manage").Manage<Plugin<{
46
+ inspect: (input: {
47
+ scope: Scope;
48
+ }, invocation: {
49
+ caller: C;
50
+ evaluation: Evaluation;
51
+ }) => Promise<RoleSnapshot<A>>;
52
+ }>, (import("@lenso/engine/operations").Operation<{
53
+ caller: C;
54
+ evaluation: Evaluation;
55
+ }> & {
56
+ readonly plugin: Plugin<{
57
+ inspect: (input: {
58
+ scope: Scope;
59
+ }, invocation: {
60
+ caller: C;
61
+ evaluation: Evaluation;
62
+ }) => Promise<RoleSnapshot<A>>;
63
+ }>;
64
+ readonly method: "inspect";
65
+ readonly input: z.ZodObject<{
66
+ scope: z.ZodObject<{
67
+ type: z.ZodString;
68
+ id: z.ZodString;
69
+ }, z.core.$strict>;
70
+ }, z.core.$strict>;
71
+ readonly context: true;
72
+ })[]>;
73
+ };
package/dist/manage.js ADDED
@@ -0,0 +1,79 @@
1
+ import {
2
+ AuthorizationError2
3
+ } from "./index-6f5nrdh9.js";
4
+ import {
5
+ sameScope2,
6
+ immutableRoleSnapshot
7
+ } from "./index-5k98eatm.js";
8
+
9
+ // src/manage.ts
10
+ import { definePlugin } from "@lenso/core/plugin";
11
+ import { defineOperation } from "@lenso/engine/operations";
12
+ import { defineManage } from "@lenso/manage";
13
+ import { z } from "zod";
14
+ function createAuthorizationInspection(options) {
15
+ return Object.freeze({
16
+ async inspect(scope, caller, evaluation) {
17
+ const boundScope = Object.freeze({ type: scope.type, id: scope.id });
18
+ const boundEvaluation = Object.freeze({ now: evaluation.now, signal: evaluation.signal });
19
+ boundEvaluation.signal.throwIfAborted();
20
+ if (!Number.isSafeInteger(boundEvaluation.now) || !boundScope.type || !boundScope.id || await options.authorize(caller, boundScope, boundEvaluation) !== true) {
21
+ throw new AuthorizationError2;
22
+ }
23
+ const current = immutableRoleSnapshot(await options.store.read(boundEvaluation), options.actions);
24
+ boundEvaluation.signal.throwIfAborted();
25
+ const bindings = [];
26
+ for (const binding of current.graph.bindings) {
27
+ if (!sameScope2(binding.scope, boundScope))
28
+ continue;
29
+ boundEvaluation.signal.throwIfAborted();
30
+ if (await options.authorizeBinding(caller, binding, boundEvaluation) === true) {
31
+ bindings.push(binding);
32
+ }
33
+ }
34
+ if (await options.authorize(caller, boundScope, boundEvaluation) !== true) {
35
+ throw new AuthorizationError2;
36
+ }
37
+ boundEvaluation.signal.throwIfAborted();
38
+ return Object.freeze({
39
+ revision: current.revision,
40
+ graph: Object.freeze({
41
+ roles: Object.freeze(current.graph.roles.filter((role) => sameScope2(role.scope, boundScope))),
42
+ bindings: Object.freeze(bindings)
43
+ })
44
+ });
45
+ }
46
+ });
47
+ }
48
+ function createAuthorizationManage(inspection, id) {
49
+ const plugin = definePlugin({
50
+ id,
51
+ requires: [inspection],
52
+ setup(context) {
53
+ const service = context.get(inspection);
54
+ return {
55
+ inspect: (input, invocation) => service.inspect(input.scope, invocation.caller, invocation.evaluation)
56
+ };
57
+ }
58
+ });
59
+ const identifier = z.string().min(1).max(256).refine((value) => !!value.trim());
60
+ const operation = defineOperation({
61
+ plugin,
62
+ method: "inspect",
63
+ context: true,
64
+ input: z.object({
65
+ scope: z.object({ type: identifier, id: identifier }).strict()
66
+ }).strict(),
67
+ effect: "read",
68
+ retry: "safe",
69
+ cancellation: "cooperative",
70
+ mapError: (error) => error instanceof AuthorizationError2 ? { code: "denied", phase: "invoke", message: "Authorization operation denied" } : undefined,
71
+ description: "Inspect authorized roles and bindings in one exact scope."
72
+ });
73
+ const operations = [operation];
74
+ return { plugin, operations, manage: defineManage({ plugin, operations }) };
75
+ }
76
+ export {
77
+ createAuthorizationInspection,
78
+ createAuthorizationManage
79
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenso/authorization",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -28,6 +28,10 @@
28
28
  "types": "./dist/plugin.d.ts",
29
29
  "default": "./dist/plugin.js"
30
30
  },
31
+ "./manage": {
32
+ "types": "./dist/manage.d.ts",
33
+ "default": "./dist/manage.js"
34
+ },
31
35
  "./drizzle/pg": {
32
36
  "types": "./dist/drizzle/pg.d.ts",
33
37
  "default": "./dist/drizzle/pg.js"
@@ -51,24 +55,39 @@
51
55
  "./migrations/*": "./migrations/*"
52
56
  },
53
57
  "scripts": {
54
- "build": "rm -rf dist && tsc -p tsconfig.build.json && bun build src/index.ts src/auth.ts src/plugin.ts src/drizzle/pg.ts src/drizzle/d1.ts src/drizzle/sqlite.ts src/drizzle/schema-pg.ts src/drizzle/schema-sqlite.ts --root src --outdir dist --target browser --packages external --splitting",
58
+ "build": "rm -rf dist && tsc -p tsconfig.build.json && bun build src/index.ts src/auth.ts src/plugin.ts src/manage.ts src/drizzle/pg.ts src/drizzle/d1.ts src/drizzle/sqlite.ts src/drizzle/schema-pg.ts src/drizzle/schema-sqlite.ts --root src --outdir dist --target browser --packages external --splitting",
55
59
  "typecheck": "tsc --noEmit -p tsconfig.json",
56
60
  "test": "bun test test"
57
61
  },
58
62
  "devDependencies": {
59
63
  "@cloudflare/workers-types": "5.20261008.1",
60
- "@lenso/auth": "^0.3.0",
61
- "@lenso/core": "^0.2.1",
64
+ "@lenso/auth": "^0.3.1",
65
+ "@lenso/core": "^0.3.1",
66
+ "@lenso/engine": "^0.6.0",
67
+ "@lenso/manage": "^0.5.0",
62
68
  "@standard-schema/spec": "1.1.0",
63
69
  "drizzle-orm": "0.45.3",
64
- "miniflare": "5.20261006.0-alpha"
70
+ "miniflare": "5.20261006.0-alpha",
71
+ "zod": "4.6.5"
65
72
  },
66
73
  "peerDependencies": {
67
- "@lenso/auth": "^0.3.0",
68
- "@lenso/core": "^0.2.1",
69
- "drizzle-orm": "^0.45.3"
74
+ "@lenso/auth": "^0.3.1",
75
+ "@lenso/core": "^0.3.1",
76
+ "@lenso/engine": "^0.6.0",
77
+ "@lenso/manage": "^0.5.0",
78
+ "drizzle-orm": "^0.45.3",
79
+ "zod": "4.6.5"
70
80
  },
71
81
  "peerDependenciesMeta": {
82
+ "@lenso/engine": {
83
+ "optional": true
84
+ },
85
+ "@lenso/manage": {
86
+ "optional": true
87
+ },
88
+ "zod": {
89
+ "optional": true
90
+ },
72
91
  "@lenso/auth": {
73
92
  "optional": true
74
93
  },