@stigmer/server 3.27.1 → 3.28.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.
Files changed (245) hide show
  1. package/README.md +2 -0
  2. package/dist/authorization/authorizer.d.ts +7 -5
  3. package/dist/authorization/authorizer.d.ts.map +1 -1
  4. package/dist/authorization/authorizer.js +7 -5
  5. package/dist/authorization/authorizer.js.map +1 -1
  6. package/dist/authorization/derived-tuples.d.ts +3 -1
  7. package/dist/authorization/derived-tuples.d.ts.map +1 -1
  8. package/dist/authorization/derived-tuples.js +52 -2
  9. package/dist/authorization/derived-tuples.js.map +1 -1
  10. package/dist/authorization/evaluator.d.ts +9 -10
  11. package/dist/authorization/evaluator.d.ts.map +1 -1
  12. package/dist/authorization/evaluator.js.map +1 -1
  13. package/dist/authorization/list-read-scope.d.ts.map +1 -1
  14. package/dist/authorization/list-read-scope.js +29 -18
  15. package/dist/authorization/list-read-scope.js.map +1 -1
  16. package/dist/authorization/model/bindings.d.ts +41 -0
  17. package/dist/authorization/model/bindings.d.ts.map +1 -0
  18. package/dist/authorization/model/bindings.js +89 -0
  19. package/dist/authorization/model/bindings.js.map +1 -0
  20. package/dist/authorization/model/data/authorization-model.json +3114 -0
  21. package/dist/authorization/model/default-of.d.ts +2 -2
  22. package/dist/authorization/model/execution-viewer.d.ts +3 -0
  23. package/dist/authorization/model/execution-viewer.d.ts.map +1 -0
  24. package/dist/authorization/model/execution-viewer.js +50 -0
  25. package/dist/authorization/model/execution-viewer.js.map +1 -0
  26. package/dist/authorization/model/index.d.ts +33 -21
  27. package/dist/authorization/model/index.d.ts.map +1 -1
  28. package/dist/authorization/model/index.js +74 -52
  29. package/dist/authorization/model/index.js.map +1 -1
  30. package/dist/authorization/model/openfga-json.d.ts +18 -0
  31. package/dist/authorization/model/openfga-json.d.ts.map +1 -0
  32. package/dist/authorization/model/openfga-json.js +266 -0
  33. package/dist/authorization/model/openfga-json.js.map +1 -0
  34. package/dist/authorization/model/rewrite.d.ts +39 -67
  35. package/dist/authorization/model/rewrite.d.ts.map +1 -1
  36. package/dist/authorization/model/rewrite.js +1 -117
  37. package/dist/authorization/model/rewrite.js.map +1 -1
  38. package/dist/authorization/strict-json.d.ts +20 -0
  39. package/dist/authorization/strict-json.d.ts.map +1 -0
  40. package/dist/authorization/strict-json.js +45 -0
  41. package/dist/authorization/strict-json.js.map +1 -0
  42. package/dist/boot/compose.d.ts.map +1 -1
  43. package/dist/boot/compose.js +9 -7
  44. package/dist/boot/compose.js.map +1 -1
  45. package/dist/boot/list-indexes.d.ts.map +1 -1
  46. package/dist/boot/list-indexes.js +6 -0
  47. package/dist/boot/list-indexes.js.map +1 -1
  48. package/dist/domain/iampolicy/list-index.d.ts +2 -0
  49. package/dist/domain/iampolicy/list-index.d.ts.map +1 -0
  50. package/dist/domain/iampolicy/list-index.js +32 -0
  51. package/dist/domain/iampolicy/list-index.js.map +1 -0
  52. package/dist/domain/iampolicy/resource-store.d.ts.map +1 -1
  53. package/dist/domain/iampolicy/resource-store.js +24 -11
  54. package/dist/domain/iampolicy/resource-store.js.map +1 -1
  55. package/dist/domain/workflowexecution/list-index.d.ts +1 -1
  56. package/dist/extensions/list-read-scope.d.ts +20 -3
  57. package/dist/extensions/list-read-scope.d.ts.map +1 -1
  58. package/dist/extensions/list-read-scope.js +22 -2
  59. package/dist/extensions/list-read-scope.js.map +1 -1
  60. package/dist/index.d.ts +8 -8
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +20 -5
  63. package/dist/index.js.map +1 -1
  64. package/package.json +7 -6
  65. package/src/authorization/README.md +48 -39
  66. package/src/authorization/__tests__/authorizer.test.ts +5 -14
  67. package/src/authorization/__tests__/derived-tuples.test.ts +192 -17
  68. package/src/authorization/__tests__/evaluator.test.ts +53 -160
  69. package/src/authorization/__tests__/facts.test.ts +5 -11
  70. package/src/authorization/__tests__/list-read-scope.measure.test.ts +424 -62
  71. package/src/authorization/__tests__/list-read-scope.test.ts +15 -14
  72. package/src/authorization/__tests__/organization-directory.test.ts +2 -2
  73. package/src/authorization/__tests__/store-test-kit.ts +246 -0
  74. package/src/authorization/__tests__/store-tests.test.ts +91 -223
  75. package/src/authorization/__tests__/support.ts +30 -2
  76. package/src/authorization/__tests__/throwaway-model.ts +75 -0
  77. package/src/authorization/__tests__/wire-permissions.test.ts +141 -107
  78. package/src/authorization/authorizer.ts +7 -5
  79. package/src/authorization/derived-tuples.ts +105 -16
  80. package/src/authorization/evaluator.ts +8 -10
  81. package/src/authorization/list-read-scope.ts +37 -22
  82. package/src/authorization/model/__tests__/openfga-json.test.ts +353 -0
  83. package/src/authorization/model/__tests__/registry.test.ts +102 -276
  84. package/src/authorization/model/bindings.ts +131 -0
  85. package/src/authorization/model/data/authorization-model.json +3114 -0
  86. package/src/authorization/model/default-of.ts +2 -2
  87. package/src/authorization/model/execution-viewer.ts +56 -0
  88. package/src/authorization/model/index.ts +81 -72
  89. package/src/authorization/model/openfga-json.ts +310 -0
  90. package/src/authorization/model/rewrite.ts +39 -188
  91. package/src/authorization/strict-json.ts +52 -0
  92. package/src/boot/__tests__/list-indexes.test.ts +4 -0
  93. package/src/boot/compose.ts +9 -7
  94. package/src/boot/list-indexes.ts +6 -0
  95. package/src/domain/iampolicy/__tests__/resource-store.test.ts +65 -5
  96. package/src/domain/iampolicy/list-index.ts +33 -0
  97. package/src/domain/iampolicy/resource-store.ts +24 -11
  98. package/src/extensions/__tests__/built-in-authorization-composed.test.ts +3 -6
  99. package/src/extensions/list-read-scope.ts +32 -4
  100. package/src/index.ts +20 -44
  101. package/dist/authorization/model/agent.d.ts +0 -2
  102. package/dist/authorization/model/agent.d.ts.map +0 -1
  103. package/dist/authorization/model/agent.js +0 -43
  104. package/dist/authorization/model/agent.js.map +0 -1
  105. package/dist/authorization/model/agent_channel.d.ts +0 -2
  106. package/dist/authorization/model/agent_channel.d.ts.map +0 -1
  107. package/dist/authorization/model/agent_channel.js +0 -39
  108. package/dist/authorization/model/agent_channel.js.map +0 -1
  109. package/dist/authorization/model/agent_execution.d.ts +0 -2
  110. package/dist/authorization/model/agent_execution.d.ts.map +0 -1
  111. package/dist/authorization/model/agent_execution.js +0 -25
  112. package/dist/authorization/model/agent_execution.js.map +0 -1
  113. package/dist/authorization/model/agent_instance.d.ts +0 -2
  114. package/dist/authorization/model/agent_instance.d.ts.map +0 -1
  115. package/dist/authorization/model/agent_instance.js +0 -48
  116. package/dist/authorization/model/agent_instance.js.map +0 -1
  117. package/dist/authorization/model/agent_share.d.ts +0 -2
  118. package/dist/authorization/model/agent_share.d.ts.map +0 -1
  119. package/dist/authorization/model/agent_share.js +0 -29
  120. package/dist/authorization/model/agent_share.js.map +0 -1
  121. package/dist/authorization/model/api_key.d.ts +0 -2
  122. package/dist/authorization/model/api_key.d.ts.map +0 -1
  123. package/dist/authorization/model/api_key.js +0 -27
  124. package/dist/authorization/model/api_key.js.map +0 -1
  125. package/dist/authorization/model/artifact.d.ts +0 -2
  126. package/dist/authorization/model/artifact.d.ts.map +0 -1
  127. package/dist/authorization/model/artifact.js +0 -27
  128. package/dist/authorization/model/artifact.js.map +0 -1
  129. package/dist/authorization/model/channel_app.d.ts +0 -2
  130. package/dist/authorization/model/channel_app.d.ts.map +0 -1
  131. package/dist/authorization/model/channel_app.js +0 -28
  132. package/dist/authorization/model/channel_app.js.map +0 -1
  133. package/dist/authorization/model/environment.d.ts +0 -2
  134. package/dist/authorization/model/environment.d.ts.map +0 -1
  135. package/dist/authorization/model/environment.js +0 -33
  136. package/dist/authorization/model/environment.js.map +0 -1
  137. package/dist/authorization/model/execution_context.d.ts +0 -2
  138. package/dist/authorization/model/execution_context.d.ts.map +0 -1
  139. package/dist/authorization/model/execution_context.js +0 -19
  140. package/dist/authorization/model/execution_context.js.map +0 -1
  141. package/dist/authorization/model/iam_policy.d.ts +0 -2
  142. package/dist/authorization/model/iam_policy.d.ts.map +0 -1
  143. package/dist/authorization/model/iam_policy.js +0 -31
  144. package/dist/authorization/model/iam_policy.js.map +0 -1
  145. package/dist/authorization/model/identity_account.d.ts +0 -2
  146. package/dist/authorization/model/identity_account.d.ts.map +0 -1
  147. package/dist/authorization/model/identity_account.js +0 -25
  148. package/dist/authorization/model/identity_account.js.map +0 -1
  149. package/dist/authorization/model/mcp_server.d.ts +0 -2
  150. package/dist/authorization/model/mcp_server.d.ts.map +0 -1
  151. package/dist/authorization/model/mcp_server.js +0 -38
  152. package/dist/authorization/model/mcp_server.js.map +0 -1
  153. package/dist/authorization/model/memory.d.ts +0 -2
  154. package/dist/authorization/model/memory.d.ts.map +0 -1
  155. package/dist/authorization/model/memory.js +0 -26
  156. package/dist/authorization/model/memory.js.map +0 -1
  157. package/dist/authorization/model/oauth_app.d.ts +0 -2
  158. package/dist/authorization/model/oauth_app.d.ts.map +0 -1
  159. package/dist/authorization/model/oauth_app.js +0 -31
  160. package/dist/authorization/model/oauth_app.js.map +0 -1
  161. package/dist/authorization/model/organization.d.ts +0 -2
  162. package/dist/authorization/model/organization.d.ts.map +0 -1
  163. package/dist/authorization/model/organization.js +0 -58
  164. package/dist/authorization/model/organization.js.map +0 -1
  165. package/dist/authorization/model/platform_client.d.ts +0 -2
  166. package/dist/authorization/model/platform_client.d.ts.map +0 -1
  167. package/dist/authorization/model/platform_client.js +0 -31
  168. package/dist/authorization/model/platform_client.js.map +0 -1
  169. package/dist/authorization/model/plugin.d.ts +0 -2
  170. package/dist/authorization/model/plugin.d.ts.map +0 -1
  171. package/dist/authorization/model/plugin.js +0 -45
  172. package/dist/authorization/model/plugin.js.map +0 -1
  173. package/dist/authorization/model/schedule.d.ts +0 -2
  174. package/dist/authorization/model/schedule.d.ts.map +0 -1
  175. package/dist/authorization/model/schedule.js +0 -34
  176. package/dist/authorization/model/schedule.js.map +0 -1
  177. package/dist/authorization/model/session.d.ts +0 -2
  178. package/dist/authorization/model/session.d.ts.map +0 -1
  179. package/dist/authorization/model/session.js +0 -38
  180. package/dist/authorization/model/session.js.map +0 -1
  181. package/dist/authorization/model/skill.d.ts +0 -2
  182. package/dist/authorization/model/skill.d.ts.map +0 -1
  183. package/dist/authorization/model/skill.js +0 -39
  184. package/dist/authorization/model/skill.js.map +0 -1
  185. package/dist/authorization/model/workflow.d.ts +0 -2
  186. package/dist/authorization/model/workflow.d.ts.map +0 -1
  187. package/dist/authorization/model/workflow.js +0 -39
  188. package/dist/authorization/model/workflow.js.map +0 -1
  189. package/dist/authorization/model/workflow_execution.d.ts +0 -2
  190. package/dist/authorization/model/workflow_execution.d.ts.map +0 -1
  191. package/dist/authorization/model/workflow_execution.js +0 -33
  192. package/dist/authorization/model/workflow_execution.js.map +0 -1
  193. package/dist/authorization/model/workflow_instance.d.ts +0 -2
  194. package/dist/authorization/model/workflow_instance.d.ts.map +0 -1
  195. package/dist/authorization/model/workflow_instance.js +0 -96
  196. package/dist/authorization/model/workflow_instance.js.map +0 -1
  197. package/dist/authorization/store-test-kit.d.ts +0 -62
  198. package/dist/authorization/store-test-kit.d.ts.map +0 -1
  199. package/dist/authorization/store-test-kit.js +0 -265
  200. package/dist/authorization/store-test-kit.js.map +0 -1
  201. package/src/authorization/__tests__/fixtures/fga/agent-channel-owner.fga.yaml +0 -112
  202. package/src/authorization/__tests__/fixtures/fga/agent-instance-creation.fga.yaml +0 -177
  203. package/src/authorization/__tests__/fixtures/fga/agent-share-owner.fga.yaml +0 -109
  204. package/src/authorization/__tests__/fixtures/fga/artifact-org-and-owner.fga.yaml +0 -128
  205. package/src/authorization/__tests__/fixtures/fga/blueprint-private-visibility.fga.yaml +0 -318
  206. package/src/authorization/__tests__/fixtures/fga/channel-app-owner.fga.yaml +0 -125
  207. package/src/authorization/__tests__/fixtures/fga/channel-participation.fga.yaml +0 -201
  208. package/src/authorization/__tests__/fixtures/fga/channel-session-visibility.fga.yaml +0 -187
  209. package/src/authorization/__tests__/fixtures/fga/default-instance-inheritance.fga.yaml +0 -214
  210. package/src/authorization/__tests__/fixtures/fga/mcp-server-authoring.fga.yaml +0 -129
  211. package/src/authorization/__tests__/fixtures/fga/memory-subject-only.fga.yaml +0 -72
  212. package/src/authorization/__tests__/fixtures/fga/org-admin-owner-inheritance.fga.yaml +0 -308
  213. package/src/authorization/__tests__/fixtures/fga/org-shared-environment.fga.yaml +0 -144
  214. package/src/authorization/__tests__/fixtures/fga/platform-visibility.fga.yaml +0 -199
  215. package/src/authorization/__tests__/fixtures/fga/provider-standing-viewer.fga.yaml +0 -40
  216. package/src/authorization/__tests__/fixtures/fga/reserved-label-writer.fga.yaml +0 -41
  217. package/src/authorization/__tests__/fixtures/fga/schedule-owner.fga.yaml +0 -113
  218. package/src/authorization/__tests__/fixtures/fga/schedule-session-visibility.fga.yaml +0 -214
  219. package/src/authorization/__tests__/fixtures/fga/session-personal-resource.fga.yaml +0 -117
  220. package/src/authorization/__tests__/fixtures/fga/workflow-execution-sharing.fga.yaml +0 -190
  221. package/src/authorization/model/agent.ts +0 -64
  222. package/src/authorization/model/agent_channel.ts +0 -57
  223. package/src/authorization/model/agent_execution.ts +0 -33
  224. package/src/authorization/model/agent_instance.ts +0 -66
  225. package/src/authorization/model/agent_share.ts +0 -40
  226. package/src/authorization/model/api_key.ts +0 -28
  227. package/src/authorization/model/artifact.ts +0 -38
  228. package/src/authorization/model/channel_app.ts +0 -39
  229. package/src/authorization/model/environment.ts +0 -48
  230. package/src/authorization/model/execution_context.ts +0 -20
  231. package/src/authorization/model/iam_policy.ts +0 -42
  232. package/src/authorization/model/identity_account.ts +0 -26
  233. package/src/authorization/model/mcp_server.ts +0 -59
  234. package/src/authorization/model/memory.ts +0 -27
  235. package/src/authorization/model/oauth_app.ts +0 -43
  236. package/src/authorization/model/organization.ts +0 -59
  237. package/src/authorization/model/platform_client.ts +0 -43
  238. package/src/authorization/model/plugin.ts +0 -66
  239. package/src/authorization/model/schedule.ts +0 -49
  240. package/src/authorization/model/session.ts +0 -50
  241. package/src/authorization/model/skill.ts +0 -60
  242. package/src/authorization/model/workflow.ts +0 -60
  243. package/src/authorization/model/workflow_execution.ts +0 -46
  244. package/src/authorization/model/workflow_instance.ts +0 -125
  245. package/src/authorization/store-test-kit.ts +0 -395
@@ -1,33 +0,0 @@
1
- /**
2
- * Transcript of fga/model/agentic/workflow_execution.fga — a run with an
3
- * owner of its own (the person who triggered it; `kind_meta` attribution
4
- * DIRECT, unlike agent_execution's inherited owner) and two ways in for
5
- * others: an explicit per-run grant, or the instance's opt-in run
6
- * audience through `execution_viewer from workflow_instance`, standing on
7
- * the `workflow_instance` link the derivation writes from
8
- * `spec.workflow_instance_id`. No `can_delete`: run history outlives its
9
- * instance by design. The per-run grant may name an Enterprise team
10
- * (`team#member`), which no open-source tuple ever does.
11
- */
12
- import { WorkflowExecutionSchema } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/api_pb";
13
- import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
14
- import { computed, declareKind, direct, from, objectOf, union, usersetOf, } from "./rewrite.js";
15
- export const workflowExecutionDeclaration = declareKind({
16
- kind: ApiResourceKind.workflow_execution,
17
- schema: WorkflowExecutionSchema,
18
- source: "fga/model/agentic/workflow_execution.fga",
19
- relations: [
20
- ["organization", direct(objectOf("organization"))],
21
- ["workflow_instance", direct(objectOf("workflow_instance"))],
22
- ["owner", direct(objectOf("identity_account"))],
23
- [
24
- "viewer",
25
- union(direct(objectOf("identity_account"), usersetOf("team", "member")), computed("owner"), from("execution_viewer", "workflow_instance")),
26
- ],
27
- ["can_view", computed("viewer")],
28
- ["can_edit", computed("owner")],
29
- ["can_grant_access", computed("owner")],
30
- ["can_view_access", computed("viewer")],
31
- ],
32
- });
33
- //# sourceMappingURL=workflow_execution.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"workflow_execution.js","sourceRoot":"","sources":["../../../src/authorization/model/workflow_execution.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,uBAAuB,EAAE,MAAM,gEAAgE,CAAC;AACzG,OAAO,EAAE,eAAe,EAAE,MAAM,qFAAqF,CAAC;AAEtH,OAAO,EACL,QAAQ,EACR,WAAW,EACX,MAAM,EACN,IAAI,EACJ,QAAQ,EACR,KAAK,EACL,SAAS,GACV,MAAM,cAAc,CAAC;AAEtB,MAAM,CAAC,MAAM,4BAA4B,GAAG,WAAW,CAAC;IACtD,IAAI,EAAE,eAAe,CAAC,kBAAkB;IACxC,MAAM,EAAE,uBAAuB;IAC/B,MAAM,EAAE,0CAA0C;IAClD,SAAS,EAAE;QACT,CAAC,cAAc,EAAE,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC;QAClD,CAAC,mBAAmB,EAAE,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC,CAAC;QAC5D,CAAC,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAC;QAC/C;YACE,QAAQ;YACR,KAAK,CACH,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,EACjE,QAAQ,CAAC,OAAO,CAAC,EACjB,IAAI,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,CAC9C;SACF;QACD,CAAC,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAChC,CAAC,UAAU,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC,kBAAkB,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;QACvC,CAAC,iBAAiB,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;KACxC;CACF,CAAC,CAAC"}
@@ -1,2 +0,0 @@
1
- export declare const workflowInstanceDeclaration: import("./rewrite.js").KindDeclaration;
2
- //# sourceMappingURL=workflow_instance.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"workflow_instance.d.ts","sourceRoot":"","sources":["../../../src/authorization/model/workflow_instance.ts"],"names":[],"mappings":"AA2EA,eAAO,MAAM,2BAA2B,wCAiDtC,CAAC"}
@@ -1,96 +0,0 @@
1
- /**
2
- * Transcript of fga/model/agentic/workflow_instance.fga — the agent
3
- * instance's shape plus one relation of its own: `execution_viewer`, the
4
- * opt-in audience for the instance's RUN history, kept apart from
5
- * `viewer` so making an instance org-runnable never exposes other
6
- * people's run inputs and outputs. Consumed by workflow_execution through
7
- * `execution_viewer from workflow_instance`.
8
- *
9
- * `execution_viewer` is derived here from `spec.execution_visibility`
10
- * (the second rule `kind_meta` cannot express): `organization` derives
11
- * `#execution_viewer@organization:<org>#viewer` — the organization's full
12
- * read audience, the shape the file's comment names and the cloud's own
13
- * store test writes, with `#member` the legacy shape the line still
14
- * admits — and `private` or unset derives nothing, so each run stays its
15
- * triggerer's. The cloud has no writer for this tuple today; this
16
- * edition enforces the field as the contract states it.
17
- *
18
- * `team#member` on `viewer` is the Enterprise team grant, which no
19
- * open-source tuple ever names; `execution_viewer` is not a grantable role
20
- * and admits no team.
21
- */
22
- import { isMessage } from "@bufbuild/protobuf";
23
- import { WorkflowSchema } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/api_pb";
24
- import { WorkflowInstanceSchema } from "@stigmer/protos/ai/stigmer/agentic/workflowinstance/v1/api_pb";
25
- import { WorkflowExecutionVisibility } from "@stigmer/protos/ai/stigmer/agentic/workflowinstance/v1/spec_pb";
26
- import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
27
- import { defaultOfBlueprint } from "./default-of.js";
28
- import { computed, declareKind, direct, from, objectOf, union, usersetOf, } from "./rewrite.js";
29
- const executionViewer = (object, row) => {
30
- if (!isMessage(row, WorkflowInstanceSchema)) {
31
- return Promise.resolve([]);
32
- }
33
- const level = row.spec?.executionVisibility ?? WorkflowExecutionVisibility.unspecified;
34
- switch (level) {
35
- case WorkflowExecutionVisibility.organization: {
36
- const org = row.metadata?.org ?? "";
37
- return Promise.resolve(org === ""
38
- ? []
39
- : [
40
- {
41
- object,
42
- relation: "execution_viewer",
43
- subject: {
44
- form: "userset",
45
- object: { type: "organization", id: org },
46
- relation: "viewer",
47
- },
48
- },
49
- ]);
50
- }
51
- case WorkflowExecutionVisibility.private:
52
- case WorkflowExecutionVisibility.unspecified:
53
- return Promise.resolve([]);
54
- default: {
55
- const exhaustive = level;
56
- throw new Error(`unknown execution visibility: ${String(exhaustive)}`);
57
- }
58
- }
59
- };
60
- export const workflowInstanceDeclaration = declareKind({
61
- kind: ApiResourceKind.workflow_instance,
62
- schema: WorkflowInstanceSchema,
63
- source: "fga/model/agentic/workflow_instance.fga",
64
- relations: [
65
- ["organization", direct(objectOf("organization"))],
66
- ["workflow", direct(objectOf("workflow"))],
67
- ["default_of", direct(objectOf("workflow"))],
68
- ["owner", direct(objectOf("identity_account"))],
69
- [
70
- "viewer",
71
- union(direct(objectOf("identity_account"), usersetOf("organization", "member"), usersetOf("organization", "viewer"), usersetOf("team", "member")), computed("owner"), from("viewer", "default_of")),
72
- ],
73
- [
74
- "execution_viewer",
75
- direct(objectOf("identity_account"), usersetOf("organization", "member"), usersetOf("organization", "viewer")),
76
- ],
77
- ["can_view", computed("viewer")],
78
- ["can_execute", computed("can_view")],
79
- ["can_edit", computed("owner")],
80
- ["can_delete", computed("owner")],
81
- ["can_grant_access", computed("owner")],
82
- ["can_view_access", computed("viewer")],
83
- ],
84
- derived: [
85
- [
86
- "default_of",
87
- defaultOfBlueprint({
88
- kind: ApiResourceKind.workflow,
89
- schema: WorkflowSchema,
90
- defaultInstanceIdOf: (workflow) => workflow.status?.defaultInstanceId ?? "",
91
- }),
92
- ],
93
- ["execution_viewer", executionViewer],
94
- ],
95
- });
96
- //# sourceMappingURL=workflow_instance.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"workflow_instance.js","sourceRoot":"","sources":["../../../src/authorization/model/workflow_instance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAE,cAAc,EAAE,MAAM,uDAAuD,CAAC;AACvF,OAAO,EAAE,sBAAsB,EAAE,MAAM,+DAA+D,CAAC;AACvG,OAAO,EAAE,2BAA2B,EAAE,MAAM,gEAAgE,CAAC;AAC7G,OAAO,EAAE,eAAe,EAAE,MAAM,qFAAqF,CAAC;AAEtH,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD,OAAO,EACL,QAAQ,EACR,WAAW,EACX,MAAM,EACN,IAAI,EACJ,QAAQ,EACR,KAAK,EACL,SAAS,GACV,MAAM,cAAc,CAAC;AAEtB,MAAM,eAAe,GAAoB,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;IACvD,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,sBAAsB,CAAC,EAAE,CAAC;QAC5C,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC7B,CAAC;IACD,MAAM,KAAK,GACT,GAAG,CAAC,IAAI,EAAE,mBAAmB,IAAI,2BAA2B,CAAC,WAAW,CAAC;IAC3E,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,2BAA2B,CAAC,YAAY,CAAC,CAAC,CAAC;YAC9C,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,EAAE,GAAG,IAAI,EAAE,CAAC;YACpC,OAAO,OAAO,CAAC,OAAO,CACpB,GAAG,KAAK,EAAE;gBACR,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC;oBACE;wBACE,MAAM;wBACN,QAAQ,EAAE,kBAAkB;wBAC5B,OAAO,EAAE;4BACP,IAAI,EAAE,SAAS;4BACf,MAAM,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,GAAG,EAAE;4BACzC,QAAQ,EAAE,QAAQ;yBACnB;qBACF;iBACF,CACN,CAAC;QACJ,CAAC;QACD,KAAK,2BAA2B,CAAC,OAAO,CAAC;QACzC,KAAK,2BAA2B,CAAC,WAAW;YAC1C,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC7B,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,UAAU,GAAU,KAAK,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,iCAAiC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,2BAA2B,GAAG,WAAW,CAAC;IACrD,IAAI,EAAE,eAAe,CAAC,iBAAiB;IACvC,MAAM,EAAE,sBAAsB;IAC9B,MAAM,EAAE,yCAAyC;IACjD,SAAS,EAAE;QACT,CAAC,cAAc,EAAE,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC;QAClD,CAAC,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;QAC1C,CAAC,YAAY,EAAE,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;QAC5C,CAAC,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAC;QAC/C;YACE,QAAQ;YACR,KAAK,CACH,MAAM,CACJ,QAAQ,CAAC,kBAAkB,CAAC,EAC5B,SAAS,CAAC,cAAc,EAAE,QAAQ,CAAC,EACnC,SAAS,CAAC,cAAc,EAAE,QAAQ,CAAC,EACnC,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,CAC5B,EACD,QAAQ,CAAC,OAAO,CAAC,EACjB,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAC7B;SACF;QACD;YACE,kBAAkB;YAClB,MAAM,CACJ,QAAQ,CAAC,kBAAkB,CAAC,EAC5B,SAAS,CAAC,cAAc,EAAE,QAAQ,CAAC,EACnC,SAAS,CAAC,cAAc,EAAE,QAAQ,CAAC,CACpC;SACF;QACD,CAAC,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAChC,CAAC,aAAa,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;QACrC,CAAC,UAAU,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC,YAAY,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;QACjC,CAAC,kBAAkB,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;QACvC,CAAC,iBAAiB,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;KACxC;IACD,OAAO,EAAE;QACP;YACE,YAAY;YACZ,kBAAkB,CAAC;gBACjB,IAAI,EAAE,eAAe,CAAC,QAAQ;gBAC9B,MAAM,EAAE,cAAc;gBACtB,mBAAmB,EAAE,CAAC,QAAQ,EAAE,EAAE,CAChC,QAAQ,CAAC,MAAM,EAAE,iBAAiB,IAAI,EAAE;aAC3C,CAAC;SACH;QACD,CAAC,kBAAkB,EAAE,eAAe,CAAC;KACtC;CACF,CAAC,CAAC"}
@@ -1,62 +0,0 @@
1
- import type { Model } from "./model/index.js";
2
- export interface StoreTestTuple {
3
- readonly user: string;
4
- readonly relation: string;
5
- readonly object: string;
6
- }
7
- export interface StoreTestCheck {
8
- readonly user: string;
9
- readonly object: string;
10
- readonly assertions: Readonly<Record<string, boolean>>;
11
- }
12
- export interface StoreTestListObjects {
13
- readonly user: string;
14
- readonly type: string;
15
- readonly assertions: Readonly<Record<string, ReadonlyArray<string>>>;
16
- }
17
- export interface StoreTest {
18
- readonly name: string;
19
- readonly check: ReadonlyArray<StoreTestCheck>;
20
- readonly list_objects: ReadonlyArray<StoreTestListObjects>;
21
- }
22
- export interface StoreTestDocument {
23
- readonly name: string;
24
- readonly tuples: ReadonlyArray<StoreTestTuple>;
25
- readonly tests: ReadonlyArray<StoreTest>;
26
- }
27
- /**
28
- * Parses a loaded YAML value into a document, refusing anything the
29
- * format the cloud's files use does not carry (`sourceName` names the
30
- * file in every message).
31
- */
32
- export declare function parseStoreTestDocument(value: unknown, sourceName: string): StoreTestDocument;
33
- export interface StoreTestCase {
34
- /** `<document> :: <test name>` — the framework prints it as the test name. */
35
- readonly name: string;
36
- run(): Promise<void>;
37
- }
38
- export type StoreTestSkipReason = "undeclared-kind" | "list-objects" | "undeclared-subject-type";
39
- /** One assertion (or assertion group) the kit did not run, in the document's words. */
40
- export interface StoreTestSkip {
41
- readonly document: string;
42
- readonly test: string;
43
- readonly subject: string;
44
- /** The check's object, or `<type>` for a list_objects assertion. */
45
- readonly object: string;
46
- /** The one relation, for a skip decided per assertion; absent for a group. */
47
- readonly relation?: string;
48
- readonly reason: StoreTestSkipReason;
49
- /** For `undeclared-subject-type`: the types the walk read that the model does not declare, sorted. */
50
- readonly types?: ReadonlyArray<string>;
51
- }
52
- export interface StoreTestKit {
53
- readonly cases: ReadonlyArray<StoreTestCase>;
54
- /** Complete once every case has run (the module header). */
55
- readonly skipped: ReadonlyArray<StoreTestSkip>;
56
- }
57
- /**
58
- * The document's tests as runnable cases over `model`, plus every
59
- * assertion group the kit does not run.
60
- */
61
- export declare function storeTestCases(document: StoreTestDocument, model: Model): StoreTestKit;
62
- //# sourceMappingURL=store-test-kit.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"store-test-kit.d.ts","sourceRoot":"","sources":["../../src/authorization/store-test-kit.ts"],"names":[],"mappings":"AAqDA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAa9C,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACxD;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;CACtE;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;IAC9C,QAAQ,CAAC,YAAY,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;CAC5D;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;IAC/C,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;CAC1C;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,OAAO,EACd,UAAU,EAAE,MAAM,GACjB,iBAAiB,CAgBnB;AAqGD,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtB;AAED,MAAM,MAAM,mBAAmB,GAC3B,iBAAiB,GACjB,cAAc,GACd,yBAAyB,CAAC;AAE9B,uFAAuF;AACvF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,8EAA8E;IAC9E,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,sGAAsG;IACtG,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IAC7C,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;CAChD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,iBAAiB,EAC3B,KAAK,EAAE,KAAK,GACX,YAAY,CAsEd"}
@@ -1,265 +0,0 @@
1
- /**
2
- * The OpenFGA store-test kit — runs a `.fga.yaml` store test (the format
3
- * `fga model test` reads: fixture tuples, then named tests with `check`
4
- * and `list_objects` assertions) against the built-in evaluator, so the
5
- * cloud's own model tests — written by the model's authors and run
6
- * against real OpenFGA in the cloud's CI — are the proof that this
7
- * evaluator answers the same model the same way. Open source runs the
8
- * kit over pinned copies of those documents (authorization/__tests__/
9
- * fixtures/fga/); the cloud runs it over the LIVE documents at the
10
- * re-pin, which is what makes the copies safe to hold.
11
- *
12
- * Shape: the port-contract kits' (store/port-contract.ts) — vitest-free,
13
- * node:assert, cases the consumer's framework iterates — because a
14
- * composition consumes it through the published barrel and vitest is a
15
- * devDependency that must not enter the barrel's runtime graph.
16
- *
17
- * What is run and what is reported. One case per named test; a case
18
- * asserts every `check` in the test whose object's kind the model
19
- * declares, relation by relation, and FAILS on the first disagreement
20
- * (the message names the check in the document's own words). Assertions
21
- * the kit does not run are returned as `skipped`, each with its reason,
22
- * so a consumer pins the list and a document that gains a scenario, or a
23
- * declaration that lands, is a visible diff and never a quietly smaller
24
- * proof. Three reasons: a check whose TARGET kind the model does not
25
- * declare (`undeclared-kind`) and every `list_objects` assertion
26
- * (`list-objects`, the list scope's proof) are known when the kit is
27
- * built; the third is known only from the walk. A document may grant
28
- * through a type the model does not declare — the cloud's platform
29
- * tenancy reaches people through `identity_provider:<idp>#platform_user`,
30
- * a kind this edition does not serve — and OpenFGA answers `true` where
31
- * a model without that type resolves the userset to nobody. The kit runs
32
- * every assertion over a recording view of the document's tuples, and an
33
- * assertion whose walk READ a tuple naming an undeclared type is
34
- * reported as `undeclared-subject-type` (the types named) instead of
35
- * being asserted, whatever it would have answered: decided from the
36
- * model and the data, per assertion, so the sibling arms of the same
37
- * check block still run and a union that is settled before it reaches
38
- * such a tuple runs too. Over a model that declares every type in the
39
- * file (the cloud's drift test, built from the live model) nothing is
40
- * recorded and everything runs — the same kit, no edition branch.
41
- * `skipped` is therefore complete once every case has run; a consumer
42
- * pins it after the cases, which is the order a test file declares them.
43
- *
44
- * The parser is strict on purpose: an unknown key anywhere is a thrown
45
- * fault, so a change to the format upstream is loud here rather than
46
- * half-run. The model declares no condition, so the format's `condition`
47
- * on a tuple and `context` on a check are among the keys refused: a
48
- * document that still carries them is written against a model this
49
- * evaluator does not answer, and saying so beats running it half-blind.
50
- */
51
- import assert from "node:assert/strict";
52
- import { checkRelation } from "./evaluator.js";
53
- import { ACCOUNT_TYPE, newInMemoryTupleSource, parseObjectRef, parseSubject, } from "./tuples.js";
54
- /**
55
- * Parses a loaded YAML value into a document, refusing anything the
56
- * format the cloud's files use does not carry (`sourceName` names the
57
- * file in every message).
58
- */
59
- export function parseStoreTestDocument(value, sourceName) {
60
- const root = record(value, sourceName, [
61
- "name",
62
- "model_file",
63
- "tuples",
64
- "tests",
65
- ]);
66
- return {
67
- name: text(root["name"], `${sourceName}: name`),
68
- tuples: list(root["tuples"], `${sourceName}: tuples`).map((entry, index) => parseTuple(entry, `${sourceName}: tuples[${index}]`)),
69
- tests: list(root["tests"], `${sourceName}: tests`).map((entry, index) => parseTest(entry, `${sourceName}: tests[${index}]`)),
70
- };
71
- }
72
- function parseTuple(value, where) {
73
- const entry = record(value, where, ["user", "relation", "object"]);
74
- return {
75
- user: text(entry["user"], `${where}.user`),
76
- relation: text(entry["relation"], `${where}.relation`),
77
- object: text(entry["object"], `${where}.object`),
78
- };
79
- }
80
- function parseTest(value, where) {
81
- const entry = record(value, where, ["name", "check", "list_objects"]);
82
- return {
83
- name: text(entry["name"], `${where}.name`),
84
- check: optionalList(entry["check"], `${where}.check`).map((item, index) => parseCheck(item, `${where}.check[${index}]`)),
85
- list_objects: optionalList(entry["list_objects"], `${where}.list_objects`).map((item, index) => parseListObjects(item, `${where}.list_objects[${index}]`)),
86
- };
87
- }
88
- function parseCheck(value, where) {
89
- const entry = record(value, where, ["user", "object", "assertions"]);
90
- const assertions = record(entry["assertions"], `${where}.assertions`);
91
- const parsed = {};
92
- for (const [relation, expected] of Object.entries(assertions)) {
93
- if (typeof expected !== "boolean") {
94
- throw new Error(`${where}.assertions.${relation}: expected a boolean`);
95
- }
96
- parsed[relation] = expected;
97
- }
98
- return {
99
- user: text(entry["user"], `${where}.user`),
100
- object: text(entry["object"], `${where}.object`),
101
- assertions: parsed,
102
- };
103
- }
104
- function parseListObjects(value, where) {
105
- const entry = record(value, where, ["user", "type", "assertions"]);
106
- const assertions = record(entry["assertions"], `${where}.assertions`);
107
- const parsed = {};
108
- for (const [relation, expected] of Object.entries(assertions)) {
109
- parsed[relation] = list(expected, `${where}.assertions.${relation}`).map((item, index) => text(item, `${where}.assertions.${relation}[${index}]`));
110
- }
111
- return {
112
- user: text(entry["user"], `${where}.user`),
113
- type: text(entry["type"], `${where}.type`),
114
- assertions: parsed,
115
- };
116
- }
117
- function record(value, where, allowedKeys) {
118
- if (value === null || typeof value !== "object" || Array.isArray(value)) {
119
- throw new Error(`${where}: expected a mapping`);
120
- }
121
- const entry = value;
122
- if (allowedKeys !== undefined) {
123
- for (const key of Object.keys(entry)) {
124
- if (!allowedKeys.includes(key)) {
125
- throw new Error(`${where}: unknown key '${key}'`);
126
- }
127
- }
128
- }
129
- return entry;
130
- }
131
- function list(value, where) {
132
- if (!Array.isArray(value)) {
133
- throw new Error(`${where}: expected a list`);
134
- }
135
- return value;
136
- }
137
- function optionalList(value, where) {
138
- return value === undefined ? [] : list(value, where);
139
- }
140
- function text(value, where) {
141
- if (typeof value !== "string") {
142
- throw new Error(`${where}: expected a string`);
143
- }
144
- return value;
145
- }
146
- /**
147
- * The document's tests as runnable cases over `model`, plus every
148
- * assertion group the kit does not run.
149
- */
150
- export function storeTestCases(document, model) {
151
- const source = newInMemoryTupleSource(document.tuples.map(tupleOf));
152
- const skipped = [];
153
- const cases = [];
154
- for (const test of document.tests) {
155
- const runnable = [];
156
- for (const item of test.check) {
157
- const object = parseObjectRef(item.object);
158
- if (model.byType(object.type) === undefined) {
159
- skipped.push({
160
- document: document.name,
161
- test: test.name,
162
- subject: item.user,
163
- object: item.object,
164
- reason: "undeclared-kind",
165
- });
166
- }
167
- else {
168
- runnable.push(item);
169
- }
170
- }
171
- for (const item of test.list_objects) {
172
- skipped.push({
173
- document: document.name,
174
- test: test.name,
175
- subject: item.user,
176
- object: `<${item.type}>`,
177
- reason: "list-objects",
178
- });
179
- }
180
- if (runnable.length === 0) {
181
- continue;
182
- }
183
- cases.push({
184
- name: `${document.name} :: ${test.name}`,
185
- async run() {
186
- for (const item of runnable) {
187
- const object = parseObjectRef(item.object);
188
- const person = personOf(item.user);
189
- for (const [relation, expected] of Object.entries(item.assertions)) {
190
- const recording = recordUndeclaredTypes(source, model);
191
- const actual = await checkRelation({ model, source: recording }, object, relation, person);
192
- if (recording.reached.size > 0) {
193
- skipped.push({
194
- document: document.name,
195
- test: test.name,
196
- subject: item.user,
197
- object: item.object,
198
- relation,
199
- reason: "undeclared-subject-type",
200
- types: [...recording.reached].sort(),
201
- });
202
- continue;
203
- }
204
- assert.equal(actual, expected, `${document.name} :: ${test.name}: ${item.user} ${relation} ${item.object} expected ${String(expected)}, got ${String(actual)}`);
205
- }
206
- }
207
- },
208
- });
209
- }
210
- return { cases, skipped };
211
- }
212
- /**
213
- * A view of `inner` that records, for one walk, every subject type the
214
- * evaluator read that `model` does not declare. A person
215
- * (`identity_account:<id>`) is a subject the evaluator compares, never a
216
- * type it resolves, so it is not a "reach" whatever the model declares; a
217
- * userset or a parent object of an undeclared type is.
218
- */
219
- function recordUndeclaredTypes(inner, model) {
220
- const reached = new Set();
221
- return {
222
- reached,
223
- async tuplesOf(object, relation) {
224
- const tuples = await inner.tuplesOf(object, relation);
225
- for (const tuple of tuples) {
226
- const type = resolvedTypeOf(tuple.subject);
227
- if (type !== undefined && model.byType(type) === undefined) {
228
- reached.add(type);
229
- }
230
- }
231
- return tuples;
232
- },
233
- };
234
- }
235
- /** The type the evaluator would resolve a subject on, or undefined for a person. */
236
- function resolvedTypeOf(subject) {
237
- switch (subject.form) {
238
- case "object":
239
- return subject.object.type === ACCOUNT_TYPE
240
- ? undefined
241
- : subject.object.type;
242
- case "userset":
243
- return subject.object.type;
244
- default: {
245
- const exhaustive = subject;
246
- throw new Error(`unknown subject form: ${JSON.stringify(exhaustive)}`);
247
- }
248
- }
249
- }
250
- function tupleOf(entry) {
251
- return {
252
- object: parseObjectRef(entry.object),
253
- relation: entry.relation,
254
- subject: parseSubject(entry.user),
255
- };
256
- }
257
- /** The document's users are plain accounts; a user of another type is a format the kit does not run. */
258
- function personOf(user) {
259
- const object = parseObjectRef(user);
260
- if (object.type !== ACCOUNT_TYPE || object.id === "*") {
261
- throw new Error(`store test users are accounts; got '${user}'`);
262
- }
263
- return { accountId: object.id, aliases: new Set([object.id]) };
264
- }
265
- //# sourceMappingURL=store-test-kit.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"store-test-kit.js","sourceRoot":"","sources":["../../src/authorization/store-test-kit.ts"],"names":[],"mappings":"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"}
@@ -1,112 +0,0 @@
1
- # Pinned copy of stigmer-cloud backend/services/stigmer-server/fga/tests/agent-channel-owner.fga.yaml
2
- # at commit b87dd1170 (the OpenFGA store test the cloud runs against its live model).
3
- # The body below is byte-identical to the source; the cloud re-runs the LIVE
4
- # file against this evaluator at the re-pin, so a stale copy is caught there.
5
- # Agent channels are owner-scoped connection config (channel-integrations
6
- # decision 001, cloned from the agent_share model): the creator owns the
7
- # channel; view/edit/delete/install require ownership or an explicit grant.
8
- # Plain org members get NO implicit access, and — critically — no
9
- # workspace-visitor tuples exist at all: workspace-user admission to the
10
- # channel runtime is app-level (webhook signature + channel caller identity
11
- # + per-turn gates), never FGA.
12
- #
13
- # Org ADMINS inherit owner (T08, consistent with agents and shares — an
14
- # admin who can edit the agent can manage its distribution channels). The
15
- # narrowness these tests pin: owner + admins have control, granted viewers
16
- # read, plain members have nothing.
17
- #
18
- # Run: fga model test --tests agent-channel-owner.fga.yaml
19
-
20
- name: agent-channel-owner
21
- model_file: ../model/fga.mod
22
-
23
- tuples:
24
- # Org membership for both accounts — proving membership grants nothing.
25
- - user: identity_account:alice
26
- relation: member
27
- object: organization:acme
28
- - user: identity_account:mallory
29
- relation: member
30
- object: organization:acme
31
-
32
- # Org admin — proving T08 inheritance reaches channels.
33
- - user: identity_account:ana
34
- relation: admin
35
- object: organization:acme
36
-
37
- # The channel, created by alice (the config-driven create tuples).
38
- - user: organization:acme
39
- relation: organization
40
- object: agent_channel:support-bot-slack
41
- - user: identity_account:alice
42
- relation: owner
43
- object: agent_channel:support-bot-slack
44
-
45
- # An explicit viewer grant to carol (not even an org member).
46
- - user: identity_account:carol
47
- relation: viewer
48
- object: agent_channel:support-bot-slack
49
-
50
- tests:
51
- - name: owner has full control including grant management
52
- check:
53
- - user: identity_account:alice
54
- object: agent_channel:support-bot-slack
55
- assertions:
56
- can_view: true
57
- can_edit: true
58
- can_delete: true
59
- can_grant_access: true
60
- can_view_access: true
61
-
62
- - name: explicitly granted viewer can read but not mutate
63
- check:
64
- - user: identity_account:carol
65
- object: agent_channel:support-bot-slack
66
- assertions:
67
- can_view: true
68
- can_edit: false
69
- can_delete: false
70
- can_grant_access: false
71
- can_view_access: true
72
-
73
- - name: fellow org member has no implicit access
74
- check:
75
- - user: identity_account:mallory
76
- object: agent_channel:support-bot-slack
77
- assertions:
78
- can_view: false
79
- can_edit: false
80
- can_delete: false
81
- can_grant_access: false
82
- can_view_access: false
83
-
84
- - name: org admin inherits channel management (T08)
85
- check:
86
- - user: identity_account:ana
87
- object: agent_channel:support-bot-slack
88
- assertions:
89
- can_view: true
90
- can_edit: true
91
- can_delete: true
92
- can_grant_access: true
93
-
94
- - name: listing channels reflects ownership and grants, not membership
95
- list_objects:
96
- - user: identity_account:alice
97
- type: agent_channel
98
- assertions:
99
- can_view:
100
- - agent_channel:support-bot-slack
101
- can_edit:
102
- - agent_channel:support-bot-slack
103
- - user: identity_account:carol
104
- type: agent_channel
105
- assertions:
106
- can_view:
107
- - agent_channel:support-bot-slack
108
- can_edit: []
109
- - user: identity_account:mallory
110
- type: agent_channel
111
- assertions:
112
- can_view: []