@stigmer/server 3.40.0 → 3.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authorization/authorizer.d.ts +16 -10
- package/dist/authorization/authorizer.d.ts.map +1 -1
- package/dist/authorization/authorizer.js +17 -9
- package/dist/authorization/authorizer.js.map +1 -1
- package/dist/authorization/derived-tuples.d.ts +7 -3
- package/dist/authorization/derived-tuples.d.ts.map +1 -1
- package/dist/authorization/derived-tuples.js +62 -6
- package/dist/authorization/derived-tuples.js.map +1 -1
- package/dist/authorization/evaluator.d.ts +11 -7
- package/dist/authorization/evaluator.d.ts.map +1 -1
- package/dist/authorization/evaluator.js +16 -10
- package/dist/authorization/evaluator.js.map +1 -1
- package/dist/authorization/list-read-scope.d.ts +4 -0
- package/dist/authorization/list-read-scope.d.ts.map +1 -1
- package/dist/authorization/list-read-scope.js +1 -0
- package/dist/authorization/list-read-scope.js.map +1 -1
- package/dist/authorization/model/data/authorization-model.json +830 -67
- package/dist/authorization/model/execution-viewer.d.ts.map +1 -1
- package/dist/authorization/model/execution-viewer.js +26 -24
- package/dist/authorization/model/execution-viewer.js.map +1 -1
- package/dist/authorization/model/index.d.ts +4 -1
- package/dist/authorization/model/index.d.ts.map +1 -1
- package/dist/authorization/model/index.js +4 -1
- package/dist/authorization/model/index.js.map +1 -1
- package/dist/authorization/model/rewrite.d.ts +5 -3
- package/dist/authorization/model/rewrite.d.ts.map +1 -1
- package/dist/authorization/organization-directory.d.ts +44 -0
- package/dist/authorization/organization-directory.d.ts.map +1 -1
- package/dist/authorization/organization-directory.js +1 -0
- package/dist/authorization/organization-directory.js.map +1 -1
- package/dist/authorization/person.d.ts +14 -1
- package/dist/authorization/person.d.ts.map +1 -1
- package/dist/authorization/person.js +16 -0
- package/dist/authorization/person.js.map +1 -1
- package/dist/authorization/policy-check.d.ts +52 -0
- package/dist/authorization/policy-check.d.ts.map +1 -0
- package/dist/authorization/policy-check.js +35 -0
- package/dist/authorization/policy-check.js.map +1 -0
- package/dist/authorization/posture.d.ts +30 -4
- package/dist/authorization/posture.d.ts.map +1 -1
- package/dist/authorization/posture.js +58 -0
- package/dist/authorization/posture.js.map +1 -1
- package/dist/authorization/resource-creators.d.ts +4 -0
- package/dist/authorization/resource-creators.d.ts.map +1 -0
- package/dist/authorization/resource-creators.js +44 -0
- package/dist/authorization/resource-creators.js.map +1 -0
- package/dist/authorization/trusted-local-authorizer.d.ts +9 -0
- package/dist/authorization/trusted-local-authorizer.d.ts.map +1 -0
- package/dist/authorization/trusted-local-authorizer.js +70 -0
- package/dist/authorization/trusted-local-authorizer.js.map +1 -0
- package/dist/authorization/tuples.d.ts +12 -0
- package/dist/authorization/tuples.d.ts.map +1 -1
- package/dist/authorization/tuples.js +3 -0
- package/dist/authorization/tuples.js.map +1 -1
- package/dist/boot/compose.d.ts.map +1 -1
- package/dist/boot/compose.js +133 -43
- package/dist/boot/compose.js.map +1 -1
- package/dist/boot/config.d.ts +22 -7
- package/dist/boot/config.d.ts.map +1 -1
- package/dist/boot/config.js +2 -1
- package/dist/boot/config.js.map +1 -1
- package/dist/boot/inprocess.d.ts.map +1 -1
- package/dist/boot/inprocess.js +10 -4
- package/dist/boot/inprocess.js.map +1 -1
- package/dist/boot/list-indexes.d.ts.map +1 -1
- package/dist/boot/list-indexes.js +6 -1
- package/dist/boot/list-indexes.js.map +1 -1
- package/dist/boot/logger.d.ts +3 -1
- package/dist/boot/logger.d.ts.map +1 -1
- package/dist/boot/logger.js +3 -1
- package/dist/boot/logger.js.map +1 -1
- package/dist/boot/oauth-redirect-uri.d.ts +33 -12
- package/dist/boot/oauth-redirect-uri.d.ts.map +1 -1
- package/dist/boot/oauth-redirect-uri.js +43 -14
- package/dist/boot/oauth-redirect-uri.js.map +1 -1
- package/dist/boot/skill-transfer-origin.d.ts +46 -14
- package/dist/boot/skill-transfer-origin.d.ts.map +1 -1
- package/dist/boot/skill-transfer-origin.js +53 -13
- package/dist/boot/skill-transfer-origin.js.map +1 -1
- package/dist/domain/agentchannel/steps.d.ts +8 -6
- package/dist/domain/agentchannel/steps.d.ts.map +1 -1
- package/dist/domain/agentchannel/steps.js +9 -7
- package/dist/domain/agentchannel/steps.js.map +1 -1
- package/dist/domain/agentexecution/controller.d.ts +7 -0
- package/dist/domain/agentexecution/controller.d.ts.map +1 -1
- package/dist/domain/agentexecution/controller.js +1 -1
- package/dist/domain/agentexecution/controller.js.map +1 -1
- package/dist/domain/agentexecution/create-steps.d.ts +26 -12
- package/dist/domain/agentexecution/create-steps.d.ts.map +1 -1
- package/dist/domain/agentexecution/create-steps.js +61 -43
- package/dist/domain/agentexecution/create-steps.js.map +1 -1
- package/dist/domain/agentexecution/submit-approval.d.ts.map +1 -1
- package/dist/domain/agentexecution/submit-approval.js +9 -6
- package/dist/domain/agentexecution/submit-approval.js.map +1 -1
- package/dist/domain/agentexecution/submit-file-decision.d.ts.map +1 -1
- package/dist/domain/agentexecution/submit-file-decision.js +5 -4
- package/dist/domain/agentexecution/submit-file-decision.js.map +1 -1
- package/dist/domain/agentshare/steps.d.ts +5 -3
- package/dist/domain/agentshare/steps.d.ts.map +1 -1
- package/dist/domain/agentshare/steps.js +6 -4
- package/dist/domain/agentshare/steps.js.map +1 -1
- package/dist/domain/apikey/verifier.d.ts +7 -0
- package/dist/domain/apikey/verifier.d.ts.map +1 -1
- package/dist/domain/apikey/verifier.js +34 -1
- package/dist/domain/apikey/verifier.js.map +1 -1
- package/dist/domain/channelapp/controller.d.ts +9 -4
- package/dist/domain/channelapp/controller.d.ts.map +1 -1
- package/dist/domain/channelapp/controller.js +19 -9
- package/dist/domain/channelapp/controller.js.map +1 -1
- package/dist/domain/executioncontext/steps.d.ts +2 -1
- package/dist/domain/executioncontext/steps.d.ts.map +1 -1
- package/dist/domain/executioncontext/steps.js +2 -1
- package/dist/domain/executioncontext/steps.js.map +1 -1
- package/dist/domain/iampolicy/access-lists.d.ts.map +1 -1
- package/dist/domain/iampolicy/access-lists.js +57 -2
- package/dist/domain/iampolicy/access-lists.js.map +1 -1
- package/dist/domain/iampolicy/change.d.ts +53 -0
- package/dist/domain/iampolicy/change.d.ts.map +1 -0
- package/dist/domain/iampolicy/change.js +44 -0
- package/dist/domain/iampolicy/change.js.map +1 -0
- package/dist/domain/iampolicy/constants.d.ts +6 -0
- package/dist/domain/iampolicy/constants.d.ts.map +1 -1
- package/dist/domain/iampolicy/constants.js +6 -0
- package/dist/domain/iampolicy/constants.js.map +1 -1
- package/dist/domain/iampolicy/controller.d.ts.map +1 -1
- package/dist/domain/iampolicy/controller.js +9 -5
- package/dist/domain/iampolicy/controller.js.map +1 -1
- package/dist/domain/iampolicy/grant-path.d.ts +31 -13
- package/dist/domain/iampolicy/grant-path.d.ts.map +1 -1
- package/dist/domain/iampolicy/grant-path.js +216 -19
- package/dist/domain/iampolicy/grant-path.js.map +1 -1
- package/dist/domain/iampolicy/membership.d.ts +11 -3
- package/dist/domain/iampolicy/membership.d.ts.map +1 -1
- package/dist/domain/iampolicy/membership.js +18 -13
- package/dist/domain/iampolicy/membership.js.map +1 -1
- package/dist/domain/iampolicy/resource-store.d.ts.map +1 -1
- package/dist/domain/iampolicy/resource-store.js +4 -0
- package/dist/domain/iampolicy/resource-store.js.map +1 -1
- package/dist/domain/iampolicy/role-lifecycle.d.ts.map +1 -1
- package/dist/domain/iampolicy/role-lifecycle.js +2 -2
- package/dist/domain/iampolicy/role-lifecycle.js.map +1 -1
- package/dist/domain/iampolicy/roles.d.ts.map +1 -1
- package/dist/domain/iampolicy/roles.js +7 -0
- package/dist/domain/iampolicy/roles.js.map +1 -1
- package/dist/domain/iampolicy/steps.d.ts +4 -3
- package/dist/domain/iampolicy/steps.d.ts.map +1 -1
- package/dist/domain/iampolicy/steps.js +7 -7
- package/dist/domain/iampolicy/steps.js.map +1 -1
- package/dist/domain/iampolicy/store-contract.d.ts.map +1 -1
- package/dist/domain/iampolicy/store-contract.js +22 -1
- package/dist/domain/iampolicy/store-contract.js.map +1 -1
- package/dist/domain/iampolicy/store.d.ts +18 -5
- package/dist/domain/iampolicy/store.d.ts.map +1 -1
- package/dist/domain/iampolicy/store.js.map +1 -1
- package/dist/domain/identityaccount/constants.d.ts +15 -0
- package/dist/domain/identityaccount/constants.d.ts.map +1 -1
- package/dist/domain/identityaccount/constants.js +17 -0
- package/dist/domain/identityaccount/constants.js.map +1 -1
- package/dist/domain/identityaccount/controller.d.ts.map +1 -1
- package/dist/domain/identityaccount/controller.js +17 -6
- package/dist/domain/identityaccount/controller.js.map +1 -1
- package/dist/domain/identityaccount/resolve.d.ts +47 -0
- package/dist/domain/identityaccount/resolve.d.ts.map +1 -1
- package/dist/domain/identityaccount/resolve.js +38 -1
- package/dist/domain/identityaccount/resolve.js.map +1 -1
- package/dist/domain/identityaccount/resource-store.d.ts.map +1 -1
- package/dist/domain/identityaccount/resource-store.js +11 -1
- package/dist/domain/identityaccount/resource-store.js.map +1 -1
- package/dist/domain/identityaccount/steps.d.ts +2 -1
- package/dist/domain/identityaccount/steps.d.ts.map +1 -1
- package/dist/domain/identityaccount/steps.js +4 -3
- package/dist/domain/identityaccount/steps.js.map +1 -1
- package/dist/domain/identityaccount/store-contract.js +2 -2
- package/dist/domain/identityaccount/store-contract.js.map +1 -1
- package/dist/domain/identityaccount/store.d.ts +6 -1
- package/dist/domain/identityaccount/store.d.ts.map +1 -1
- package/dist/domain/mcpserver/connect-sandbox.d.ts +67 -0
- package/dist/domain/mcpserver/connect-sandbox.d.ts.map +1 -0
- package/dist/domain/mcpserver/connect-sandbox.js +64 -0
- package/dist/domain/mcpserver/connect-sandbox.js.map +1 -0
- package/dist/domain/mcpserver/connect.d.ts +42 -10
- package/dist/domain/mcpserver/connect.d.ts.map +1 -1
- package/dist/domain/mcpserver/connect.js +147 -40
- package/dist/domain/mcpserver/connect.js.map +1 -1
- package/dist/domain/mcpserver/controller.js +4 -2
- package/dist/domain/mcpserver/controller.js.map +1 -1
- package/dist/domain/mcpserver/engine.d.ts +26 -7
- package/dist/domain/mcpserver/engine.d.ts.map +1 -1
- package/dist/domain/mcpserver/engine.js.map +1 -1
- package/dist/domain/mcpserver/start-connect.d.ts +5 -0
- package/dist/domain/mcpserver/start-connect.d.ts.map +1 -1
- package/dist/domain/mcpserver/start-connect.js +38 -15
- package/dist/domain/mcpserver/start-connect.js.map +1 -1
- package/dist/domain/memory/controller.js +2 -1
- package/dist/domain/memory/controller.js.map +1 -1
- package/dist/domain/memory/list-index.d.ts +2 -0
- package/dist/domain/memory/list-index.d.ts.map +1 -0
- package/dist/domain/memory/list-index.js +31 -0
- package/dist/domain/memory/list-index.js.map +1 -0
- package/dist/domain/memory/queries.d.ts +10 -0
- package/dist/domain/memory/queries.d.ts.map +1 -0
- package/dist/domain/memory/queries.js +54 -0
- package/dist/domain/memory/queries.js.map +1 -0
- package/dist/domain/memory/steps.d.ts +13 -11
- package/dist/domain/memory/steps.d.ts.map +1 -1
- package/dist/domain/memory/steps.js +26 -57
- package/dist/domain/memory/steps.js.map +1 -1
- package/dist/domain/organization/steps.d.ts.map +1 -1
- package/dist/domain/organization/steps.js +1 -1
- package/dist/domain/organization/steps.js.map +1 -1
- package/dist/domain/platformclient/mint.d.ts.map +1 -1
- package/dist/domain/platformclient/mint.js +2 -2
- package/dist/domain/platformclient/mint.js.map +1 -1
- package/dist/domain/platformclient/verifier.d.ts.map +1 -1
- package/dist/domain/platformclient/verifier.js +6 -3
- package/dist/domain/platformclient/verifier.js.map +1 -1
- package/dist/domain/plugin/push.d.ts.map +1 -1
- package/dist/domain/plugin/push.js +1 -0
- package/dist/domain/plugin/push.js.map +1 -1
- package/dist/domain/schedule/steps.d.ts +3 -1
- package/dist/domain/schedule/steps.d.ts.map +1 -1
- package/dist/domain/schedule/steps.js +3 -1
- package/dist/domain/schedule/steps.js.map +1 -1
- package/dist/domain/skill/push.d.ts.map +1 -1
- package/dist/domain/skill/push.js +1 -0
- package/dist/domain/skill/push.js.map +1 -1
- package/dist/domain/workflow/registry/data/task-kind-registry.json +27 -1
- package/dist/domain/workflowexecution/submit-approval.d.ts +5 -2
- package/dist/domain/workflowexecution/submit-approval.d.ts.map +1 -1
- package/dist/domain/workflowexecution/submit-approval.js +1 -1
- package/dist/domain/workflowexecution/submit-approval.js.map +1 -1
- package/dist/domain/workflowexecution/submit-file-decision.d.ts +5 -2
- package/dist/domain/workflowexecution/submit-file-decision.d.ts.map +1 -1
- package/dist/domain/workflowexecution/submit-file-decision.js +1 -1
- package/dist/domain/workflowexecution/submit-file-decision.js.map +1 -1
- package/dist/domain/workflowexecution/submit-workflow-task-approval.d.ts.map +1 -1
- package/dist/domain/workflowexecution/submit-workflow-task-approval.js +12 -1
- package/dist/domain/workflowexecution/submit-workflow-task-approval.js.map +1 -1
- package/dist/domain/workflowinstance/controller.d.ts +6 -3
- package/dist/domain/workflowinstance/controller.d.ts.map +1 -1
- package/dist/domain/workflowinstance/controller.js +12 -7
- package/dist/domain/workflowinstance/controller.js.map +1 -1
- package/dist/domain/workflowinstance/steps.d.ts +31 -0
- package/dist/domain/workflowinstance/steps.d.ts.map +1 -1
- package/dist/domain/workflowinstance/steps.js +88 -1
- package/dist/domain/workflowinstance/steps.js.map +1 -1
- package/dist/extensions/composed-services.d.ts +56 -0
- package/dist/extensions/composed-services.d.ts.map +1 -0
- package/dist/extensions/composed-services.js +2 -0
- package/dist/extensions/composed-services.js.map +1 -0
- package/dist/extensions/drivers.d.ts +32 -1
- package/dist/extensions/drivers.d.ts.map +1 -1
- package/dist/extensions/identity.d.ts +4 -1
- package/dist/extensions/identity.d.ts.map +1 -1
- package/dist/extensions/identity.js +4 -1
- package/dist/extensions/identity.js.map +1 -1
- package/dist/extensions/registry.d.ts +37 -0
- package/dist/extensions/registry.d.ts.map +1 -1
- package/dist/extensions/registry.js +50 -0
- package/dist/extensions/registry.js.map +1 -1
- package/dist/extensions/resource-authorization.d.ts +78 -0
- package/dist/extensions/resource-authorization.d.ts.map +1 -1
- package/dist/extensions/resource-row-reader.d.ts +43 -0
- package/dist/extensions/resource-row-reader.d.ts.map +1 -0
- package/dist/extensions/resource-row-reader.js +2 -0
- package/dist/extensions/resource-row-reader.js.map +1 -0
- package/dist/extensions/visitor-classifier.d.ts +41 -0
- package/dist/extensions/visitor-classifier.d.ts.map +1 -0
- package/dist/extensions/visitor-classifier.js +2 -0
- package/dist/extensions/visitor-classifier.js.map +1 -0
- package/dist/identity/oidc-userinfo.d.ts +6 -1
- package/dist/identity/oidc-userinfo.d.ts.map +1 -1
- package/dist/identity/oidc-userinfo.js.map +1 -1
- package/dist/identity/oidc-verifier.d.ts.map +1 -1
- package/dist/identity/oidc-verifier.js +37 -8
- package/dist/identity/oidc-verifier.js.map +1 -1
- package/dist/index.d.ts +14 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -9
- package/dist/index.js.map +1 -1
- package/dist/pipeline/errors.d.ts +8 -0
- package/dist/pipeline/errors.d.ts.map +1 -1
- package/dist/pipeline/errors.js +25 -16
- package/dist/pipeline/errors.js.map +1 -1
- package/dist/pipeline/interceptors/auth.d.ts +17 -9
- package/dist/pipeline/interceptors/auth.d.ts.map +1 -1
- package/dist/pipeline/interceptors/auth.js +17 -9
- package/dist/pipeline/interceptors/auth.js.map +1 -1
- package/dist/pipeline/steps/authorization-tuples.d.ts +23 -1
- package/dist/pipeline/steps/authorization-tuples.d.ts.map +1 -1
- package/dist/pipeline/steps/authorization-tuples.js +42 -0
- package/dist/pipeline/steps/authorization-tuples.js.map +1 -1
- package/dist/pipeline/steps/authorize.d.ts +6 -5
- package/dist/pipeline/steps/authorize.d.ts.map +1 -1
- package/dist/pipeline/steps/authorize.js +6 -5
- package/dist/pipeline/steps/authorize.js.map +1 -1
- package/dist/pipeline/steps/defaults.d.ts +12 -0
- package/dist/pipeline/steps/defaults.d.ts.map +1 -1
- package/dist/pipeline/steps/defaults.js +42 -7
- package/dist/pipeline/steps/defaults.js.map +1 -1
- package/dist/pipeline/steps/guard-memory-capture.d.ts +13 -15
- package/dist/pipeline/steps/guard-memory-capture.d.ts.map +1 -1
- package/dist/pipeline/steps/guard-memory-capture.js +2 -3
- package/dist/pipeline/steps/guard-memory-capture.js.map +1 -1
- package/dist/runnerauth/built-in-runner-credential-provider.d.ts +0 -2
- package/dist/runnerauth/built-in-runner-credential-provider.d.ts.map +1 -1
- package/dist/runnerauth/built-in-runner-credential-provider.js +8 -17
- package/dist/runnerauth/built-in-runner-credential-provider.js.map +1 -1
- package/dist/runnerauth/runner-credential-provider.d.ts +17 -7
- package/dist/runnerauth/runner-credential-provider.d.ts.map +1 -1
- package/dist/runnerauth/runner-credential-provider.js.map +1 -1
- package/dist/sandbox/docker.d.ts.map +1 -1
- package/dist/sandbox/docker.js +3 -0
- package/dist/sandbox/docker.js.map +1 -1
- package/dist/sandbox/kubernetes.d.ts.map +1 -1
- package/dist/sandbox/kubernetes.js +3 -0
- package/dist/sandbox/kubernetes.js.map +1 -1
- package/dist/sandbox/local-process.d.ts.map +1 -1
- package/dist/sandbox/local-process.js +4 -0
- package/dist/sandbox/local-process.js.map +1 -1
- package/dist/sandbox/provisioner.d.ts +18 -6
- package/dist/sandbox/provisioner.d.ts.map +1 -1
- package/dist/sandbox/provisioner.js.map +1 -1
- package/dist/temporal/manager.d.ts +5 -0
- package/dist/temporal/manager.d.ts.map +1 -1
- package/dist/temporal/manager.js +9 -0
- package/dist/temporal/manager.js.map +1 -1
- package/dist/temporal/mcpserver/engine-client.d.ts +2 -2
- package/dist/temporal/mcpserver/engine-client.d.ts.map +1 -1
- package/dist/temporal/mcpserver/engine-client.js +6 -4
- package/dist/temporal/mcpserver/engine-client.js.map +1 -1
- package/dist/temporal/mcpserver/names.d.ts +13 -2
- package/dist/temporal/mcpserver/names.d.ts.map +1 -1
- package/dist/temporal/mcpserver/names.js +15 -2
- package/dist/temporal/mcpserver/names.js.map +1 -1
- package/dist/temporal/sdk-logger.d.ts +74 -0
- package/dist/temporal/sdk-logger.d.ts.map +1 -0
- package/dist/temporal/sdk-logger.js +143 -0
- package/dist/temporal/sdk-logger.js.map +1 -0
- package/package.json +6 -6
- package/src/authorization/README.md +18 -6
- package/src/authorization/__tests__/authorizer.test.ts +5 -1
- package/src/authorization/__tests__/derived-tuples.test.ts +26 -0
- package/src/authorization/__tests__/evaluator.test.ts +4 -0
- package/src/authorization/__tests__/list-read-scope.test.ts +11 -5
- package/src/authorization/__tests__/store-tests.test.ts +3 -1
- package/src/authorization/__tests__/trusted-local-authorizer.test.ts +155 -0
- package/src/authorization/__tests__/unit-owned-kinds.test.ts +420 -0
- package/src/authorization/__tests__/wire-permissions.test.ts +31 -2
- package/src/authorization/authorizer.ts +21 -10
- package/src/authorization/derived-tuples.ts +92 -18
- package/src/authorization/evaluator.ts +30 -15
- package/src/authorization/list-read-scope.ts +4 -0
- package/src/authorization/model/__tests__/registry.test.ts +23 -0
- package/src/authorization/model/data/authorization-model.json +830 -67
- package/src/authorization/model/execution-viewer.ts +32 -27
- package/src/authorization/model/index.ts +4 -1
- package/src/authorization/model/rewrite.ts +5 -3
- package/src/authorization/organization-directory.ts +4 -0
- package/src/authorization/person.ts +32 -1
- package/src/authorization/policy-check.ts +105 -0
- package/src/authorization/posture.ts +45 -4
- package/src/authorization/resource-creators.ts +48 -0
- package/src/authorization/trusted-local-authorizer.ts +91 -0
- package/src/authorization/tuples.ts +17 -0
- package/src/boot/__tests__/compose.test.ts +120 -4
- package/src/boot/__tests__/list-indexes.test.ts +4 -0
- package/src/boot/__tests__/oauth-redirect-uri.test.ts +121 -10
- package/src/boot/__tests__/skill-transfer-origin.test.ts +96 -25
- package/src/boot/compose.ts +154 -61
- package/src/boot/config.ts +28 -12
- package/src/boot/inprocess.ts +18 -4
- package/src/boot/list-indexes.ts +6 -1
- package/src/boot/logger.ts +3 -1
- package/src/boot/oauth-redirect-uri.ts +62 -17
- package/src/boot/skill-transfer-origin.ts +71 -16
- package/src/domain/agent/__tests__/agent.test.ts +42 -1
- package/src/domain/agentchannel/__tests__/agentchannel.test.ts +9 -0
- package/src/domain/agentchannel/__tests__/channel-runtime.test.ts +2 -0
- package/src/domain/agentchannel/__tests__/create-authorization.test.ts +7 -6
- package/src/domain/agentchannel/steps.ts +9 -7
- package/src/domain/agentexecution/__tests__/agentexecution.test.ts +148 -56
- package/src/domain/agentexecution/__tests__/create-steps.test.ts +175 -12
- package/src/domain/agentexecution/__tests__/file-review-seed.ts +91 -0
- package/src/domain/agentexecution/controller.ts +8 -0
- package/src/domain/agentexecution/create-steps.ts +74 -44
- package/src/domain/agentexecution/submit-approval.ts +9 -6
- package/src/domain/agentexecution/submit-file-decision.ts +5 -4
- package/src/domain/agentinstance/__tests__/agentinstance.test.ts +2 -0
- package/src/domain/agentshare/__tests__/agentshare.test.ts +17 -0
- package/src/domain/agentshare/__tests__/create-authorization.test.ts +5 -4
- package/src/domain/agentshare/steps.ts +6 -4
- package/src/domain/apikey/__tests__/verifier.test.ts +73 -1
- package/src/domain/apikey/verifier.ts +45 -1
- package/src/domain/artifact/__tests__/artifact.test.ts +13 -24
- package/src/domain/channelapp/__tests__/channelapp.test.ts +7 -0
- package/src/domain/channelapp/controller.ts +35 -12
- package/src/domain/environment/__tests__/environment.test.ts +13 -0
- package/src/domain/executioncontext/__tests__/executioncontext.test.ts +2 -0
- package/src/domain/executioncontext/steps.ts +2 -1
- package/src/domain/iampolicy/__tests__/access-lists.test.ts +79 -0
- package/src/domain/iampolicy/__tests__/change.test.ts +63 -0
- package/src/domain/iampolicy/__tests__/controller.test.ts +39 -3
- package/src/domain/iampolicy/__tests__/grant-path.test.ts +595 -27
- package/src/domain/iampolicy/__tests__/iampolicy.test.ts +33 -2
- package/src/domain/iampolicy/__tests__/membership.test.ts +47 -5
- package/src/domain/iampolicy/__tests__/resource-store.test.ts +1 -0
- package/src/domain/iampolicy/__tests__/role-lifecycle.test.ts +41 -8
- package/src/domain/iampolicy/__tests__/roles.test.ts +3 -2
- package/src/domain/iampolicy/__tests__/steps.test.ts +75 -6
- package/src/domain/iampolicy/__tests__/support.ts +68 -4
- package/src/domain/iampolicy/access-lists.ts +70 -2
- package/src/domain/iampolicy/change.ts +109 -0
- package/src/domain/iampolicy/constants.ts +8 -0
- package/src/domain/iampolicy/controller.ts +17 -3
- package/src/domain/iampolicy/grant-path.ts +349 -31
- package/src/domain/iampolicy/membership.ts +32 -12
- package/src/domain/iampolicy/resource-store.ts +4 -0
- package/src/domain/iampolicy/role-lifecycle.ts +2 -0
- package/src/domain/iampolicy/roles.ts +8 -0
- package/src/domain/iampolicy/steps.ts +15 -5
- package/src/domain/iampolicy/store-contract.ts +37 -1
- package/src/domain/iampolicy/store.ts +19 -5
- package/src/domain/identityaccount/__tests__/resolve.test.ts +246 -2
- package/src/domain/identityaccount/__tests__/resource-store.test.ts +1 -1
- package/src/domain/identityaccount/constants.ts +20 -0
- package/src/domain/identityaccount/controller.ts +20 -5
- package/src/domain/identityaccount/resolve.ts +80 -1
- package/src/domain/identityaccount/resource-store.ts +14 -1
- package/src/domain/identityaccount/steps.ts +4 -3
- package/src/domain/identityaccount/store-contract.ts +4 -4
- package/src/domain/identityaccount/store.ts +6 -1
- package/src/domain/mcpserver/__tests__/connect-authorization.test.ts +1 -0
- package/src/domain/mcpserver/__tests__/connect.test.ts +358 -15
- package/src/domain/mcpserver/__tests__/mcpserver.test.ts +2 -0
- package/src/domain/mcpserver/__tests__/oauth-handshake.test.ts +2 -0
- package/src/domain/mcpserver/__tests__/store-faults.test.ts +2 -0
- package/src/domain/mcpserver/connect-sandbox.ts +135 -0
- package/src/domain/mcpserver/connect.ts +226 -56
- package/src/domain/mcpserver/controller.ts +8 -2
- package/src/domain/mcpserver/engine.ts +24 -6
- package/src/domain/mcpserver/start-connect.ts +49 -10
- package/src/domain/memory/__tests__/memory.test.ts +92 -22
- package/src/domain/memory/__tests__/queries.test.ts +173 -0
- package/src/domain/memory/controller.ts +2 -1
- package/src/domain/memory/list-index.ts +32 -0
- package/src/domain/memory/queries.ts +72 -0
- package/src/domain/memory/steps.ts +26 -63
- package/src/domain/oauthapp/__tests__/oauthapp.test.ts +14 -0
- package/src/domain/organization/__tests__/support.ts +41 -0
- package/src/domain/organization/steps.ts +1 -0
- package/src/domain/platformclient/mint.ts +6 -2
- package/src/domain/platformclient/verifier.ts +6 -3
- package/src/domain/plugin/__tests__/plugin.test.ts +11 -24
- package/src/domain/plugin/push.ts +1 -0
- package/src/domain/schedule/__tests__/schedule.composed.test.ts +2 -0
- package/src/domain/schedule/steps.ts +3 -1
- package/src/domain/session/__tests__/session.test.ts +2 -0
- package/src/domain/skill/__tests__/skill.test.ts +7 -25
- package/src/domain/skill/push.ts +1 -0
- package/src/domain/workflow/__tests__/workflow.test.ts +2 -0
- package/src/domain/workflow/registry/data/task-kind-registry.json +27 -1
- package/src/domain/workflowexecution/__tests__/forward-attribution-composed.test.ts +270 -0
- package/src/domain/workflowexecution/__tests__/submit-workflow-task-approval.test.ts +196 -0
- package/src/domain/workflowexecution/__tests__/workflowexecution.test.ts +3 -57
- package/src/domain/workflowexecution/submit-approval.ts +9 -2
- package/src/domain/workflowexecution/submit-file-decision.ts +6 -1
- package/src/domain/workflowexecution/submit-workflow-task-approval.ts +23 -5
- package/src/domain/workflowinstance/controller.ts +27 -9
- package/src/domain/workflowinstance/steps.ts +123 -1
- package/src/extensions/__tests__/apikey-before-sign-in-composed.test.ts +172 -0
- package/src/extensions/__tests__/built-in-authorization-composed.test.ts +80 -1
- package/src/extensions/__tests__/composed-services-composed.test.ts +236 -0
- package/src/extensions/__tests__/extension-composition.test.ts +229 -16
- package/src/extensions/__tests__/identity-account-composed.test.ts +84 -4
- package/src/extensions/__tests__/list-read-scope-composed.test.ts +41 -1
- package/src/extensions/__tests__/registry.test.ts +147 -1
- package/src/extensions/__tests__/secret-cleanup-composed.test.ts +2 -0
- package/src/extensions/__tests__/visitor-classifier-points.test.ts +42 -0
- package/src/extensions/composed-services.ts +61 -0
- package/src/extensions/drivers.ts +33 -1
- package/src/extensions/identity.ts +4 -1
- package/src/extensions/registry.ts +105 -0
- package/src/extensions/resource-authorization.ts +87 -0
- package/src/extensions/resource-row-reader.ts +43 -0
- package/src/extensions/visitor-classifier.ts +41 -0
- package/src/identity/__tests__/oidc-verifier.test.ts +38 -12
- package/src/identity/oidc-userinfo.ts +6 -1
- package/src/identity/oidc-verifier.ts +42 -8
- package/src/index.ts +61 -6
- package/src/pipeline/__tests__/steps.test.ts +31 -4
- package/src/pipeline/errors.ts +30 -16
- package/src/pipeline/interceptors/auth.ts +17 -9
- package/src/pipeline/steps/__tests__/authorization-tuples.test.ts +20 -0
- package/src/pipeline/steps/__tests__/guard-memory-capture.test.ts +72 -15
- package/src/pipeline/steps/authorization-tuples.ts +54 -0
- package/src/pipeline/steps/authorize.ts +6 -5
- package/src/pipeline/steps/defaults.ts +52 -7
- package/src/pipeline/steps/guard-memory-capture.ts +15 -20
- package/src/runnerauth/__tests__/built-in-runner-credential-provider.test.ts +1 -1
- package/src/runnerauth/__tests__/runner-subject-composed.test.ts +13 -0
- package/src/runnerauth/bound-execution.ts +2 -2
- package/src/runnerauth/built-in-runner-credential-provider.ts +8 -21
- package/src/runnerauth/runner-credential-provider.ts +17 -7
- package/src/sandbox/__tests__/docker.integration.test.ts +4 -0
- package/src/sandbox/__tests__/kubernetes.test.ts +18 -1
- package/src/sandbox/__tests__/local-process.integration.test.ts +4 -0
- package/src/sandbox/__tests__/provisioner.test.ts +1 -0
- package/src/sandbox/docker.ts +3 -0
- package/src/sandbox/kubernetes.ts +3 -0
- package/src/sandbox/local-process.ts +4 -0
- package/src/sandbox/provisioner.ts +18 -6
- package/src/store/postgres/__tests__/support.ts +11 -2
- package/src/temporal/README.md +1 -0
- package/src/temporal/__tests__/manager.test.ts +56 -4
- package/src/temporal/__tests__/sdk-log-workflows.ts +26 -0
- package/src/temporal/__tests__/sdk-logger.e2e.test.ts +150 -0
- package/src/temporal/__tests__/sdk-logger.test.ts +238 -0
- package/src/temporal/manager.ts +9 -0
- package/src/temporal/mcpserver/engine-client.ts +8 -3
- package/src/temporal/mcpserver/names.ts +16 -2
- package/src/temporal/sdk-logger.ts +173 -0
- package/dist/authorization/lane-admission.d.ts +0 -50
- package/dist/authorization/lane-admission.d.ts.map +0 -1
- package/dist/authorization/lane-admission.js +0 -13
- package/dist/authorization/lane-admission.js.map +0 -1
- package/src/authorization/__tests__/lane-admission.test.ts +0 -115
- package/src/authorization/lane-admission.ts +0 -73
|
@@ -22,6 +22,15 @@
|
|
|
22
22
|
* - revokeOrgAccess revokes the account's direct rows on the
|
|
23
23
|
* organization; the inert org-column arm the cloud carried is gone
|
|
24
24
|
* (Q-OR-9).
|
|
25
|
+
* - Leaving an organization: once an account holds no row on an
|
|
26
|
+
* organization, the rows it holds on that organization's resources
|
|
27
|
+
* go too (shares, team memberships, an owner someone else granted),
|
|
28
|
+
* found through the resources' scope links, each through the revoke
|
|
29
|
+
* order; its authorship (`owner`/`creator` naming the resource's
|
|
30
|
+
* recorded creator) stays, as open source's derived tuple does, for
|
|
31
|
+
* the model's organization bound to govern. revokeOrgAccess always
|
|
32
|
+
* sweeps, so a retry converges; a revoke by spec sweeps only when it
|
|
33
|
+
* removed the last role; cleanupResource never sweeps.
|
|
25
34
|
* - With no lifecycle composed (the OSS default before the built-in
|
|
26
35
|
* posture, and any unit that implements only the three required
|
|
27
36
|
* methods), the path writes rows and notifies nothing.
|
|
@@ -36,6 +45,15 @@
|
|
|
36
45
|
* revoke, so a composition never receives a garbage spec to delete a
|
|
37
46
|
* bare tuple from.
|
|
38
47
|
*
|
|
48
|
+
* - Who and why: every access row the path writes or deletes reaches the
|
|
49
|
+
* store with its change record (the caller's id and class, the door,
|
|
50
|
+
* the row's organization), and nothing else does: no record for a
|
|
51
|
+
* structural link, no write at all on a duplicate grant or a revoke of
|
|
52
|
+
* a row that is not held. The organization is read before any row of
|
|
53
|
+
* the operation goes, so a cleanup that deletes a resource's scope link
|
|
54
|
+
* before its owner still names the owner row's organization. The log
|
|
55
|
+
* lines carry the same facts.
|
|
56
|
+
*
|
|
39
57
|
* The row the path builds is pinned too: the proto's apiVersion const, the
|
|
40
58
|
* derived id, the caller's audit stamp — the cloud's `buildNewPolicy`
|
|
41
59
|
* stamped neither org nor creator, and this path stamps the creator.
|
|
@@ -53,6 +71,7 @@ import type { IamPolicySpec } from "@stigmer/protos/ai/stigmer/iam/iampolicy/v1/
|
|
|
53
71
|
import type { CallerIdentity } from "../../../extensions/identity.js";
|
|
54
72
|
import type { ResourceAuthorizationLifecycle } from "../../../extensions/resource-authorization.js";
|
|
55
73
|
import {
|
|
74
|
+
AFFILIATION_NOT_GRANTABLE_MESSAGE,
|
|
56
75
|
IAM_POLICY_API_VERSION,
|
|
57
76
|
IAM_POLICY_KIND,
|
|
58
77
|
malformedTripleMessage,
|
|
@@ -60,15 +79,19 @@ import {
|
|
|
60
79
|
unknownPrincipalKindMessage,
|
|
61
80
|
unknownResourceKindMessage,
|
|
62
81
|
} from "../constants.js";
|
|
82
|
+
import type { PolicyActor } from "../change.js";
|
|
63
83
|
import { newIamPolicyGrantPath } from "../grant-path.js";
|
|
84
|
+
import type { ResourceCreators } from "../grant-path.js";
|
|
64
85
|
import { DuplicatePolicyError } from "../store.js";
|
|
65
86
|
import {
|
|
87
|
+
NO_RECORDED_CREATORS,
|
|
66
88
|
fakeIamPolicyStore,
|
|
67
89
|
orgRole,
|
|
90
|
+
recordedCreators,
|
|
68
91
|
recordingLifecycle,
|
|
69
92
|
triple,
|
|
70
93
|
} from "./support.js";
|
|
71
|
-
import type { RecordedEvent } from "./support.js";
|
|
94
|
+
import type { FakeIamPolicyStore, RecordedEvent } from "./support.js";
|
|
72
95
|
|
|
73
96
|
const ALICE = "ida_wtr3jcf281yfk9xx61kj59fsme";
|
|
74
97
|
const BOB = "ida_0byc5k14t1e7b7kdxft7hwz1f7";
|
|
@@ -114,9 +137,11 @@ const silentLogger = { debug() {}, info() {}, warn() {}, error() {} };
|
|
|
114
137
|
function pathOver(
|
|
115
138
|
recorded: RecordedEvent[],
|
|
116
139
|
lifecycle: ResourceAuthorizationLifecycle | undefined,
|
|
140
|
+
creators: ResourceCreators = NO_RECORDED_CREATORS,
|
|
117
141
|
) {
|
|
118
142
|
const policies = fakeIamPolicyStore(recorded);
|
|
119
143
|
const path = newIamPolicyGrantPath({
|
|
144
|
+
creators,
|
|
120
145
|
policies,
|
|
121
146
|
lifecycle,
|
|
122
147
|
logger: silentLogger,
|
|
@@ -130,7 +155,7 @@ describe("grant: row before onPolicyGranted", () => {
|
|
|
130
155
|
const { policies, path } = pathOver(recorded, recordingLifecycle(recorded));
|
|
131
156
|
const spec = orgRole(ALICE, "admin", "acme");
|
|
132
157
|
|
|
133
|
-
const result = await path.grant(spec, alice);
|
|
158
|
+
const result = await path.grant(spec, alice, "grant");
|
|
134
159
|
|
|
135
160
|
const id = policyIdFor(spec);
|
|
136
161
|
expect(result.duplicate).toBe(false);
|
|
@@ -150,10 +175,10 @@ describe("grant: row before onPolicyGranted", () => {
|
|
|
150
175
|
const recorded: RecordedEvent[] = [];
|
|
151
176
|
const { path } = pathOver(recorded, recordingLifecycle(recorded));
|
|
152
177
|
const spec = orgRole(ALICE, "admin", "acme");
|
|
153
|
-
const first = await path.grant(spec, alice);
|
|
178
|
+
const first = await path.grant(spec, alice, "grant");
|
|
154
179
|
recorded.length = 0;
|
|
155
180
|
|
|
156
|
-
const second = await path.grant(spec, alice);
|
|
181
|
+
const second = await path.grant(spec, alice, "grant");
|
|
157
182
|
|
|
158
183
|
expect(second.duplicate).toBe(true);
|
|
159
184
|
expect(second.policy).toEqual(first.policy);
|
|
@@ -170,7 +195,9 @@ describe("grant: row before onPolicyGranted", () => {
|
|
|
170
195
|
);
|
|
171
196
|
const spec = orgRole(ALICE, "admin", "acme");
|
|
172
197
|
|
|
173
|
-
await expect(path.grant(spec, alice)).rejects.toThrow(
|
|
198
|
+
await expect(path.grant(spec, alice, "grant")).rejects.toThrow(
|
|
199
|
+
"fga is down",
|
|
200
|
+
);
|
|
174
201
|
|
|
175
202
|
expect(policies.rows.has(policyIdFor(spec))).toBe(true);
|
|
176
203
|
expect(recorded).toEqual([{ kind: "row-save", id: policyIdFor(spec) }]);
|
|
@@ -179,7 +206,7 @@ describe("grant: row before onPolicyGranted", () => {
|
|
|
179
206
|
it("with no lifecycle composed, writes the row and notifies nothing", async () => {
|
|
180
207
|
const recorded: RecordedEvent[] = [];
|
|
181
208
|
const { path } = pathOver(recorded, undefined);
|
|
182
|
-
await path.grant(orgRole(ALICE, "member", "acme"), alice);
|
|
209
|
+
await path.grant(orgRole(ALICE, "member", "acme"), alice, "grant");
|
|
183
210
|
expect(recorded.map((event) => event.kind)).toEqual(["row-save"]);
|
|
184
211
|
});
|
|
185
212
|
|
|
@@ -191,8 +218,8 @@ describe("grant: row before onPolicyGranted", () => {
|
|
|
191
218
|
async onVisibilityChanged() {},
|
|
192
219
|
};
|
|
193
220
|
const { path } = pathOver(recorded, threeMethods);
|
|
194
|
-
await path.grant(orgRole(ALICE, "member", "acme"), alice);
|
|
195
|
-
await path.revokeBySpec(orgRole(ALICE, "member", "acme"));
|
|
221
|
+
await path.grant(orgRole(ALICE, "member", "acme"), alice, "grant");
|
|
222
|
+
await path.revokeBySpec(orgRole(ALICE, "member", "acme"), alice);
|
|
196
223
|
expect(recorded.map((event) => event.kind)).toEqual([
|
|
197
224
|
"row-save",
|
|
198
225
|
"row-delete",
|
|
@@ -207,7 +234,7 @@ describe("the row for a triple is found by triple, so legacy ids converge", () =
|
|
|
207
234
|
const spec = orgRole(ALICE, "admin", "acme");
|
|
208
235
|
policies.rows.set(LEGACY_ID, legacyRow(spec));
|
|
209
236
|
|
|
210
|
-
const result = await path.grant(spec, alice);
|
|
237
|
+
const result = await path.grant(spec, alice, "grant");
|
|
211
238
|
|
|
212
239
|
expect(result.duplicate).toBe(true);
|
|
213
240
|
expect(result.policy.metadata?.id).toBe(LEGACY_ID);
|
|
@@ -223,7 +250,7 @@ describe("the row for a triple is found by triple, so legacy ids converge", () =
|
|
|
223
250
|
const spec = orgRole(ALICE, "admin", "acme");
|
|
224
251
|
policies.rows.set(LEGACY_ID, legacyRow(spec));
|
|
225
252
|
|
|
226
|
-
const revoked = await path.revokeBySpec(spec);
|
|
253
|
+
const revoked = await path.revokeBySpec(spec, alice);
|
|
227
254
|
|
|
228
255
|
expect(revoked?.metadata?.id).toBe(LEGACY_ID);
|
|
229
256
|
expect(policies.rows.size).toBe(0);
|
|
@@ -253,12 +280,13 @@ describe("the row for a triple is found by triple, so legacy ids converge", () =
|
|
|
253
280
|
await racingSave(policy);
|
|
254
281
|
};
|
|
255
282
|
const path = newIamPolicyGrantPath({
|
|
283
|
+
creators: NO_RECORDED_CREATORS,
|
|
256
284
|
policies,
|
|
257
285
|
lifecycle: recordingLifecycle(recorded),
|
|
258
286
|
logger: silentLogger,
|
|
259
287
|
});
|
|
260
288
|
|
|
261
|
-
const result = await path.grant(spec, bob);
|
|
289
|
+
const result = await path.grant(spec, bob, "grant");
|
|
262
290
|
|
|
263
291
|
expect(result.duplicate).toBe(true);
|
|
264
292
|
expect(result.policy.status).toBeUndefined();
|
|
@@ -298,7 +326,9 @@ describe("the one writer is the one gate: nothing is read, written or notified f
|
|
|
298
326
|
[bothUnknown, unknownResourceKindMessage("organisation")],
|
|
299
327
|
];
|
|
300
328
|
for (const [spec, message] of refusals) {
|
|
301
|
-
const error = await invalidArgument(() =>
|
|
329
|
+
const error = await invalidArgument(() =>
|
|
330
|
+
path.grant(spec, alice, "grant"),
|
|
331
|
+
);
|
|
302
332
|
expect(error.rawMessage, message).toBe(message);
|
|
303
333
|
}
|
|
304
334
|
expect(policies.rows.size).toBe(0);
|
|
@@ -309,7 +339,7 @@ describe("the one writer is the one gate: nothing is read, written or notified f
|
|
|
309
339
|
const recorded: RecordedEvent[] = [];
|
|
310
340
|
const { path } = pathOver(recorded, recordingLifecycle(recorded));
|
|
311
341
|
for (const spec of [unknownResource, unknownPrincipal, malformed]) {
|
|
312
|
-
await invalidArgument(() => path.revokeBySpec(spec));
|
|
342
|
+
await invalidArgument(() => path.revokeBySpec(spec, alice));
|
|
313
343
|
}
|
|
314
344
|
expect(recorded).toEqual([]);
|
|
315
345
|
});
|
|
@@ -324,6 +354,7 @@ describe("the one writer is the one gate: nothing is read, written or notified f
|
|
|
324
354
|
id: "acme",
|
|
325
355
|
}),
|
|
326
356
|
alice,
|
|
357
|
+
"grant",
|
|
327
358
|
),
|
|
328
359
|
);
|
|
329
360
|
expect(error.rawMessage).toBe(
|
|
@@ -337,10 +368,10 @@ describe("revoke: onPolicyRevoked before the row delete", () => {
|
|
|
337
368
|
const recorded: RecordedEvent[] = [];
|
|
338
369
|
const { policies, path } = pathOver(recorded, recordingLifecycle(recorded));
|
|
339
370
|
const spec = orgRole(ALICE, "admin", "acme");
|
|
340
|
-
await path.grant(spec, alice);
|
|
371
|
+
await path.grant(spec, alice, "grant");
|
|
341
372
|
recorded.length = 0;
|
|
342
373
|
|
|
343
|
-
const revoked = await path.revokeBySpec(spec);
|
|
374
|
+
const revoked = await path.revokeBySpec(spec, alice);
|
|
344
375
|
|
|
345
376
|
const id = policyIdFor(spec);
|
|
346
377
|
expect(revoked?.metadata?.id).toBe(id);
|
|
@@ -358,10 +389,10 @@ describe("revoke: onPolicyRevoked before the row delete", () => {
|
|
|
358
389
|
recordingLifecycle(recorded, { revoked: new Error("fga is down") }),
|
|
359
390
|
);
|
|
360
391
|
const spec = orgRole(ALICE, "admin", "acme");
|
|
361
|
-
await path.grant(spec, alice);
|
|
392
|
+
await path.grant(spec, alice, "grant");
|
|
362
393
|
recorded.length = 0;
|
|
363
394
|
|
|
364
|
-
await expect(path.revokeBySpec(spec)).rejects.toThrow("fga is down");
|
|
395
|
+
await expect(path.revokeBySpec(spec, alice)).rejects.toThrow("fga is down");
|
|
365
396
|
|
|
366
397
|
expect(policies.rows.has(policyIdFor(spec))).toBe(true);
|
|
367
398
|
expect(recorded).toEqual([]);
|
|
@@ -371,7 +402,10 @@ describe("revoke: onPolicyRevoked before the row delete", () => {
|
|
|
371
402
|
const recorded: RecordedEvent[] = [];
|
|
372
403
|
const { path } = pathOver(recorded, recordingLifecycle(recorded));
|
|
373
404
|
|
|
374
|
-
const revoked = await path.revokeBySpec(
|
|
405
|
+
const revoked = await path.revokeBySpec(
|
|
406
|
+
orgRole(BOB, "viewer", "acme"),
|
|
407
|
+
alice,
|
|
408
|
+
);
|
|
375
409
|
|
|
376
410
|
expect(revoked).toBeUndefined();
|
|
377
411
|
expect(recorded).toEqual([
|
|
@@ -394,12 +428,13 @@ describe("cleanupResource: bidirectional, deduplicated, each row through the rev
|
|
|
394
428
|
const unrelated = orgRole(ALICE, "member", "globex");
|
|
395
429
|
// Granted owner first, so the order below is the cleanup's, not the store's.
|
|
396
430
|
for (const spec of [owner, member, asPrincipal, unrelated]) {
|
|
397
|
-
await path.grant(spec, alice);
|
|
431
|
+
await path.grant(spec, alice, "grant");
|
|
398
432
|
}
|
|
399
433
|
recorded.length = 0;
|
|
400
434
|
|
|
401
435
|
await path.cleanupResource(
|
|
402
436
|
create(ApiResourceRefSchema, { kind: "organization", id: "acme" }),
|
|
437
|
+
alice,
|
|
403
438
|
);
|
|
404
439
|
|
|
405
440
|
expect([...policies.rows.keys()]).toEqual([policyIdFor(unrelated)]);
|
|
@@ -430,12 +465,13 @@ describe("cleanupResource: bidirectional, deduplicated, each row through the rev
|
|
|
430
465
|
const owner = orgRole(ALICE, "owner", "acme");
|
|
431
466
|
const member = orgRole(BOB, "member", "acme");
|
|
432
467
|
for (const spec of [owner, member]) {
|
|
433
|
-
await path.grant(spec, alice);
|
|
468
|
+
await path.grant(spec, alice, "grant");
|
|
434
469
|
}
|
|
435
470
|
|
|
436
471
|
await expect(
|
|
437
472
|
path.cleanupResource(
|
|
438
473
|
create(ApiResourceRefSchema, { kind: "organization", id: "acme" }),
|
|
474
|
+
alice,
|
|
439
475
|
),
|
|
440
476
|
).rejects.toThrow("tuple delete refused");
|
|
441
477
|
|
|
@@ -450,11 +486,12 @@ describe("cleanupResource: bidirectional, deduplicated, each row through the rev
|
|
|
450
486
|
kind: "identity_account",
|
|
451
487
|
id: ALICE,
|
|
452
488
|
});
|
|
453
|
-
await path.grant(selfOwner, alice);
|
|
489
|
+
await path.grant(selfOwner, alice, "grant");
|
|
454
490
|
recorded.length = 0;
|
|
455
491
|
|
|
456
492
|
await path.cleanupResource(
|
|
457
493
|
create(ApiResourceRefSchema, { kind: "identity_account", id: ALICE }),
|
|
494
|
+
alice,
|
|
458
495
|
);
|
|
459
496
|
|
|
460
497
|
expect(
|
|
@@ -467,6 +504,7 @@ describe("cleanupResource: bidirectional, deduplicated, each row through the rev
|
|
|
467
504
|
const { path } = pathOver(recorded, recordingLifecycle(recorded));
|
|
468
505
|
await path.cleanupResource(
|
|
469
506
|
create(ApiResourceRefSchema, { kind: "agent", id: AGENT }),
|
|
507
|
+
alice,
|
|
470
508
|
);
|
|
471
509
|
expect(recorded).toEqual([]);
|
|
472
510
|
});
|
|
@@ -476,13 +514,13 @@ describe("revokeOrgAccess: the account's direct rows on the organization", () =>
|
|
|
476
514
|
it("revokes every relation the account holds on that organization and nothing on another", async () => {
|
|
477
515
|
const recorded: RecordedEvent[] = [];
|
|
478
516
|
const { policies, path } = pathOver(recorded, recordingLifecycle(recorded));
|
|
479
|
-
await path.grant(orgRole(ALICE, "admin", "acme"), alice);
|
|
480
|
-
await path.grant(orgRole(ALICE, "viewer", "acme"), alice);
|
|
481
|
-
await path.grant(orgRole(ALICE, "member", "globex"), alice);
|
|
482
|
-
await path.grant(orgRole(BOB, "member", "acme"), alice);
|
|
517
|
+
await path.grant(orgRole(ALICE, "admin", "acme"), alice, "grant");
|
|
518
|
+
await path.grant(orgRole(ALICE, "viewer", "acme"), alice, "grant");
|
|
519
|
+
await path.grant(orgRole(ALICE, "member", "globex"), alice, "grant");
|
|
520
|
+
await path.grant(orgRole(BOB, "member", "acme"), alice, "grant");
|
|
483
521
|
recorded.length = 0;
|
|
484
522
|
|
|
485
|
-
await path.revokeOrgAccess(ALICE, "acme");
|
|
523
|
+
await path.revokeOrgAccess(ALICE, "acme", alice);
|
|
486
524
|
|
|
487
525
|
expect([...policies.rows.keys()].sort()).toEqual(
|
|
488
526
|
[
|
|
@@ -501,7 +539,537 @@ describe("revokeOrgAccess: the account's direct rows on the organization", () =>
|
|
|
501
539
|
it("an account with no rows on the organization is a no-op", async () => {
|
|
502
540
|
const recorded: RecordedEvent[] = [];
|
|
503
541
|
const { path } = pathOver(recorded, recordingLifecycle(recorded));
|
|
504
|
-
await path.revokeOrgAccess(BOB, "acme");
|
|
542
|
+
await path.revokeOrgAccess(BOB, "acme", alice);
|
|
543
|
+
expect(recorded).toEqual([]);
|
|
544
|
+
});
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
describe("leaving an organization: the account's rows on its resources", () => {
|
|
548
|
+
const SHARED = "agt_shared0000000000000000000";
|
|
549
|
+
const AUTHORED = "agt_authored000000000000000000";
|
|
550
|
+
const ELSEWHERE = "agt_elsewhere0000000000000000";
|
|
551
|
+
const TEAM = "tm_sre000000000000000000000000";
|
|
552
|
+
const SESSION = "ses_alice0000000000000000000000";
|
|
553
|
+
const RUN = "aex_alice0000000000000000000000";
|
|
554
|
+
|
|
555
|
+
/** The scope link a resource's creation writes: `organization:<org>` holds `organization` on it. */
|
|
556
|
+
function scopeLink(kind: string, id: string, org: string): IamPolicySpec {
|
|
557
|
+
return triple({ kind: "organization", id: org }, "organization", {
|
|
558
|
+
kind,
|
|
559
|
+
id,
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const bob: CallerIdentity = {
|
|
564
|
+
...alice,
|
|
565
|
+
identityId: BOB,
|
|
566
|
+
email: "bob@example.com",
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* alice holds acme's admin role, a viewer share on bob's agent, the
|
|
571
|
+
* owner row of the agent she created, an owner row bob granted her on
|
|
572
|
+
* his agent, a team membership, and her session's owner row with a run
|
|
573
|
+
* inside it; bob holds an agent in globex that alice may view.
|
|
574
|
+
*/
|
|
575
|
+
async function seeded() {
|
|
576
|
+
const recorded: RecordedEvent[] = [];
|
|
577
|
+
const creators = recordedCreators({
|
|
578
|
+
[`agent:${SHARED}`]: BOB,
|
|
579
|
+
[`agent:${AUTHORED}`]: ALICE,
|
|
580
|
+
[`agent:${ELSEWHERE}`]: BOB,
|
|
581
|
+
[`session:${SESSION}`]: ALICE,
|
|
582
|
+
});
|
|
583
|
+
const { policies, path } = pathOver(
|
|
584
|
+
recorded,
|
|
585
|
+
recordingLifecycle(recorded),
|
|
586
|
+
creators,
|
|
587
|
+
);
|
|
588
|
+
const agent = (id: string) => ({ kind: "agent", id });
|
|
589
|
+
for (const spec of [
|
|
590
|
+
orgRole(ALICE, "admin", "acme"),
|
|
591
|
+
orgRole(ALICE, "member", "globex"),
|
|
592
|
+
scopeLink("agent", SHARED, "acme"),
|
|
593
|
+
scopeLink("agent", AUTHORED, "acme"),
|
|
594
|
+
scopeLink("agent", ELSEWHERE, "globex"),
|
|
595
|
+
scopeLink("team", TEAM, "acme"),
|
|
596
|
+
scopeLink("session", SESSION, "acme"),
|
|
597
|
+
triple({ kind: "session", id: SESSION }, "session", {
|
|
598
|
+
kind: "agent_execution",
|
|
599
|
+
id: RUN,
|
|
600
|
+
}),
|
|
601
|
+
triple({ kind: "identity_account", id: ALICE }, "viewer", agent(SHARED)),
|
|
602
|
+
triple({ kind: "identity_account", id: ALICE }, "owner", agent(AUTHORED)),
|
|
603
|
+
triple({ kind: "identity_account", id: ALICE }, "owner", agent(SHARED)),
|
|
604
|
+
triple(
|
|
605
|
+
{ kind: "identity_account", id: ALICE },
|
|
606
|
+
"viewer",
|
|
607
|
+
agent(ELSEWHERE),
|
|
608
|
+
),
|
|
609
|
+
triple({ kind: "identity_account", id: ALICE }, "member", {
|
|
610
|
+
kind: "team",
|
|
611
|
+
id: TEAM,
|
|
612
|
+
}),
|
|
613
|
+
triple({ kind: "identity_account", id: ALICE }, "owner", {
|
|
614
|
+
kind: "session",
|
|
615
|
+
id: SESSION,
|
|
616
|
+
}),
|
|
617
|
+
triple({ kind: "identity_account", id: ALICE }, "viewer", {
|
|
618
|
+
kind: "agent_execution",
|
|
619
|
+
id: RUN,
|
|
620
|
+
}),
|
|
621
|
+
]) {
|
|
622
|
+
await path.grant(spec, bob, "grant");
|
|
623
|
+
}
|
|
624
|
+
recorded.length = 0;
|
|
625
|
+
return { recorded, policies, path };
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
function aliceHolds(policies: FakeIamPolicyStore): ReadonlyArray<string> {
|
|
629
|
+
return [...policies.rows.values()]
|
|
630
|
+
.filter((policy) => policy.spec?.principal?.id === ALICE)
|
|
631
|
+
.map(
|
|
632
|
+
(policy) =>
|
|
633
|
+
`${policy.spec?.relation} ${policy.spec?.resource?.kind}:${policy.spec?.resource?.id}`,
|
|
634
|
+
)
|
|
635
|
+
.sort();
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
it("revokeOrgAccess removes the shares and memberships the account held in the organization and keeps its authorship", async () => {
|
|
639
|
+
const { recorded, policies, path } = await seeded();
|
|
640
|
+
|
|
641
|
+
await path.revokeOrgAccess(ALICE, "acme", alice);
|
|
642
|
+
|
|
643
|
+
expect(aliceHolds(policies)).toEqual(
|
|
644
|
+
[
|
|
645
|
+
"member organization:globex",
|
|
646
|
+
`owner agent:${AUTHORED}`,
|
|
647
|
+
`owner session:${SESSION}`,
|
|
648
|
+
`viewer agent:${ELSEWHERE}`,
|
|
649
|
+
].sort(),
|
|
650
|
+
);
|
|
651
|
+
// Every row went through the revoke order: the hook, then the delete.
|
|
652
|
+
const kinds = recorded.map((event) => event.kind);
|
|
653
|
+
expect(kinds.filter((kind) => kind === "revoked")).toHaveLength(5);
|
|
654
|
+
expect(kinds.filter((kind) => kind === "row-delete")).toHaveLength(5);
|
|
655
|
+
for (let at = 0; at < kinds.length; at += 2) {
|
|
656
|
+
expect(kinds.slice(at, at + 2)).toEqual(["revoked", "row-delete"]);
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
it("a resource below another is placed by its parent's scope link", async () => {
|
|
661
|
+
const { policies, path } = await seeded();
|
|
662
|
+
|
|
663
|
+
await path.revokeOrgAccess(ALICE, "acme", alice);
|
|
664
|
+
|
|
665
|
+
expect(aliceHolds(policies)).not.toContain(`viewer agent_execution:${RUN}`);
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
it("a retried revokeOrgAccess still sweeps when the organization rows are already gone", async () => {
|
|
669
|
+
const { policies, path } = await seeded();
|
|
670
|
+
for (const id of [policyIdFor(orgRole(ALICE, "admin", "acme"))]) {
|
|
671
|
+
policies.rows.delete(id);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
await path.revokeOrgAccess(ALICE, "acme", alice);
|
|
675
|
+
|
|
676
|
+
expect(aliceHolds(policies)).not.toContain(`viewer agent:${SHARED}`);
|
|
677
|
+
expect(aliceHolds(policies)).not.toContain(`member team:${TEAM}`);
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
it("revoking the last role by spec sweeps the organization", async () => {
|
|
681
|
+
const { policies, path } = await seeded();
|
|
682
|
+
|
|
683
|
+
await path.revokeBySpec(orgRole(ALICE, "admin", "acme"), alice);
|
|
684
|
+
|
|
685
|
+
expect(aliceHolds(policies)).not.toContain(`viewer agent:${SHARED}`);
|
|
686
|
+
expect(aliceHolds(policies)).toContain(`owner agent:${AUTHORED}`);
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
it("revoking one role by spec while another remains sweeps nothing", async () => {
|
|
690
|
+
const { policies, path } = await seeded();
|
|
691
|
+
await path.grant(orgRole(ALICE, "viewer", "acme"), bob, "grant");
|
|
692
|
+
|
|
693
|
+
await path.revokeBySpec(orgRole(ALICE, "admin", "acme"), alice);
|
|
694
|
+
|
|
695
|
+
expect(aliceHolds(policies)).toContain(`viewer agent:${SHARED}`);
|
|
696
|
+
expect(aliceHolds(policies)).toContain(`member team:${TEAM}`);
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
it("revoking a row that is not an organization role sweeps nothing", async () => {
|
|
700
|
+
const { policies, path } = await seeded();
|
|
701
|
+
|
|
702
|
+
await path.revokeBySpec(
|
|
703
|
+
triple({ kind: "identity_account", id: ALICE }, "member", {
|
|
704
|
+
kind: "team",
|
|
705
|
+
id: TEAM,
|
|
706
|
+
}),
|
|
707
|
+
alice,
|
|
708
|
+
);
|
|
709
|
+
|
|
710
|
+
expect(aliceHolds(policies)).toContain(`viewer agent:${SHARED}`);
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
it("an organization's delete (cleanupResource) revokes its own rows and sweeps nothing else", async () => {
|
|
714
|
+
const { policies, path } = await seeded();
|
|
715
|
+
|
|
716
|
+
await path.cleanupResource(
|
|
717
|
+
create(ApiResourceRefSchema, { kind: "organization", id: "acme" }),
|
|
718
|
+
alice,
|
|
719
|
+
);
|
|
720
|
+
|
|
721
|
+
expect(aliceHolds(policies)).toContain(`viewer agent:${SHARED}`);
|
|
722
|
+
expect(aliceHolds(policies)).not.toContain("admin organization:acme");
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
it("an owner row whose principal is not the recorded creator is a grant, and goes", async () => {
|
|
726
|
+
const { policies, path } = await seeded();
|
|
727
|
+
|
|
728
|
+
await path.revokeOrgAccess(ALICE, "acme", alice);
|
|
729
|
+
|
|
730
|
+
expect(aliceHolds(policies)).not.toContain(`owner agent:${SHARED}`);
|
|
731
|
+
});
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
describe("affiliation: every change of an account's organization rows is announced", () => {
|
|
735
|
+
const TEAM = "tm_core000000000000000000000";
|
|
736
|
+
|
|
737
|
+
function announcing(faults: { changing?: Error } = {}) {
|
|
738
|
+
const recorded: RecordedEvent[] = [];
|
|
739
|
+
const { policies, path } = pathOver(
|
|
740
|
+
recorded,
|
|
741
|
+
recordingLifecycle(recorded, faults, { affiliation: true }),
|
|
742
|
+
);
|
|
743
|
+
return { recorded, policies, path };
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
it("a role granted announces changed after the row's grant hook, on the duplicate arm too", async () => {
|
|
747
|
+
const { recorded, path } = announcing();
|
|
748
|
+
await path.grant(orgRole(ALICE, "member", "acme"), alice, "grant");
|
|
749
|
+
await path.grant(orgRole(ALICE, "member", "acme"), alice, "grant");
|
|
750
|
+
expect(recorded.map((event) => event.kind)).toEqual([
|
|
751
|
+
"row-save",
|
|
752
|
+
"granted",
|
|
753
|
+
"affiliation-changed",
|
|
754
|
+
"granted",
|
|
755
|
+
"affiliation-changed",
|
|
756
|
+
]);
|
|
757
|
+
expect(recorded.at(-1)).toEqual({
|
|
758
|
+
kind: "affiliation-changed",
|
|
759
|
+
pair: `${ALICE}@acme`,
|
|
760
|
+
});
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
it("a role revoked announces changing before its revoke hook and changed after the delete", async () => {
|
|
764
|
+
const { recorded, path } = announcing();
|
|
765
|
+
await path.grant(orgRole(ALICE, "admin", "acme"), alice, "grant");
|
|
766
|
+
await path.grant(orgRole(ALICE, "viewer", "acme"), alice, "grant");
|
|
767
|
+
recorded.length = 0;
|
|
768
|
+
|
|
769
|
+
await path.revokeBySpec(orgRole(ALICE, "admin", "acme"), alice);
|
|
770
|
+
|
|
771
|
+
expect(recorded.map((event) => event.kind)).toEqual([
|
|
772
|
+
"affiliation-changing",
|
|
773
|
+
"revoked",
|
|
774
|
+
"row-delete",
|
|
775
|
+
"affiliation-changed",
|
|
776
|
+
]);
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
it("a revoke that finds no row still announces changed, so a tuple that outlived its rows heals", async () => {
|
|
780
|
+
const { recorded, path } = announcing();
|
|
781
|
+
await path.revokeBySpec(orgRole(ALICE, "member", "acme"), alice);
|
|
782
|
+
expect(recorded.map((event) => event.kind)).toEqual([
|
|
783
|
+
"revoked",
|
|
784
|
+
"affiliation-changed",
|
|
785
|
+
]);
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
it("every path that deletes a role announces it: revokeOrgAccess and an organization's delete", async () => {
|
|
789
|
+
const { recorded, path } = announcing();
|
|
790
|
+
await path.grant(orgRole(ALICE, "admin", "acme"), alice, "grant");
|
|
791
|
+
await path.grant(orgRole(BOB, "member", "acme"), alice, "grant");
|
|
792
|
+
recorded.length = 0;
|
|
793
|
+
|
|
794
|
+
await path.revokeOrgAccess(ALICE, "acme", alice);
|
|
795
|
+
expect(
|
|
796
|
+
recorded.filter((event) => event.kind === "affiliation-changing"),
|
|
797
|
+
).toEqual([{ kind: "affiliation-changing", pair: `${ALICE}@acme` }]);
|
|
798
|
+
|
|
799
|
+
recorded.length = 0;
|
|
800
|
+
await path.cleanupResource(
|
|
801
|
+
create(ApiResourceRefSchema, { kind: "organization", id: "acme" }),
|
|
802
|
+
alice,
|
|
803
|
+
);
|
|
804
|
+
expect(
|
|
805
|
+
recorded.filter((event) => event.kind === "affiliation-changing"),
|
|
806
|
+
).toEqual([{ kind: "affiliation-changing", pair: `${BOB}@acme` }]);
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
it("a row of any other shape announces nothing: a share, a team membership, a userset on an organization", async () => {
|
|
810
|
+
const { recorded, path } = announcing();
|
|
811
|
+
await path.grant(
|
|
812
|
+
triple({ kind: "identity_account", id: ALICE }, "viewer", {
|
|
813
|
+
kind: "agent",
|
|
814
|
+
id: AGENT,
|
|
815
|
+
}),
|
|
816
|
+
alice,
|
|
817
|
+
"grant",
|
|
818
|
+
);
|
|
819
|
+
await path.grant(
|
|
820
|
+
triple({ kind: "identity_account", id: ALICE }, "member", {
|
|
821
|
+
kind: "team",
|
|
822
|
+
id: TEAM,
|
|
823
|
+
}),
|
|
824
|
+
alice,
|
|
825
|
+
"grant",
|
|
826
|
+
);
|
|
827
|
+
await path.grant(
|
|
828
|
+
triple({ kind: "team", id: TEAM, relation: "member" }, "viewer", {
|
|
829
|
+
kind: "organization",
|
|
830
|
+
id: "acme",
|
|
831
|
+
}),
|
|
832
|
+
alice,
|
|
833
|
+
"grant",
|
|
834
|
+
);
|
|
835
|
+
expect(
|
|
836
|
+
recorded.filter((event) => event.kind.startsWith("affiliation")),
|
|
837
|
+
).toEqual([]);
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
it("a fault in changing stops the revoke with the row in place", async () => {
|
|
841
|
+
const fault = new Error("tuple store down");
|
|
842
|
+
const { policies, path } = announcing({ changing: fault });
|
|
843
|
+
await path.grant(orgRole(ALICE, "member", "acme"), alice, "grant");
|
|
844
|
+
|
|
845
|
+
await expect(
|
|
846
|
+
path.revokeBySpec(orgRole(ALICE, "member", "acme"), alice),
|
|
847
|
+
).rejects.toBe(fault);
|
|
848
|
+
|
|
849
|
+
expect(
|
|
850
|
+
policies.rows.has(policyIdFor(orgRole(ALICE, "member", "acme"))),
|
|
851
|
+
).toBe(true);
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
it("affiliated is refused as a grant before any read, write or hook", async () => {
|
|
855
|
+
const { recorded, path } = announcing();
|
|
856
|
+
const refusal = await path
|
|
857
|
+
.grant(orgRole(ALICE, "affiliated", "acme"), alice, "grant")
|
|
858
|
+
.then(
|
|
859
|
+
() => undefined,
|
|
860
|
+
(error: unknown) => error,
|
|
861
|
+
);
|
|
862
|
+
expect(refusal).toBeInstanceOf(ConnectError);
|
|
863
|
+
expect((refusal as ConnectError).code).toBe(Code.InvalidArgument);
|
|
864
|
+
expect((refusal as ConnectError).rawMessage).toBe(
|
|
865
|
+
AFFILIATION_NOT_GRANTABLE_MESSAGE,
|
|
866
|
+
);
|
|
505
867
|
expect(recorded).toEqual([]);
|
|
506
868
|
});
|
|
507
869
|
});
|
|
870
|
+
|
|
871
|
+
describe("who and why: every access row reaches the store with its change record", () => {
|
|
872
|
+
const TEAM = "tm_audit0000000000000000000";
|
|
873
|
+
const SHARED = "agt_audited00000000000000000";
|
|
874
|
+
const bob: CallerIdentity = {
|
|
875
|
+
...alice,
|
|
876
|
+
identityId: BOB,
|
|
877
|
+
email: "bob@example.com",
|
|
878
|
+
};
|
|
879
|
+
const aliceActor: PolicyActor = { id: ALICE, callerClass: "user" };
|
|
880
|
+
const bobActor: PolicyActor = { id: BOB, callerClass: "user" };
|
|
881
|
+
|
|
882
|
+
function scopeLink(kind: string, id: string, org: string): IamPolicySpec {
|
|
883
|
+
return triple({ kind: "organization", id: org }, "organization", {
|
|
884
|
+
kind,
|
|
885
|
+
id,
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
function share(principal: string, relation: string): IamPolicySpec {
|
|
890
|
+
return triple({ kind: "identity_account", id: principal }, relation, {
|
|
891
|
+
kind: "agent",
|
|
892
|
+
id: SHARED,
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
it("a fresh grant hands the store its actor, its door and its organization; a duplicate hands nothing", async () => {
|
|
897
|
+
const { policies, path } = pathOver([], undefined);
|
|
898
|
+
const spec = orgRole(BOB, "member", "acme");
|
|
899
|
+
|
|
900
|
+
await path.grant(spec, alice, "grant");
|
|
901
|
+
await path.grant(spec, alice, "grant");
|
|
902
|
+
|
|
903
|
+
expect(policies.changes).toEqual([
|
|
904
|
+
{
|
|
905
|
+
op: "save",
|
|
906
|
+
id: policyIdFor(spec),
|
|
907
|
+
record: { actor: aliceActor, cause: "grant", organizationId: "acme" },
|
|
908
|
+
},
|
|
909
|
+
]);
|
|
910
|
+
});
|
|
911
|
+
|
|
912
|
+
it("the actor is the caller's id and class, never its email or token", async () => {
|
|
913
|
+
const { policies, path } = pathOver([], undefined);
|
|
914
|
+
await path.grant(orgRole(BOB, "member", "acme"), alice, "first_sign_in");
|
|
915
|
+
expect(policies.changes[0]?.record?.actor).toStrictEqual(aliceActor);
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
it("a structural link carries no record; a share below the organization finds it through the link, and a team's audience is an access row", async () => {
|
|
919
|
+
const { policies, path } = pathOver([], undefined);
|
|
920
|
+
const link = scopeLink("agent", SHARED, "acme");
|
|
921
|
+
const toTeam = triple(
|
|
922
|
+
{ kind: "team", id: TEAM, relation: "member" },
|
|
923
|
+
"viewer",
|
|
924
|
+
{ kind: "agent", id: SHARED },
|
|
925
|
+
);
|
|
926
|
+
|
|
927
|
+
await path.grant(link, alice, "structural");
|
|
928
|
+
await path.grant(share(BOB, "viewer"), alice, "grant");
|
|
929
|
+
await path.grant(toTeam, alice, "grant");
|
|
930
|
+
|
|
931
|
+
expect(policies.changes.map((change) => change.record)).toEqual([
|
|
932
|
+
undefined,
|
|
933
|
+
{ actor: aliceActor, cause: "grant", organizationId: "acme" },
|
|
934
|
+
{ actor: aliceActor, cause: "grant", organizationId: "acme" },
|
|
935
|
+
]);
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
it("a resource whose organization cannot be found is recorded with none, never refused", async () => {
|
|
939
|
+
const { policies, path } = pathOver([], undefined);
|
|
940
|
+
await path.grant(share(BOB, "viewer"), alice, "grant");
|
|
941
|
+
expect(policies.changes[0]?.record?.organizationId).toBe("");
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
it("a revoke names its revoker; revoking a row that is not held records nothing", async () => {
|
|
945
|
+
const { policies, path } = pathOver([], undefined);
|
|
946
|
+
const spec = orgRole(BOB, "member", "acme");
|
|
947
|
+
await path.grant(orgRole(BOB, "admin", "acme"), alice, "grant");
|
|
948
|
+
await path.grant(spec, alice, "grant");
|
|
949
|
+
policies.changes.length = 0;
|
|
950
|
+
|
|
951
|
+
await path.revokeBySpec(spec, alice);
|
|
952
|
+
await path.revokeBySpec(orgRole(BOB, "viewer", "acme"), alice);
|
|
953
|
+
|
|
954
|
+
expect(policies.changes).toEqual([
|
|
955
|
+
{
|
|
956
|
+
op: "delete",
|
|
957
|
+
id: policyIdFor(spec),
|
|
958
|
+
record: { actor: aliceActor, cause: "revoke", organizationId: "acme" },
|
|
959
|
+
},
|
|
960
|
+
]);
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
it("removing a person records the removal on their roles and left_organization on what the sweep takes, both as the remover", async () => {
|
|
964
|
+
const { policies, path } = pathOver([], undefined);
|
|
965
|
+
await path.grant(scopeLink("agent", SHARED, "acme"), alice, "structural");
|
|
966
|
+
await path.grant(orgRole(BOB, "member", "acme"), alice, "grant");
|
|
967
|
+
await path.grant(share(BOB, "viewer"), alice, "grant");
|
|
968
|
+
policies.changes.length = 0;
|
|
969
|
+
|
|
970
|
+
await path.revokeOrgAccess(BOB, "acme", alice);
|
|
971
|
+
|
|
972
|
+
expect(policies.changes).toEqual([
|
|
973
|
+
{
|
|
974
|
+
op: "delete",
|
|
975
|
+
id: policyIdFor(orgRole(BOB, "member", "acme")),
|
|
976
|
+
record: {
|
|
977
|
+
actor: aliceActor,
|
|
978
|
+
cause: "organization_access_revoked",
|
|
979
|
+
organizationId: "acme",
|
|
980
|
+
},
|
|
981
|
+
},
|
|
982
|
+
{
|
|
983
|
+
op: "delete",
|
|
984
|
+
id: policyIdFor(share(BOB, "viewer")),
|
|
985
|
+
record: {
|
|
986
|
+
actor: aliceActor,
|
|
987
|
+
cause: "left_organization",
|
|
988
|
+
organizationId: "acme",
|
|
989
|
+
},
|
|
990
|
+
},
|
|
991
|
+
]);
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
it("the last role's revoke sweeps as the revoker", async () => {
|
|
995
|
+
const { policies, path } = pathOver([], undefined);
|
|
996
|
+
await path.grant(scopeLink("agent", SHARED, "acme"), alice, "structural");
|
|
997
|
+
await path.grant(orgRole(BOB, "member", "acme"), alice, "grant");
|
|
998
|
+
await path.grant(share(BOB, "viewer"), alice, "grant");
|
|
999
|
+
policies.changes.length = 0;
|
|
1000
|
+
|
|
1001
|
+
await path.revokeBySpec(orgRole(BOB, "member", "acme"), alice);
|
|
1002
|
+
|
|
1003
|
+
expect(policies.changes.map((change) => change.record?.cause)).toEqual([
|
|
1004
|
+
"revoke",
|
|
1005
|
+
"left_organization",
|
|
1006
|
+
]);
|
|
1007
|
+
});
|
|
1008
|
+
|
|
1009
|
+
it("a resource's cleanup reads its organization before its scope link goes, and names whoever deleted it", async () => {
|
|
1010
|
+
const { policies, path } = pathOver([], undefined);
|
|
1011
|
+
const link = scopeLink("agent", SHARED, "acme");
|
|
1012
|
+
await path.grant(link, alice, "structural");
|
|
1013
|
+
await path.grant(share(ALICE, "viewer"), alice, "grant");
|
|
1014
|
+
await path.grant(share(BOB, "owner"), bob, "structural");
|
|
1015
|
+
policies.changes.length = 0;
|
|
1016
|
+
|
|
1017
|
+
await path.cleanupResource(
|
|
1018
|
+
create(ApiResourceRefSchema, { kind: "agent", id: SHARED }),
|
|
1019
|
+
bob,
|
|
1020
|
+
);
|
|
1021
|
+
|
|
1022
|
+
const deleted = {
|
|
1023
|
+
actor: bobActor,
|
|
1024
|
+
cause: "resource_deleted",
|
|
1025
|
+
organizationId: "acme",
|
|
1026
|
+
};
|
|
1027
|
+
expect(policies.changes).toEqual([
|
|
1028
|
+
{ op: "delete", id: policyIdFor(link), record: undefined },
|
|
1029
|
+
{
|
|
1030
|
+
op: "delete",
|
|
1031
|
+
id: policyIdFor(share(ALICE, "viewer")),
|
|
1032
|
+
record: deleted,
|
|
1033
|
+
},
|
|
1034
|
+
{ op: "delete", id: policyIdFor(share(BOB, "owner")), record: deleted },
|
|
1035
|
+
]);
|
|
1036
|
+
});
|
|
1037
|
+
|
|
1038
|
+
it("the grant and revoke log lines carry who, why and where", async () => {
|
|
1039
|
+
const lines: Array<{ message: string; fields: Record<string, unknown> }> =
|
|
1040
|
+
[];
|
|
1041
|
+
const capture = (message: string, fields?: Record<string, unknown>) => {
|
|
1042
|
+
lines.push({ message, fields: fields ?? {} });
|
|
1043
|
+
};
|
|
1044
|
+
const path = newIamPolicyGrantPath({
|
|
1045
|
+
creators: NO_RECORDED_CREATORS,
|
|
1046
|
+
policies: fakeIamPolicyStore(),
|
|
1047
|
+
lifecycle: undefined,
|
|
1048
|
+
logger: { ...silentLogger, info: capture },
|
|
1049
|
+
});
|
|
1050
|
+
const spec = orgRole(BOB, "member", "acme");
|
|
1051
|
+
|
|
1052
|
+
await path.grant(spec, alice, "grant");
|
|
1053
|
+
await path.revokeBySpec(spec, alice);
|
|
1054
|
+
|
|
1055
|
+
expect(lines.map(({ message, fields }) => [message, fields])).toEqual([
|
|
1056
|
+
[
|
|
1057
|
+
"iam policy granted",
|
|
1058
|
+
expect.objectContaining({
|
|
1059
|
+
actorId: ALICE,
|
|
1060
|
+
actorClass: "user",
|
|
1061
|
+
cause: "grant",
|
|
1062
|
+
organizationId: "acme",
|
|
1063
|
+
}),
|
|
1064
|
+
],
|
|
1065
|
+
[
|
|
1066
|
+
"iam policy revoked",
|
|
1067
|
+
expect.objectContaining({
|
|
1068
|
+
actorId: ALICE,
|
|
1069
|
+
cause: "revoke",
|
|
1070
|
+
organizationId: "acme",
|
|
1071
|
+
}),
|
|
1072
|
+
],
|
|
1073
|
+
]);
|
|
1074
|
+
});
|
|
1075
|
+
});
|