@stigmer/server 3.22.1 → 3.23.1-dev.20260923075205
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/dist/artifactstorage/artifact-storage.d.ts +29 -31
- package/dist/artifactstorage/artifact-storage.d.ts.map +1 -1
- package/dist/artifactstorage/artifact-storage.js +5 -7
- package/dist/artifactstorage/artifact-storage.js.map +1 -1
- package/dist/artifactstorage/r2-storage.d.ts +12 -7
- package/dist/artifactstorage/r2-storage.d.ts.map +1 -1
- package/dist/artifactstorage/r2-storage.js +6 -14
- package/dist/artifactstorage/r2-storage.js.map +1 -1
- package/dist/authorization/authorizer.d.ts +4 -8
- package/dist/authorization/authorizer.d.ts.map +1 -1
- package/dist/authorization/authorizer.js +5 -9
- package/dist/authorization/authorizer.js.map +1 -1
- package/dist/authorization/derived-tuples.d.ts.map +1 -1
- package/dist/authorization/derived-tuples.js +10 -25
- package/dist/authorization/derived-tuples.js.map +1 -1
- package/dist/authorization/evaluator.d.ts +7 -8
- package/dist/authorization/evaluator.d.ts.map +1 -1
- package/dist/authorization/evaluator.js +4 -20
- package/dist/authorization/evaluator.js.map +1 -1
- package/dist/authorization/list-read-scope.d.ts.map +1 -1
- package/dist/authorization/list-read-scope.js +5 -9
- package/dist/authorization/list-read-scope.js.map +1 -1
- package/dist/authorization/model/agent.d.ts.map +1 -1
- package/dist/authorization/model/agent.js +4 -5
- package/dist/authorization/model/agent.js.map +1 -1
- package/dist/authorization/model/agent_instance.d.ts.map +1 -1
- package/dist/authorization/model/agent_instance.js +2 -2
- package/dist/authorization/model/agent_instance.js.map +1 -1
- package/dist/authorization/model/mcp_server.d.ts.map +1 -1
- package/dist/authorization/model/mcp_server.js +2 -2
- package/dist/authorization/model/mcp_server.js.map +1 -1
- package/dist/authorization/model/plugin.d.ts.map +1 -1
- package/dist/authorization/model/plugin.js +3 -4
- package/dist/authorization/model/plugin.js.map +1 -1
- package/dist/authorization/model/rewrite.d.ts +8 -10
- package/dist/authorization/model/rewrite.d.ts.map +1 -1
- package/dist/authorization/model/rewrite.js +0 -3
- package/dist/authorization/model/rewrite.js.map +1 -1
- package/dist/authorization/model/skill.d.ts.map +1 -1
- package/dist/authorization/model/skill.js +2 -2
- package/dist/authorization/model/skill.js.map +1 -1
- package/dist/authorization/model/workflow.d.ts.map +1 -1
- package/dist/authorization/model/workflow.js +2 -2
- package/dist/authorization/model/workflow.js.map +1 -1
- package/dist/authorization/model/workflow_instance.d.ts.map +1 -1
- package/dist/authorization/model/workflow_instance.js +2 -2
- package/dist/authorization/model/workflow_instance.js.map +1 -1
- package/dist/authorization/organization-directory.d.ts.map +1 -1
- package/dist/authorization/organization-directory.js +2 -3
- package/dist/authorization/organization-directory.js.map +1 -1
- package/dist/authorization/store-test-kit.d.ts +0 -6
- package/dist/authorization/store-test-kit.d.ts.map +1 -1
- package/dist/authorization/store-test-kit.js +16 -62
- package/dist/authorization/store-test-kit.js.map +1 -1
- package/dist/authorization/tuples.d.ts +13 -32
- package/dist/authorization/tuples.d.ts.map +1 -1
- package/dist/authorization/tuples.js +3 -11
- package/dist/authorization/tuples.js.map +1 -1
- package/dist/boot/compose.d.ts.map +1 -1
- package/dist/boot/compose.js +49 -24
- package/dist/boot/compose.js.map +1 -1
- package/dist/boot/config.d.ts +4 -3
- package/dist/boot/config.d.ts.map +1 -1
- package/dist/boot/config.js.map +1 -1
- package/dist/boot/skill-transfer-origin.d.ts +52 -0
- package/dist/boot/skill-transfer-origin.d.ts.map +1 -0
- package/dist/boot/skill-transfer-origin.js +54 -0
- package/dist/boot/skill-transfer-origin.js.map +1 -0
- package/dist/domain/agent/controller.d.ts.map +1 -1
- package/dist/domain/agent/controller.js +4 -4
- package/dist/domain/agent/controller.js.map +1 -1
- package/dist/domain/agentchannel/controller.d.ts.map +1 -1
- package/dist/domain/agentchannel/controller.js +3 -1
- package/dist/domain/agentchannel/controller.js.map +1 -1
- package/dist/domain/agentexecution/controller.d.ts.map +1 -1
- package/dist/domain/agentexecution/controller.js +3 -1
- package/dist/domain/agentexecution/controller.js.map +1 -1
- package/dist/domain/agentinstance/controller.d.ts.map +1 -1
- package/dist/domain/agentinstance/controller.js +3 -4
- package/dist/domain/agentinstance/controller.js.map +1 -1
- package/dist/domain/agentshare/constants.d.ts +6 -11
- package/dist/domain/agentshare/constants.d.ts.map +1 -1
- package/dist/domain/agentshare/constants.js +8 -16
- package/dist/domain/agentshare/constants.js.map +1 -1
- package/dist/domain/agentshare/controller.d.ts.map +1 -1
- package/dist/domain/agentshare/controller.js +3 -1
- package/dist/domain/agentshare/controller.js.map +1 -1
- package/dist/domain/agentshare/steps.d.ts +18 -25
- package/dist/domain/agentshare/steps.d.ts.map +1 -1
- package/dist/domain/agentshare/steps.js +41 -142
- package/dist/domain/agentshare/steps.js.map +1 -1
- package/dist/domain/environment/controller.d.ts.map +1 -1
- package/dist/domain/environment/controller.js +2 -3
- package/dist/domain/environment/controller.js.map +1 -1
- package/dist/domain/executioncontext/controller.d.ts.map +1 -1
- package/dist/domain/executioncontext/controller.js +2 -1
- package/dist/domain/executioncontext/controller.js.map +1 -1
- package/dist/domain/mcpserver/controller.d.ts.map +1 -1
- package/dist/domain/mcpserver/controller.js +3 -4
- package/dist/domain/mcpserver/controller.js.map +1 -1
- package/dist/domain/plugin/controller.d.ts +3 -7
- package/dist/domain/plugin/controller.d.ts.map +1 -1
- package/dist/domain/plugin/controller.js +9 -11
- package/dist/domain/plugin/controller.js.map +1 -1
- package/dist/domain/plugin/push.d.ts +7 -5
- package/dist/domain/plugin/push.d.ts.map +1 -1
- package/dist/domain/plugin/push.js +8 -14
- package/dist/domain/plugin/push.js.map +1 -1
- package/dist/domain/schedule/controller.d.ts.map +1 -1
- package/dist/domain/schedule/controller.js +3 -1
- package/dist/domain/schedule/controller.js.map +1 -1
- package/dist/domain/session/controller.d.ts.map +1 -1
- package/dist/domain/session/controller.js +3 -2
- package/dist/domain/session/controller.js.map +1 -1
- package/dist/domain/skill/constants.d.ts +26 -3
- package/dist/domain/skill/constants.d.ts.map +1 -1
- package/dist/domain/skill/constants.js +26 -3
- package/dist/domain/skill/constants.js.map +1 -1
- package/dist/domain/skill/controller.d.ts +6 -11
- package/dist/domain/skill/controller.d.ts.map +1 -1
- package/dist/domain/skill/controller.js +33 -24
- package/dist/domain/skill/controller.js.map +1 -1
- package/dist/domain/skill/push.d.ts +2 -2
- package/dist/domain/skill/push.d.ts.map +1 -1
- package/dist/domain/skill/push.js +2 -2
- package/dist/domain/skill/push.js.map +1 -1
- package/dist/domain/skill/transfer/handler.d.ts +17 -9
- package/dist/domain/skill/transfer/handler.d.ts.map +1 -1
- package/dist/domain/skill/transfer/handler.js +19 -8
- package/dist/domain/skill/transfer/handler.js.map +1 -1
- package/dist/domain/skill/transfer/slots.d.ts +26 -41
- package/dist/domain/skill/transfer/slots.d.ts.map +1 -1
- package/dist/domain/skill/transfer/slots.js +80 -87
- package/dist/domain/skill/transfer/slots.js.map +1 -1
- package/dist/domain/skill/transfer/staging.d.ts +48 -0
- package/dist/domain/skill/transfer/staging.d.ts.map +1 -0
- package/dist/domain/skill/transfer/staging.js +127 -0
- package/dist/domain/skill/transfer/staging.js.map +1 -0
- package/dist/domain/workflow/agent-call-references.d.ts +54 -0
- package/dist/domain/workflow/agent-call-references.d.ts.map +1 -0
- package/dist/domain/workflow/agent-call-references.js +97 -0
- package/dist/domain/workflow/agent-call-references.js.map +1 -0
- package/dist/domain/workflow/controller.d.ts.map +1 -1
- package/dist/domain/workflow/controller.js +14 -6
- package/dist/domain/workflow/controller.js.map +1 -1
- package/dist/domain/workflow/registry/data/task-kind-registry.json +1 -1
- package/dist/domain/workflow/validation/task-config-constraints.d.ts +10 -1
- package/dist/domain/workflow/validation/task-config-constraints.d.ts.map +1 -1
- package/dist/domain/workflow/validation/task-config-constraints.js +3 -2
- package/dist/domain/workflow/validation/task-config-constraints.js.map +1 -1
- package/dist/domain/workflowexecution/controller.d.ts.map +1 -1
- package/dist/domain/workflowexecution/controller.js +4 -2
- package/dist/domain/workflowexecution/controller.js.map +1 -1
- package/dist/domain/workflowinstance/controller.d.ts.map +1 -1
- package/dist/domain/workflowinstance/controller.js +3 -4
- package/dist/domain/workflowinstance/controller.js.map +1 -1
- package/dist/extensions/authorization-queries.d.ts +6 -5
- package/dist/extensions/authorization-queries.d.ts.map +1 -1
- package/dist/extensions/list-read-scope.d.ts +1 -1
- package/dist/extensions/resource-authorization.d.ts +3 -2
- package/dist/extensions/resource-authorization.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/pipeline/apiresource-meta.d.ts +12 -3
- package/dist/pipeline/apiresource-meta.d.ts.map +1 -1
- package/dist/pipeline/apiresource-meta.js +21 -10
- package/dist/pipeline/apiresource-meta.js.map +1 -1
- package/dist/pipeline/steps/authorization-tuples.d.ts +9 -3
- package/dist/pipeline/steps/authorization-tuples.d.ts.map +1 -1
- package/dist/pipeline/steps/authorization-tuples.js +9 -11
- package/dist/pipeline/steps/authorization-tuples.js.map +1 -1
- package/dist/pipeline/steps/references.d.ts +178 -20
- package/dist/pipeline/steps/references.d.ts.map +1 -1
- package/dist/pipeline/steps/references.js +344 -43
- package/dist/pipeline/steps/references.js.map +1 -1
- package/dist/pipeline/steps/resolve-artifact-source.d.ts +11 -8
- package/dist/pipeline/steps/resolve-artifact-source.d.ts.map +1 -1
- package/dist/pipeline/steps/resolve-artifact-source.js +8 -4
- package/dist/pipeline/steps/resolve-artifact-source.js.map +1 -1
- package/dist/query/search/controller.js +0 -1
- package/dist/query/search/controller.js.map +1 -1
- package/dist/query/search/criteria.d.ts +1 -5
- package/dist/query/search/criteria.d.ts.map +1 -1
- package/dist/query/search/criteria.js +3 -13
- package/dist/query/search/criteria.js.map +1 -1
- package/dist/query/search/handler.d.ts +5 -4
- package/dist/query/search/handler.d.ts.map +1 -1
- package/dist/query/search/handler.js +7 -6
- package/dist/query/search/handler.js.map +1 -1
- package/dist/query/search/query-store.d.ts.map +1 -1
- package/dist/query/search/query-store.js +0 -2
- package/dist/query/search/query-store.js.map +1 -1
- package/dist/store/interface.d.ts +3 -6
- package/dist/store/interface.d.ts.map +1 -1
- package/dist/store/interface.js.map +1 -1
- package/dist/store/postgres/migrations.d.ts +3 -1
- package/dist/store/postgres/migrations.d.ts.map +1 -1
- package/dist/store/postgres/migrations.js +35 -1
- package/dist/store/postgres/migrations.js.map +1 -1
- package/dist/store/postgres/store.d.ts.map +1 -1
- package/dist/store/postgres/store.js +4 -9
- package/dist/store/postgres/store.js.map +1 -1
- package/dist/store/public-visibility-retired.d.ts +67 -0
- package/dist/store/public-visibility-retired.d.ts.map +1 -0
- package/dist/store/public-visibility-retired.js +55 -0
- package/dist/store/public-visibility-retired.js.map +1 -0
- package/dist/store/sqlite/migrations.d.ts +3 -1
- package/dist/store/sqlite/migrations.d.ts.map +1 -1
- package/dist/store/sqlite/migrations.js +36 -1
- package/dist/store/sqlite/migrations.js.map +1 -1
- package/dist/store/sqlite/store.d.ts.map +1 -1
- package/dist/store/sqlite/store.js +4 -8
- package/dist/store/sqlite/store.js.map +1 -1
- package/package.json +6 -6
- package/src/artifactstorage/__tests__/artifact-storage.test.ts +36 -33
- package/src/artifactstorage/__tests__/r2-storage.test.ts +11 -13
- package/src/artifactstorage/artifact-storage.ts +37 -33
- package/src/artifactstorage/r2-storage.ts +6 -15
- package/src/authorization/README.md +24 -26
- package/src/authorization/__tests__/authorizer.test.ts +10 -9
- package/src/authorization/__tests__/derived-tuples.test.ts +4 -11
- package/src/authorization/__tests__/evaluator.test.ts +14 -73
- package/src/authorization/__tests__/facts.test.ts +2 -2
- package/src/authorization/__tests__/fixtures/fga/agent-instance-creation.fga.yaml +19 -27
- package/src/authorization/__tests__/fixtures/fga/blueprint-private-visibility.fga.yaml +6 -98
- package/src/authorization/__tests__/fixtures/fga/default-instance-inheritance.fga.yaml +4 -61
- package/src/authorization/__tests__/fixtures/fga/mcp-server-authoring.fga.yaml +1 -15
- package/src/authorization/__tests__/fixtures/fga/org-admin-owner-inheritance.fga.yaml +1 -21
- package/src/authorization/__tests__/fixtures/fga/platform-visibility.fga.yaml +1 -25
- package/src/authorization/__tests__/fixtures/fga/workflow-execution-sharing.fga.yaml +1 -3
- package/src/authorization/__tests__/list-read-scope.test.ts +18 -17
- package/src/authorization/__tests__/store-tests.test.ts +2 -6
- package/src/authorization/__tests__/tuples.test.ts +12 -11
- package/src/authorization/authorizer.ts +4 -9
- package/src/authorization/derived-tuples.ts +14 -31
- package/src/authorization/evaluator.ts +8 -44
- package/src/authorization/list-read-scope.ts +5 -11
- package/src/authorization/model/agent.ts +2 -5
- package/src/authorization/model/agent_instance.ts +0 -2
- package/src/authorization/model/mcp_server.ts +0 -2
- package/src/authorization/model/plugin.ts +1 -4
- package/src/authorization/model/rewrite.ts +8 -13
- package/src/authorization/model/skill.ts +0 -2
- package/src/authorization/model/workflow.ts +0 -2
- package/src/authorization/model/workflow_instance.ts +0 -2
- package/src/authorization/organization-directory.ts +1 -3
- package/src/authorization/store-test-kit.ts +16 -75
- package/src/authorization/tuples.ts +16 -39
- package/src/boot/__tests__/skill-transfer-origin.test.ts +72 -0
- package/src/boot/compose.ts +62 -25
- package/src/boot/config.ts +4 -3
- package/src/boot/skill-transfer-origin.ts +76 -0
- package/src/domain/agent/__tests__/agent.test.ts +11 -4
- package/src/domain/agent/controller.ts +7 -7
- package/src/domain/agentchannel/__tests__/agentchannel.test.ts +223 -48
- package/src/domain/agentchannel/controller.ts +6 -1
- package/src/domain/agentexecution/controller.ts +6 -1
- package/src/domain/agentinstance/controller.ts +6 -12
- package/src/domain/agentshare/__tests__/agentshare.test.ts +196 -172
- package/src/domain/agentshare/__tests__/create-authorization.test.ts +16 -31
- package/src/domain/agentshare/constants.ts +14 -26
- package/src/domain/agentshare/controller.ts +6 -1
- package/src/domain/agentshare/steps.ts +41 -194
- package/src/domain/environment/controller.ts +5 -8
- package/src/domain/executioncontext/controller.ts +5 -1
- package/src/domain/mcpserver/__tests__/mcpserver.test.ts +115 -28
- package/src/domain/mcpserver/__tests__/oauth-handshake.test.ts +53 -18
- package/src/domain/mcpserver/controller.ts +6 -12
- package/src/domain/plugin/controller.ts +16 -25
- package/src/domain/plugin/push.ts +8 -16
- package/src/domain/schedule/controller.ts +6 -1
- package/src/domain/session/controller.ts +12 -4
- package/src/domain/skill/__tests__/skill.test.ts +5 -3
- package/src/domain/skill/__tests__/slots.test.ts +113 -60
- package/src/domain/skill/__tests__/staging.test.ts +304 -0
- package/src/domain/skill/constants.ts +28 -3
- package/src/domain/skill/controller.ts +44 -42
- package/src/domain/skill/push.ts +3 -3
- package/src/domain/skill/transfer/handler.ts +33 -15
- package/src/domain/skill/transfer/slots.ts +89 -100
- package/src/domain/skill/transfer/staging.ts +191 -0
- package/src/domain/workflow/__tests__/agent-call-references.test.ts +222 -0
- package/src/domain/workflow/agent-call-references.ts +157 -0
- package/src/domain/workflow/controller.ts +28 -10
- package/src/domain/workflow/registry/data/task-kind-registry.json +1 -1
- package/src/domain/workflow/validation/task-config-constraints.ts +7 -3
- package/src/domain/workflowexecution/controller.ts +7 -2
- package/src/domain/workflowinstance/controller.ts +6 -12
- package/src/extensions/__tests__/built-in-authorization-composed.test.ts +32 -19
- package/src/extensions/__tests__/extension-composition.test.ts +4 -4
- package/src/extensions/__tests__/list-read-scope-composed.test.ts +9 -6
- package/src/extensions/authorization-queries.ts +6 -5
- package/src/extensions/list-read-scope.ts +1 -1
- package/src/extensions/resource-authorization.ts +3 -5
- package/src/index.ts +0 -1
- package/src/pipeline/__tests__/supports-visibility.test.ts +112 -0
- package/src/pipeline/apiresource-meta.ts +21 -10
- package/src/pipeline/steps/__tests__/authorization-tuples.test.ts +18 -31
- package/src/pipeline/steps/__tests__/references.test.ts +703 -0
- package/src/pipeline/steps/authorization-tuples.ts +9 -11
- package/src/pipeline/steps/references.ts +578 -70
- package/src/pipeline/steps/resolve-artifact-source.ts +20 -13
- package/src/query/search/__tests__/criteria.test.ts +0 -4
- package/src/query/search/__tests__/query-store.test.ts +11 -40
- package/src/query/search/controller.ts +0 -1
- package/src/query/search/criteria.ts +0 -14
- package/src/query/search/handler.ts +6 -7
- package/src/query/search/query-store.ts +0 -2
- package/src/store/__tests__/public-visibility-retired.test.ts +127 -0
- package/src/store/__tests__/store-contract.ts +6 -51
- package/src/store/interface.ts +3 -6
- package/src/store/postgres/__tests__/migrations.test.ts +187 -6
- package/src/store/postgres/__tests__/store-contract.test.ts +0 -2
- package/src/store/postgres/migrations.ts +48 -1
- package/src/store/postgres/store.ts +4 -11
- package/src/store/public-visibility-retired.ts +119 -0
- package/src/store/sqlite/__tests__/migrations.test.ts +191 -5
- package/src/store/sqlite/migrations.ts +45 -1
- package/src/store/sqlite/store.ts +4 -10
- package/src/transport/registry/__tests__/lanes.test.ts +1 -1
- package/dist/pipeline/steps/visibility-gates.d.ts +0 -27
- package/dist/pipeline/steps/visibility-gates.d.ts.map +0 -1
- package/dist/pipeline/steps/visibility-gates.js +0 -139
- package/dist/pipeline/steps/visibility-gates.js.map +0 -1
- package/src/authorization/__tests__/fixtures/fga/cross-org-share-creation.fga.yaml +0 -98
- package/src/authorization/__tests__/fixtures/fga/public-visibility-setter.fga.yaml +0 -52
- package/src/pipeline/steps/__tests__/visibility-gates.test.ts +0 -187
- package/src/pipeline/steps/visibility-gates.ts +0 -174
|
@@ -58,17 +58,15 @@ export class ArtifactStorageNotFoundError extends Error {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
|
-
* A staged upload minted by presignPut: the URL receives the bytes,
|
|
62
|
-
*
|
|
63
|
-
* short-lived by contract — the local lane sweeps them on TTL
|
|
64
|
-
* boot;
|
|
65
|
-
* prefix (the §6b driver-side sweep expectation).
|
|
61
|
+
* A staged upload minted by presignPut: the URL receives the bytes, which
|
|
62
|
+
* the caller reads back through download() under the key IT named. Staged
|
|
63
|
+
* blobs are short-lived by contract — the local lane sweeps them on TTL
|
|
64
|
+
* expiry and boot; a bucket deployment configures a lifecycle rule on the
|
|
65
|
+
* domain's staging prefix (the §6b driver-side sweep expectation).
|
|
66
66
|
*/
|
|
67
67
|
export interface PresignedUpload {
|
|
68
|
-
/** Accepts
|
|
68
|
+
/** Accepts an HTTP PUT of exactly the declared byte count. */
|
|
69
69
|
readonly url: string;
|
|
70
|
-
/** Driver key where the staged bytes land, readable via download(). */
|
|
71
|
-
readonly stagingKey: string;
|
|
72
70
|
/** The TTL actually granted, after per-driver clamping. */
|
|
73
71
|
readonly ttlMs: number;
|
|
74
72
|
}
|
|
@@ -98,18 +96,25 @@ export interface ArtifactStorage {
|
|
|
98
96
|
downloadFilename: string,
|
|
99
97
|
): Promise<string>;
|
|
100
98
|
/**
|
|
101
|
-
* A time-limited upload URL for a
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
99
|
+
* A time-limited upload URL for a blob of exactly declaredSizeBytes at
|
|
100
|
+
* the key the CALLER names (§6b). The key is the domain's: it chooses
|
|
101
|
+
* the staging prefix its sweep targets and the mapping from its wire
|
|
102
|
+
* reference, and reads the bytes back through download(key) — the
|
|
103
|
+
* driver never invents a key of its own. Per-driver semantics differ
|
|
104
|
+
* DELIBERATELY and are contract, not accident: the local backend rides
|
|
105
|
+
* the skill transfer lane's URL-as-credential slot mechanism
|
|
106
|
+
* (single-use, exact-size enforced by the lane, TTL clamped to the
|
|
107
|
+
* lane's slot TTL) and answers only on instances with a staged-upload
|
|
108
|
+
* lane wired — unwired instances throw the explicit not-configured
|
|
109
|
+
* error, never a silent no-op. The R2 backend presigns a PUT
|
|
110
|
+
* (repeatable within its TTL, size enforced by the signed
|
|
111
|
+
* Content-Length header, TTL clamped to the 7-day R2 maximum).
|
|
111
112
|
*/
|
|
112
|
-
presignPut(
|
|
113
|
+
presignPut(
|
|
114
|
+
key: string,
|
|
115
|
+
declaredSizeBytes: number,
|
|
116
|
+
ttlMs: number,
|
|
117
|
+
): Promise<PresignedUpload>;
|
|
113
118
|
/** Removes the artifact; missing is not an error. */
|
|
114
119
|
delete(key: string): Promise<void>;
|
|
115
120
|
/** Whether an artifact with the key exists. */
|
|
@@ -136,16 +141,16 @@ export type ArtifactStorageDriverFactory = () => ArtifactStorage;
|
|
|
136
141
|
* the shape it needs and stays ignorant of who provides it.
|
|
137
142
|
*/
|
|
138
143
|
export interface StagedUploadLane {
|
|
139
|
-
/** Reserves a single-use upload slot; returns its reference + TTL. */
|
|
140
|
-
mint(declaredSizeBytes: number): { ref: string; ttlMs: number };
|
|
141
|
-
/** The externally-reachable PUT URL for a minted reference. */
|
|
142
|
-
uploadUrl(ref: string): string;
|
|
143
144
|
/**
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
145
|
+
* Reserves a single-use upload slot whose bytes land at `key` under this
|
|
146
|
+
* driver instance's root — the lane stages into the driver's own tree,
|
|
147
|
+
* so download(key) reads exactly what the lane received. Returns the
|
|
148
|
+
* externally-reachable PUT URL and the TTL the lane grants.
|
|
147
149
|
*/
|
|
148
|
-
|
|
150
|
+
reserve(
|
|
151
|
+
key: string,
|
|
152
|
+
declaredSizeBytes: number,
|
|
153
|
+
): { url: string; ttlMs: number };
|
|
149
154
|
}
|
|
150
155
|
|
|
151
156
|
export interface ArtifactStorageConfig {
|
|
@@ -259,6 +264,7 @@ export class LocalArtifactStorage implements ArtifactStorage {
|
|
|
259
264
|
}
|
|
260
265
|
|
|
261
266
|
async presignPut(
|
|
267
|
+
key: string,
|
|
262
268
|
declaredSizeBytes: number,
|
|
263
269
|
_ttlMs: number,
|
|
264
270
|
): Promise<PresignedUpload> {
|
|
@@ -267,15 +273,13 @@ export class LocalArtifactStorage implements ArtifactStorage {
|
|
|
267
273
|
"local presigned uploads not configured - no staged-upload lane is wired to this store instance",
|
|
268
274
|
);
|
|
269
275
|
}
|
|
276
|
+
// The key must be one this instance can read back, or the lane would
|
|
277
|
+
// stage bytes download(key) can never reach.
|
|
278
|
+
this.resolveWithinRoot(key);
|
|
270
279
|
// The lane's slot TTL governs, not the caller's ask — the slot
|
|
271
280
|
// registry sweeps on ITS clock, and a URL outliving its slot would be
|
|
272
281
|
// a credential for nothing.
|
|
273
|
-
|
|
274
|
-
return {
|
|
275
|
-
url: this.stagedUploadLane.uploadUrl(ref),
|
|
276
|
-
stagingKey: this.stagedUploadLane.stagedKey(ref),
|
|
277
|
-
ttlMs,
|
|
278
|
-
};
|
|
282
|
+
return this.stagedUploadLane.reserve(key, declaredSizeBytes);
|
|
279
283
|
}
|
|
280
284
|
|
|
281
285
|
async getSignedUrl(
|
|
@@ -9,8 +9,6 @@
|
|
|
9
9
|
* that have logic: config validation copy, the 7-day presign clamp, the
|
|
10
10
|
* signed Content-Disposition, and the not-found mapping.
|
|
11
11
|
*/
|
|
12
|
-
import { randomBytes } from "node:crypto";
|
|
13
|
-
|
|
14
12
|
import {
|
|
15
13
|
DeleteObjectCommand,
|
|
16
14
|
GetObjectCommand,
|
|
@@ -30,13 +28,6 @@ import {
|
|
|
30
28
|
/** Go: "R2 has a maximum expiration of 7 days" — presigns clamp here. */
|
|
31
29
|
export const R2_MAX_EXPIRATION_MS = 7 * 24 * 60 * 60 * 1000;
|
|
32
30
|
|
|
33
|
-
/**
|
|
34
|
-
* Key prefix for presignPut staging blobs (§6b). Pinned as the surface a
|
|
35
|
-
* deployment's bucket lifecycle rule targets: the driver mints under it,
|
|
36
|
-
* operations sweeps it — changing it orphans staged blobs from the sweep.
|
|
37
|
-
*/
|
|
38
|
-
export const R2_STAGING_PREFIX = "staging/";
|
|
39
|
-
|
|
40
31
|
/**
|
|
41
32
|
* Go get_content/get_download_url wrap their storage calls in a 30s
|
|
42
33
|
* context timeout. The TS ArtifactStorage interface carries no signal, so
|
|
@@ -167,17 +158,17 @@ export class R2ArtifactStorage implements ArtifactStorage {
|
|
|
167
158
|
}
|
|
168
159
|
|
|
169
160
|
async presignPut(
|
|
161
|
+
key: string,
|
|
170
162
|
declaredSizeBytes: number,
|
|
171
163
|
ttlMs: number,
|
|
172
164
|
): Promise<PresignedUpload> {
|
|
173
165
|
const clampedMs = Math.min(ttlMs, R2_MAX_EXPIRATION_MS);
|
|
174
|
-
//
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
const stagingKey = `${R2_STAGING_PREFIX}${randomBytes(16).toString("hex")}`;
|
|
166
|
+
// The key is the caller's: the domain names its staging prefix (the
|
|
167
|
+
// surface a bucket lifecycle rule sweeps) and keeps the unguessable
|
|
168
|
+
// part of the URL-as-credential in the reference it hands the client.
|
|
178
169
|
const command = new PutObjectCommand({
|
|
179
170
|
Bucket: this.bucket,
|
|
180
|
-
Key:
|
|
171
|
+
Key: key,
|
|
181
172
|
// Signed into the URL: a PUT whose body disagrees with the declared
|
|
182
173
|
// size fails the signature check — R2's arm of the exact-size
|
|
183
174
|
// contract the local lane enforces by counting bytes.
|
|
@@ -187,7 +178,7 @@ export class R2ArtifactStorage implements ArtifactStorage {
|
|
|
187
178
|
const url = await this.presign(this.client, command, {
|
|
188
179
|
expiresIn: Math.floor(clampedMs / 1000),
|
|
189
180
|
});
|
|
190
|
-
return { url,
|
|
181
|
+
return { url, ttlMs: clampedMs };
|
|
191
182
|
} catch (error) {
|
|
192
183
|
throw new Error(`r2 presign failed: ${errorText(error)}`, { cause: error });
|
|
193
184
|
}
|
|
@@ -34,9 +34,9 @@ editions; the cloud stores what open source computes.
|
|
|
34
34
|
`spec.execution_visibility`).
|
|
35
35
|
- `tuples.ts` — the tuple vocabulary (object, relation, subject; the string
|
|
36
36
|
notation), the `Person` type (the caller as the model sees them: account id
|
|
37
|
-
plus the aliases a creator stamp may carry)
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
plus the aliases a creator stamp may carry). The model declares no wildcard
|
|
38
|
+
subject and no condition, so a check carries no context and the parser refuses
|
|
39
|
+
the `type:*` notation.
|
|
40
40
|
- `person.ts` — the one construction of a `Person`: `resolvePerson` reads the
|
|
41
41
|
caller's account through the port the way whoAmI does, `personFor` builds the
|
|
42
42
|
aliases (the account id and the issuer subject; an email is nobody's) and
|
|
@@ -70,8 +70,8 @@ editions; the cloud stores what open source computes.
|
|
|
70
70
|
|
|
71
71
|
- `authorizer.ts` — the `Authorizer` driver, arm for arm the cloud's
|
|
72
72
|
(`stigmer-cloud src/authorizer/fga-authorizer.ts`): the four pre-check denials
|
|
73
|
-
with the Java copy; a kind this edition does not serve denied (
|
|
74
|
-
|
|
73
|
+
with the Java copy; a kind this edition does not serve denied (a platform
|
|
74
|
+
capability no self-host holds); the target loaded once through the source;
|
|
75
75
|
`not-found` for a missing row except on the kinds the cloud never probes
|
|
76
76
|
(`NOT_FOUND_EXEMPT_KINDS`: `identity_account`, `iam_policy` — account ids
|
|
77
77
|
cannot be enumerated); the model's answer with an empty reason on a denial so
|
|
@@ -87,15 +87,14 @@ editions; the cloud stores what open source computes.
|
|
|
87
87
|
counts against the schedule instead of retrying forever.
|
|
88
88
|
- `list-read-scope.ts` — the `ListReadScope` driver, the cloud's twin
|
|
89
89
|
(`stigmer-cloud src/iam/list-read-scope.ts`): every candidate a list lane
|
|
90
|
-
offers is answered by `can_view
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
bug, loud.
|
|
90
|
+
offers is answered by `can_view`, the same question a get asks — one evaluator
|
|
91
|
+
walk per candidate over one source seeded with the candidates' facts, so no
|
|
92
|
+
candidate row is read twice and a parent hop reads each distinct parent once;
|
|
93
|
+
the enumeration verb (search, recent activity, the two execution summaries —
|
|
94
|
+
the console library rides it) scans the kind and evaluates the same way, a row
|
|
95
|
+
that does not decode skipped; the `internal` class keeps everything (the
|
|
96
|
+
in-process skip); a fault throws and is never an empty answer; an undeclared
|
|
97
|
+
kind is a consumer bug, loud.
|
|
99
98
|
- `posture.ts` — the three postures, named once for the composition root and the
|
|
100
99
|
boot log.
|
|
101
100
|
|
|
@@ -110,15 +109,14 @@ kind. `model/__tests__/registry.test.ts` pins the declared set to the
|
|
|
110
109
|
open-source tier and each kind's wire permissions to its file;
|
|
111
110
|
`__tests__/wire-permissions.test.ts` pins that every static `(kind, permission)`
|
|
112
111
|
a served RPC asks about is a declared relation. The rest pin the machinery:
|
|
113
|
-
alias matching, the
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
`
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
`AUTHORIZATION_MEASURE=1`).
|
|
112
|
+
alias matching, the memo, the bounds, the source and the two derived rules on
|
|
113
|
+
both store drivers. The four drivers are pinned arm by arm on both store drivers
|
|
114
|
+
(`__tests__/authorizer.test.ts`, `organization-directory.test.ts`,
|
|
115
|
+
`schedule-fire-caller.test.ts`, `list-read-scope.test.ts` — the adversarial
|
|
116
|
+
cells first: the outsider, the viewer rung, the admin who does not read members'
|
|
117
|
+
conversations, the orphaned execution, the memory with no subject), the posture
|
|
118
|
+
at the wire over three real boots
|
|
119
|
+
(`extensions/__tests__/built-in-authorization-composed.test.ts`, the list lanes
|
|
120
|
+
included, and the two-boot proof that the sole person's lists are byte-identical
|
|
121
|
+
with and without the scope), and the scope's cost is measured rather than
|
|
122
|
+
assumed (`list-read-scope.measure.test.ts`, gated on `AUTHORIZATION_MEASURE=1`).
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
* Pins the built-in Authorizer arm by arm, on both store drivers, against
|
|
3
3
|
* the cloud's driver it mirrors (stigmer-cloud src/authorizer/
|
|
4
4
|
* fga-authorizer.ts): the four pre-check denials with the Java copy; the
|
|
5
|
-
* unserved-kind denial (
|
|
6
|
-
visibility on a self-host"); not-found for a
|
|
5
|
+
* unserved-kind denial (a platform capability no self-host holds); not-found for a
|
|
7
6
|
* missing target and a DENIAL for a missing row of an exempt kind (the
|
|
8
7
|
* cloud's probe scope, so account ids cannot be enumerated); allow and
|
|
9
8
|
* deny from the model over a seeded organization — the owner, an admin, a
|
|
@@ -222,9 +221,11 @@ describe.each(driverFixtures(SEEDED_KINDS))(
|
|
|
222
221
|
await grant(orgRole(FOUNDER.accountId, "owner", "acme"));
|
|
223
222
|
await grant(orgRole(ADMIN.accountId, "admin", "acme"));
|
|
224
223
|
await grant(orgRole(MEMBER.accountId, "member", "acme"));
|
|
225
|
-
// The founder's blueprints: one private, one org-visible, one
|
|
226
|
-
//
|
|
227
|
-
//
|
|
224
|
+
// The founder's blueprints: one private, one org-visible, and one
|
|
225
|
+
// still carrying the retired public level (a row the store
|
|
226
|
+
// migration has not met); the org-visible one stamped with the
|
|
227
|
+
// founder's RAW subject (a row a 3.14.x verifier wrote), the
|
|
228
|
+
// others with the id.
|
|
228
229
|
await seed("agent", "agt_private", {
|
|
229
230
|
org: "acme",
|
|
230
231
|
visibility: ApiResourceVisibility.visibility_private,
|
|
@@ -287,10 +288,10 @@ describe.each(driverFixtures(SEEDED_KINDS))(
|
|
|
287
288
|
UNKNOWN_KIND_DENY_REASON,
|
|
288
289
|
],
|
|
289
290
|
[
|
|
290
|
-
"a kind this edition does not serve (platform — nobody
|
|
291
|
+
"a kind this edition does not serve (platform — nobody holds an operator capability)",
|
|
291
292
|
resolved(FOUNDER),
|
|
292
293
|
check(
|
|
293
|
-
IamPermission.
|
|
294
|
+
IamPermission.can_view_provider_standing,
|
|
294
295
|
ApiResourceKind.platform,
|
|
295
296
|
"stigmer",
|
|
296
297
|
),
|
|
@@ -465,7 +466,7 @@ describe.each(driverFixtures(SEEDED_KINDS))(
|
|
|
465
466
|
});
|
|
466
467
|
});
|
|
467
468
|
|
|
468
|
-
it("an unprovisioned subject reads a
|
|
469
|
+
it("an unprovisioned subject reads nothing — not a private agent, and not a row still carrying the retired public level, which grants no one anything", async () => {
|
|
469
470
|
const auth = authorizer();
|
|
470
471
|
expect(
|
|
471
472
|
await auth.authorize(
|
|
@@ -476,7 +477,7 @@ describe.each(driverFixtures(SEEDED_KINDS))(
|
|
|
476
477
|
"agt_public",
|
|
477
478
|
),
|
|
478
479
|
),
|
|
479
|
-
).toEqual({ kind: "
|
|
480
|
+
).toEqual({ kind: "deny", reason: "" });
|
|
480
481
|
expect(
|
|
481
482
|
await auth.authorize(
|
|
482
483
|
idpShaped(STRANGER),
|
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* built-in authorizer: which tuples a stored row stands for, in exactly
|
|
4
4
|
* the shapes the cloud's tuple driver writes (stigmer-cloud
|
|
5
5
|
* iam/tuple-lifecycle.ts: `organization:<org>#viewer` for the org level,
|
|
6
|
-
* the
|
|
7
|
-
* kind's attribution, the parent links by `kind_meta`). Two facts are the
|
|
6
|
+
* the owner by the kind's attribution, the parent links by `kind_meta`). Two facts are the
|
|
8
7
|
* OSS edition's own and are pinned here by name: the organization's owner
|
|
9
8
|
* is a ROW (2b's role lifecycle), never derived from its creator stamp,
|
|
10
9
|
* or a revoked founder would stay owner; and a stamp that names no person
|
|
@@ -96,7 +95,7 @@ describe("deriveTuples — the cloud driver's shapes, from the row", () => {
|
|
|
96
95
|
]);
|
|
97
96
|
});
|
|
98
97
|
|
|
99
|
-
it("a
|
|
98
|
+
it("a row that still carries the retired public level derives no viewer tuple at all — no shape reaches every account, and the level's own org floor left with it", () => {
|
|
100
99
|
expect(
|
|
101
100
|
derivedFor("skill", {
|
|
102
101
|
visibility: ApiResourceVisibility.visibility_public,
|
|
@@ -104,8 +103,6 @@ describe("deriveTuples — the cloud driver's shapes, from the row", () => {
|
|
|
104
103
|
).toEqual([
|
|
105
104
|
"skill:skill-1#organization@organization:acme",
|
|
106
105
|
"skill:skill-1#owner@identity_account:ida_carol",
|
|
107
|
-
"skill:skill-1#viewer@identity_account:* with allow_public",
|
|
108
|
-
"skill:skill-1#viewer@organization:acme#viewer",
|
|
109
106
|
]);
|
|
110
107
|
});
|
|
111
108
|
|
|
@@ -166,7 +163,7 @@ describe("deriveTuples — the cloud driver's shapes, from the row", () => {
|
|
|
166
163
|
).toEqual(["memory:memory-1#organization@organization:acme"]);
|
|
167
164
|
});
|
|
168
165
|
|
|
169
|
-
it("an environment carries its creator tuple
|
|
166
|
+
it("an environment carries its creator tuple and an org-viewer at org level, and nothing beyond its org ceiling", () => {
|
|
170
167
|
expect(
|
|
171
168
|
derivedFor("environment", {
|
|
172
169
|
visibility: ApiResourceVisibility.visibility_org,
|
|
@@ -179,7 +176,7 @@ describe("deriveTuples — the cloud driver's shapes, from the row", () => {
|
|
|
179
176
|
]);
|
|
180
177
|
expect(
|
|
181
178
|
derivedFor("environment", {
|
|
182
|
-
visibility: ApiResourceVisibility.
|
|
179
|
+
visibility: ApiResourceVisibility.visibility_platform,
|
|
183
180
|
}),
|
|
184
181
|
).toEqual([
|
|
185
182
|
"environment:environment-1#organization@organization:acme",
|
|
@@ -509,9 +506,6 @@ describe.each(driverFixtures(SEEDED_KINDS))(
|
|
|
509
506
|
object,
|
|
510
507
|
"can_view",
|
|
511
508
|
DAVE,
|
|
512
|
-
{
|
|
513
|
-
allow: false,
|
|
514
|
-
},
|
|
515
509
|
),
|
|
516
510
|
).toBe(true);
|
|
517
511
|
expect(rowReads).toBe(1);
|
|
@@ -707,7 +701,6 @@ describe.each(driverFixtures(SEEDED_KINDS))(
|
|
|
707
701
|
parseObjectRef(object),
|
|
708
702
|
relation,
|
|
709
703
|
person,
|
|
710
|
-
{ allow: true },
|
|
711
704
|
);
|
|
712
705
|
}
|
|
713
706
|
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pins the evaluator's semantics the store tests cannot express (the
|
|
3
3
|
* cloud's `.fga.yaml` documents prove the model's answers; this file
|
|
4
|
-
* proves the machinery under them): how a person matches a tuple,
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* target is a thrown fault and never a silent deny.
|
|
4
|
+
* proves the machinery under them): how a person matches a tuple, that a
|
|
5
|
+
* direct tuple outside the line's type restriction is ignored (model drift
|
|
6
|
+
* fails closed), how far a resolution may walk, and that a declaration
|
|
7
|
+
* cycle or an undeclared target is a thrown fault and never a silent deny.
|
|
9
8
|
*
|
|
10
9
|
* The fixture source is the in-memory one the store-test kit uses, so the
|
|
11
10
|
* two proofs share one tuple vocabulary. Where a case needs a shape the
|
|
@@ -30,7 +29,6 @@ import {
|
|
|
30
29
|
direct,
|
|
31
30
|
from,
|
|
32
31
|
objectOf,
|
|
33
|
-
publicWith,
|
|
34
32
|
union,
|
|
35
33
|
usersetOf,
|
|
36
34
|
} from "../model/rewrite.js";
|
|
@@ -56,22 +54,17 @@ function person(accountId: string, ...aliases: string[]): Person {
|
|
|
56
54
|
return { accountId, aliases: new Set([accountId, ...aliases]) };
|
|
57
55
|
}
|
|
58
56
|
|
|
59
|
-
const RESOLVE = { allow: true };
|
|
60
|
-
const LIST = { allow: false };
|
|
61
|
-
|
|
62
57
|
async function allowed(
|
|
63
58
|
source: TupleSource,
|
|
64
59
|
object: string,
|
|
65
60
|
relation: string,
|
|
66
61
|
who: Person,
|
|
67
|
-
context = RESOLVE,
|
|
68
62
|
): Promise<boolean> {
|
|
69
63
|
return checkRelation(
|
|
70
64
|
{ model: builtInModel, source },
|
|
71
65
|
parseObjectRef(object),
|
|
72
66
|
relation,
|
|
73
67
|
who,
|
|
74
|
-
context,
|
|
75
68
|
);
|
|
76
69
|
}
|
|
77
70
|
|
|
@@ -100,67 +93,17 @@ describe("a person against a direct tuple", () => {
|
|
|
100
93
|
});
|
|
101
94
|
});
|
|
102
95
|
|
|
103
|
-
describe("
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
},
|
|
112
|
-
});
|
|
113
|
-
const source = newInMemoryTupleSource([
|
|
114
|
-
tuple("agent:open#organization@organization:acme"),
|
|
115
|
-
publicViewer("agent:open"),
|
|
116
|
-
]);
|
|
117
|
-
const stranger = person("ida_mallory");
|
|
118
|
-
|
|
119
|
-
it("reads for anyone on a point check (the cloud's `allow: true`) and for nobody in a listing (`allow: false`)", async () => {
|
|
96
|
+
describe("a stranger against a row with no grant that names them", () => {
|
|
97
|
+
it("reads nothing: no subject form reaches every account, so a stranger holds no relation on a row they were not granted", async () => {
|
|
98
|
+
const source = newInMemoryTupleSource([
|
|
99
|
+
tuple("agent:open#organization@organization:acme"),
|
|
100
|
+
tuple("agent:open#owner@identity_account:ida_carol"),
|
|
101
|
+
tuple("agent:open#viewer@organization:acme#viewer"),
|
|
102
|
+
]);
|
|
103
|
+
const stranger = person("ida_mallory");
|
|
120
104
|
expect(await allowed(source, "agent:open", "can_view", stranger)).toBe(
|
|
121
|
-
|
|
105
|
+
false,
|
|
122
106
|
);
|
|
123
|
-
expect(
|
|
124
|
-
await allowed(source, "agent:open", "can_view", stranger, LIST),
|
|
125
|
-
).toBe(false);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("ignores a wildcard tuple that carries no condition where the line demands one — a tuple the cloud could never have written", async () => {
|
|
129
|
-
const bare = newInMemoryTupleSource([
|
|
130
|
-
tuple("agent:open#viewer@identity_account:*"),
|
|
131
|
-
]);
|
|
132
|
-
expect(await allowed(bare, "agent:open", "can_view", stranger)).toBe(false);
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it("is a thrown fault under a condition the transcript names but the evaluator does not know — a model that grew a condition", async () => {
|
|
136
|
-
const grown = declareKind({
|
|
137
|
-
kind: ApiResourceKind.agent,
|
|
138
|
-
schema: AgentSchema,
|
|
139
|
-
source: "test/grown.fga",
|
|
140
|
-
relations: [
|
|
141
|
-
["viewer", direct(publicWith("identity_account", "after_hours"))],
|
|
142
|
-
],
|
|
143
|
-
});
|
|
144
|
-
const afterHours = newInMemoryTupleSource([
|
|
145
|
-
{
|
|
146
|
-
object: parseObjectRef("agent:open"),
|
|
147
|
-
relation: "viewer",
|
|
148
|
-
subject: {
|
|
149
|
-
form: "wildcard",
|
|
150
|
-
type: "identity_account",
|
|
151
|
-
condition: "after_hours",
|
|
152
|
-
},
|
|
153
|
-
},
|
|
154
|
-
]);
|
|
155
|
-
await expect(
|
|
156
|
-
checkRelation(
|
|
157
|
-
{ model: newModel([grown]), source: afterHours },
|
|
158
|
-
parseObjectRef("agent:open"),
|
|
159
|
-
"viewer",
|
|
160
|
-
stranger,
|
|
161
|
-
RESOLVE,
|
|
162
|
-
),
|
|
163
|
-
).rejects.toMatchObject({ reason: "unknown-condition" });
|
|
164
107
|
});
|
|
165
108
|
});
|
|
166
109
|
|
|
@@ -277,7 +220,6 @@ describe("the walk itself", () => {
|
|
|
277
220
|
parseObjectRef("agent:n0"),
|
|
278
221
|
"next",
|
|
279
222
|
person("ida_nobody"),
|
|
280
|
-
RESOLVE,
|
|
281
223
|
),
|
|
282
224
|
).rejects.toMatchObject({ reason: "resolution-depth-exceeded" });
|
|
283
225
|
});
|
|
@@ -298,7 +240,6 @@ describe("the walk itself", () => {
|
|
|
298
240
|
parseObjectRef("agent:x"),
|
|
299
241
|
"a",
|
|
300
242
|
person("ida_nobody"),
|
|
301
|
-
RESOLVE,
|
|
302
243
|
),
|
|
303
244
|
).rejects.toBeInstanceOf(AuthorizationEvaluationError);
|
|
304
245
|
});
|
|
@@ -332,7 +273,7 @@ describe("declareKind validates a transcript at load", () => {
|
|
|
332
273
|
source: "test/bad.fga",
|
|
333
274
|
relations: [
|
|
334
275
|
["viewer", direct(objectOf("identity_account"))],
|
|
335
|
-
["viewer", direct(
|
|
276
|
+
["viewer", direct(usersetOf("organization", "viewer"))],
|
|
336
277
|
],
|
|
337
278
|
}),
|
|
338
279
|
).toThrow("declared twice");
|
|
@@ -50,7 +50,7 @@ describe("rowFactsOf against the tuple lifecycle's own resolution", () => {
|
|
|
50
50
|
for (const visibility of [
|
|
51
51
|
ApiResourceVisibility.visibility_private,
|
|
52
52
|
ApiResourceVisibility.visibility_org,
|
|
53
|
-
ApiResourceVisibility.
|
|
53
|
+
ApiResourceVisibility.visibility_platform,
|
|
54
54
|
]) {
|
|
55
55
|
it(`${declaration.type} at ${ApiResourceVisibility[visibility]}: the same parent links and visibility shapes`, () => {
|
|
56
56
|
const row = fixtureRow(declaration, {
|
|
@@ -133,7 +133,7 @@ describe("a list candidate against the loaded row — the facts' two producers",
|
|
|
133
133
|
for (const visibility of [
|
|
134
134
|
ApiResourceVisibility.visibility_private,
|
|
135
135
|
ApiResourceVisibility.visibility_org,
|
|
136
|
-
ApiResourceVisibility.
|
|
136
|
+
ApiResourceVisibility.visibility_platform,
|
|
137
137
|
]) {
|
|
138
138
|
it(`${declaration.type} at ${ApiResourceVisibility[visibility]}: the candidate the helper builds carries the facts the driver derives from`, async () => {
|
|
139
139
|
const row = fixtureRow(declaration, {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Pinned copy of stigmer-cloud backend/services/stigmer-server/fga/tests/agent-instance-creation.fga.yaml
|
|
2
|
-
#
|
|
2
|
+
# as changed by stigmer-cloud#765 (the OpenFGA store test the cloud runs against its live model).
|
|
3
3
|
# The body below is byte-identical to the source; the cloud re-runs the LIVE
|
|
4
4
|
# file against this evaluator at the re-pin, so a stale copy is caught there.
|
|
5
5
|
# Store tests for agent-instance creation: two halves, asked together by
|
|
@@ -8,15 +8,15 @@
|
|
|
8
8
|
# agent may instantiate it). The organization half is
|
|
9
9
|
# can_create_agent_instance: member on the organization the instance is
|
|
10
10
|
# created IN, which is the caller's own, not the agent's: a personal
|
|
11
|
-
# instance of another organization's
|
|
12
|
-
# organization (the
|
|
13
|
-
# would let a row be planted in any organization named on the request.
|
|
11
|
+
# instance of another organization's platform-visible agent lives in the
|
|
12
|
+
# caller's organization (the tenant-family case), so the agent's permission
|
|
13
|
+
# alone would let a row be planted in any organization named on the request.
|
|
14
14
|
#
|
|
15
15
|
# What these pin: owners, admins and members hold the organization half;
|
|
16
16
|
# a viewer-role account may run an org-visible agent (the agent half) and
|
|
17
17
|
# still lacks the organization half; a guest holds neither; an outsider
|
|
18
|
-
# holds neither; and the
|
|
19
|
-
# organization while the agent half comes from the
|
|
18
|
+
# holds neither; and the tenant-family case passes on the caller's own
|
|
19
|
+
# organization while the agent half comes from the platform_viewer grant.
|
|
20
20
|
#
|
|
21
21
|
# Run: fga model test --tests agent-instance-creation.fga.yaml
|
|
22
22
|
|
|
@@ -47,6 +47,15 @@ tuples:
|
|
|
47
47
|
relation: member
|
|
48
48
|
object: organization:consumer
|
|
49
49
|
|
|
50
|
+
# acme owns identity_provider:idp1, which manages consumer: the two are
|
|
51
|
+
# one tenant family, the audience a platform-visible agent reaches.
|
|
52
|
+
- user: organization:acme
|
|
53
|
+
relation: organization
|
|
54
|
+
object: identity_provider:idp1
|
|
55
|
+
- user: organization:consumer
|
|
56
|
+
relation: managed_org
|
|
57
|
+
object: identity_provider:idp1
|
|
58
|
+
|
|
50
59
|
# An ORG-visible agent in acme, authored by oscar.
|
|
51
60
|
- user: organization:acme
|
|
52
61
|
relation: organization
|
|
@@ -66,7 +75,7 @@ tuples:
|
|
|
66
75
|
relation: owner
|
|
67
76
|
object: agent:secret-bot
|
|
68
77
|
|
|
69
|
-
# A
|
|
78
|
+
# A PLATFORM-visible agent in acme (the platform_viewer grant plus the
|
|
70
79
|
# org floor tuple the visibility writer emits).
|
|
71
80
|
- user: organization:acme
|
|
72
81
|
relation: organization
|
|
@@ -74,12 +83,9 @@ tuples:
|
|
|
74
83
|
- user: identity_account:oscar
|
|
75
84
|
relation: owner
|
|
76
85
|
object: agent:open-bot
|
|
77
|
-
- user:
|
|
78
|
-
relation:
|
|
86
|
+
- user: identity_provider:idp1#platform_user
|
|
87
|
+
relation: platform_viewer
|
|
79
88
|
object: agent:open-bot
|
|
80
|
-
condition:
|
|
81
|
-
name: allow_public
|
|
82
|
-
context: {}
|
|
83
89
|
- user: organization:acme#viewer
|
|
84
90
|
relation: viewer
|
|
85
91
|
object: agent:open-bot
|
|
@@ -121,8 +127,6 @@ tests:
|
|
|
121
127
|
check:
|
|
122
128
|
- user: identity_account:dave
|
|
123
129
|
object: agent:team-bot
|
|
124
|
-
context:
|
|
125
|
-
allow: false
|
|
126
130
|
assertions:
|
|
127
131
|
can_create_instance: true
|
|
128
132
|
can_execute: true
|
|
@@ -131,8 +135,6 @@ tests:
|
|
|
131
135
|
check:
|
|
132
136
|
- user: identity_account:vera
|
|
133
137
|
object: agent:team-bot
|
|
134
|
-
context:
|
|
135
|
-
allow: false
|
|
136
138
|
assertions:
|
|
137
139
|
can_create_instance: true
|
|
138
140
|
- user: identity_account:vera
|
|
@@ -144,35 +146,25 @@ tests:
|
|
|
144
146
|
check:
|
|
145
147
|
- user: identity_account:oscar
|
|
146
148
|
object: agent:secret-bot
|
|
147
|
-
context:
|
|
148
|
-
allow: false
|
|
149
149
|
assertions:
|
|
150
150
|
can_create_instance: true
|
|
151
151
|
- user: identity_account:olivia
|
|
152
152
|
object: agent:secret-bot
|
|
153
|
-
context:
|
|
154
|
-
allow: false
|
|
155
153
|
assertions:
|
|
156
154
|
can_create_instance: true
|
|
157
155
|
- user: identity_account:dave
|
|
158
156
|
object: agent:secret-bot
|
|
159
|
-
context:
|
|
160
|
-
allow: false
|
|
161
157
|
assertions:
|
|
162
158
|
can_create_instance: false
|
|
163
159
|
- user: identity_account:mia
|
|
164
160
|
object: agent:secret-bot
|
|
165
|
-
context:
|
|
166
|
-
allow: false
|
|
167
161
|
assertions:
|
|
168
162
|
can_create_instance: false
|
|
169
163
|
|
|
170
|
-
- name: the
|
|
164
|
+
- name: the tenant-family case, the agent half from platform_viewer and the organization half at home
|
|
171
165
|
check:
|
|
172
166
|
- user: identity_account:mia
|
|
173
167
|
object: agent:open-bot
|
|
174
|
-
context:
|
|
175
|
-
allow: true
|
|
176
168
|
assertions:
|
|
177
169
|
can_create_instance: true
|
|
178
170
|
- user: identity_account:mia
|