@stigmer/server 3.20.0 → 3.22.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/evaluator.d.ts +7 -1
- package/dist/authorization/evaluator.d.ts.map +1 -1
- 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 +19 -11
- package/dist/authorization/list-read-scope.js.map +1 -1
- package/dist/authorization/model/organization.d.ts.map +1 -1
- package/dist/authorization/model/organization.js +2 -0
- package/dist/authorization/model/organization.js.map +1 -1
- package/dist/boot/compose.d.ts.map +1 -1
- package/dist/boot/compose.js +0 -1
- package/dist/boot/compose.js.map +1 -1
- package/dist/boot/inprocess.d.ts +0 -2
- package/dist/boot/inprocess.d.ts.map +1 -1
- package/dist/boot/inprocess.js +0 -6
- package/dist/boot/inprocess.js.map +1 -1
- package/dist/domain/agent/controller.d.ts +2 -2
- package/dist/domain/agent/controller.d.ts.map +1 -1
- package/dist/domain/agent/controller.js +3 -17
- package/dist/domain/agent/controller.js.map +1 -1
- package/dist/domain/agent/steps.d.ts +0 -1
- package/dist/domain/agent/steps.d.ts.map +1 -1
- package/dist/domain/agent/steps.js +1 -40
- package/dist/domain/agent/steps.js.map +1 -1
- package/dist/domain/agentchannel/controller.d.ts.map +1 -1
- package/dist/domain/agentchannel/controller.js +6 -1
- package/dist/domain/agentchannel/controller.js.map +1 -1
- package/dist/domain/agentchannel/steps.d.ts +19 -0
- package/dist/domain/agentchannel/steps.d.ts.map +1 -1
- package/dist/domain/agentchannel/steps.js +32 -0
- package/dist/domain/agentchannel/steps.js.map +1 -1
- package/dist/domain/agentexecution/controller.d.ts.map +1 -1
- package/dist/domain/agentexecution/controller.js +7 -9
- package/dist/domain/agentexecution/controller.js.map +1 -1
- package/dist/domain/agentexecution/create-execution-context-step.d.ts +0 -2
- package/dist/domain/agentexecution/create-execution-context-step.d.ts.map +1 -1
- package/dist/domain/agentexecution/create-execution-context-step.js +179 -60
- package/dist/domain/agentexecution/create-execution-context-step.js.map +1 -1
- package/dist/domain/agentexecution/create-steps.d.ts +16 -31
- package/dist/domain/agentexecution/create-steps.d.ts.map +1 -1
- package/dist/domain/agentexecution/create-steps.js +36 -102
- package/dist/domain/agentexecution/create-steps.js.map +1 -1
- package/dist/domain/agentexecution/run-target.d.ts +7 -3
- package/dist/domain/agentexecution/run-target.d.ts.map +1 -1
- package/dist/domain/agentexecution/run-target.js.map +1 -1
- package/dist/domain/agentinstance/controller.d.ts +5 -5
- package/dist/domain/agentinstance/controller.d.ts.map +1 -1
- package/dist/domain/agentinstance/controller.js +6 -1
- package/dist/domain/agentinstance/controller.js.map +1 -1
- package/dist/domain/agentinstance/steps.d.ts +43 -8
- package/dist/domain/agentinstance/steps.d.ts.map +1 -1
- package/dist/domain/agentinstance/steps.js +71 -8
- package/dist/domain/agentinstance/steps.js.map +1 -1
- package/dist/domain/agentshare/controller.d.ts.map +1 -1
- package/dist/domain/agentshare/controller.js +7 -1
- package/dist/domain/agentshare/controller.js.map +1 -1
- package/dist/domain/agentshare/steps.d.ts +58 -0
- package/dist/domain/agentshare/steps.d.ts.map +1 -1
- package/dist/domain/agentshare/steps.js +109 -4
- package/dist/domain/agentshare/steps.js.map +1 -1
- package/dist/domain/artifact/controller.js +21 -25
- package/dist/domain/artifact/controller.js.map +1 -1
- package/dist/domain/channelapp/controller.d.ts.map +1 -1
- package/dist/domain/channelapp/controller.js +2 -0
- package/dist/domain/channelapp/controller.js.map +1 -1
- package/dist/domain/environment/controller.d.ts.map +1 -1
- package/dist/domain/environment/controller.js +2 -0
- package/dist/domain/environment/controller.js.map +1 -1
- package/dist/domain/environment/personal.d.ts +42 -0
- package/dist/domain/environment/personal.d.ts.map +1 -0
- package/dist/domain/environment/personal.js +96 -0
- package/dist/domain/environment/personal.js.map +1 -0
- package/dist/domain/executioncontext/controller.d.ts.map +1 -1
- package/dist/domain/executioncontext/controller.js +2 -0
- package/dist/domain/executioncontext/controller.js.map +1 -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/mcpserver/connect.d.ts +0 -2
- package/dist/domain/mcpserver/connect.d.ts.map +1 -1
- package/dist/domain/mcpserver/connect.js +24 -79
- package/dist/domain/mcpserver/connect.js.map +1 -1
- package/dist/domain/mcpserver/controller.d.ts.map +1 -1
- package/dist/domain/mcpserver/controller.js +2 -0
- package/dist/domain/mcpserver/controller.js.map +1 -1
- package/dist/domain/mcpserver/start-connect.d.ts.map +1 -1
- package/dist/domain/mcpserver/start-connect.js +8 -7
- package/dist/domain/mcpserver/start-connect.js.map +1 -1
- package/dist/domain/memory/controller.d.ts.map +1 -1
- package/dist/domain/memory/controller.js +6 -1
- package/dist/domain/memory/controller.js.map +1 -1
- package/dist/domain/memory/steps.d.ts +23 -0
- package/dist/domain/memory/steps.d.ts.map +1 -1
- package/dist/domain/memory/steps.js +35 -0
- package/dist/domain/memory/steps.js.map +1 -1
- package/dist/domain/oauthapp/controller.d.ts.map +1 -1
- package/dist/domain/oauthapp/controller.js +2 -0
- package/dist/domain/oauthapp/controller.js.map +1 -1
- package/dist/domain/plugin/controller.d.ts.map +1 -1
- package/dist/domain/plugin/controller.js +5 -24
- package/dist/domain/plugin/controller.js.map +1 -1
- package/dist/domain/plugin/overlay/sanitize.js +2 -2
- package/dist/domain/schedule/controller.d.ts.map +1 -1
- package/dist/domain/schedule/controller.js +7 -1
- package/dist/domain/schedule/controller.js.map +1 -1
- package/dist/domain/schedule/steps.d.ts +19 -0
- package/dist/domain/schedule/steps.d.ts.map +1 -1
- package/dist/domain/schedule/steps.js +32 -0
- package/dist/domain/schedule/steps.js.map +1 -1
- package/dist/domain/session/controller.d.ts +0 -7
- package/dist/domain/session/controller.d.ts.map +1 -1
- package/dist/domain/session/controller.js +11 -16
- package/dist/domain/session/controller.js.map +1 -1
- package/dist/domain/session/run-target.d.ts +9 -8
- package/dist/domain/session/run-target.d.ts.map +1 -1
- package/dist/domain/session/run-target.js.map +1 -1
- package/dist/domain/session/steps.d.ts +8 -36
- package/dist/domain/session/steps.d.ts.map +1 -1
- package/dist/domain/session/steps.js +27 -172
- package/dist/domain/session/steps.js.map +1 -1
- package/dist/domain/skill/controller.d.ts.map +1 -1
- package/dist/domain/skill/controller.js +8 -17
- package/dist/domain/skill/controller.js.map +1 -1
- package/dist/domain/workflow/controller.d.ts.map +1 -1
- package/dist/domain/workflow/controller.js +30 -107
- package/dist/domain/workflow/controller.js.map +1 -1
- package/dist/domain/workflow/version-resolution.d.ts +5 -0
- package/dist/domain/workflow/version-resolution.d.ts.map +1 -0
- package/dist/domain/workflow/version-resolution.js +113 -0
- package/dist/domain/workflow/version-resolution.js.map +1 -0
- package/dist/domain/workflowexecution/create-steps.d.ts +4 -3
- package/dist/domain/workflowexecution/create-steps.d.ts.map +1 -1
- package/dist/domain/workflowexecution/create-steps.js +4 -3
- package/dist/domain/workflowexecution/create-steps.js.map +1 -1
- package/dist/domain/workflowinstance/controller.d.ts.map +1 -1
- package/dist/domain/workflowinstance/controller.js +6 -1
- package/dist/domain/workflowinstance/controller.js.map +1 -1
- package/dist/domain/workflowinstance/steps.d.ts +20 -0
- package/dist/domain/workflowinstance/steps.d.ts.map +1 -1
- package/dist/domain/workflowinstance/steps.js +39 -0
- package/dist/domain/workflowinstance/steps.js.map +1 -1
- package/dist/extensions/identity.d.ts +19 -0
- package/dist/extensions/identity.d.ts.map +1 -1
- package/dist/extensions/identity.js +22 -3
- package/dist/extensions/identity.js.map +1 -1
- package/dist/extensions/list-read-scope.d.ts +41 -7
- package/dist/extensions/list-read-scope.d.ts.map +1 -1
- package/dist/extensions/list-read-scope.js +13 -2
- package/dist/extensions/list-read-scope.js.map +1 -1
- package/dist/pipeline/apiresource-labels.d.ts +2 -2
- package/dist/pipeline/apiresource-labels.js +2 -2
- package/dist/pipeline/errors.d.ts +2 -2
- package/dist/pipeline/errors.js +2 -2
- package/dist/pipeline/steps/authorize-resolved-target.d.ts +88 -0
- package/dist/pipeline/steps/authorize-resolved-target.d.ts.map +1 -0
- package/dist/pipeline/steps/authorize-resolved-target.js +36 -0
- package/dist/pipeline/steps/authorize-resolved-target.js.map +1 -0
- package/dist/pipeline/steps/authorize.d.ts +27 -0
- package/dist/pipeline/steps/authorize.d.ts.map +1 -1
- package/dist/pipeline/steps/authorize.js +49 -10
- package/dist/pipeline/steps/authorize.js.map +1 -1
- package/dist/pipeline/steps/guard-memory-capture.d.ts.map +1 -1
- package/dist/pipeline/steps/guard-memory-capture.js +2 -1
- package/dist/pipeline/steps/guard-memory-capture.js.map +1 -1
- package/dist/pipeline/steps/guard-reserved-labels.d.ts.map +1 -1
- package/dist/pipeline/steps/guard-reserved-labels.js +12 -12
- package/dist/pipeline/steps/guard-reserved-labels.js.map +1 -1
- package/dist/pipeline/steps/helpers.d.ts +9 -5
- package/dist/pipeline/steps/helpers.d.ts.map +1 -1
- package/dist/pipeline/steps/helpers.js +11 -15
- package/dist/pipeline/steps/helpers.js.map +1 -1
- package/dist/pipeline/steps/version-history.d.ts +11 -0
- package/dist/pipeline/steps/version-history.d.ts.map +1 -1
- package/dist/pipeline/steps/version-history.js +26 -0
- package/dist/pipeline/steps/version-history.js.map +1 -1
- package/package.json +6 -6
- package/src/authorization/__tests__/list-read-scope.test.ts +23 -12
- package/src/authorization/__tests__/skip-lane-inventory.test.ts +160 -0
- package/src/authorization/__tests__/wire-permissions.test.ts +5 -5
- package/src/authorization/evaluator.ts +7 -0
- package/src/authorization/list-read-scope.ts +22 -11
- package/src/authorization/model/__tests__/registry.test.ts +2 -0
- package/src/authorization/model/organization.ts +2 -0
- package/src/boot/compose.ts +0 -1
- package/src/boot/inprocess.ts +0 -9
- package/src/domain/agent/__tests__/agent.test.ts +0 -68
- package/src/domain/agent/controller.ts +11 -36
- package/src/domain/agent/steps.ts +1 -60
- package/src/domain/agentchannel/__tests__/create-authorization.test.ts +58 -0
- package/src/domain/agentchannel/controller.ts +19 -0
- package/src/domain/agentchannel/steps.ts +40 -0
- package/src/domain/agentexecution/__tests__/create-execution-context-step.test.ts +216 -0
- package/src/domain/agentexecution/__tests__/create-steps.test.ts +70 -145
- package/src/domain/agentexecution/controller.ts +6 -10
- package/src/domain/agentexecution/create-execution-context-step.ts +271 -83
- package/src/domain/agentexecution/create-steps.ts +38 -138
- package/src/domain/agentexecution/run-target.ts +7 -3
- package/src/domain/agentinstance/__tests__/create-authorization.composed.test.ts +243 -0
- package/src/domain/agentinstance/__tests__/create-authorization.test.ts +144 -0
- package/src/domain/agentinstance/controller.ts +24 -5
- package/src/domain/agentinstance/steps.ts +82 -8
- package/src/domain/agentshare/__tests__/create-authorization.test.ts +86 -0
- package/src/domain/agentshare/__tests__/member-audience.test.ts +113 -0
- package/src/domain/agentshare/controller.ts +21 -0
- package/src/domain/agentshare/steps.ts +181 -15
- package/src/domain/artifact/controller.ts +18 -22
- package/src/domain/channelapp/controller.ts +10 -0
- package/src/domain/environment/__tests__/personal.test.ts +143 -0
- package/src/domain/environment/controller.ts +10 -0
- package/src/domain/environment/personal.ts +141 -0
- package/src/domain/executioncontext/__tests__/executioncontext.test.ts +1 -0
- package/src/domain/executioncontext/controller.ts +10 -0
- package/src/domain/executioncontext/steps.ts +2 -1
- package/src/domain/mcpserver/connect.ts +32 -92
- package/src/domain/mcpserver/controller.ts +13 -1
- package/src/domain/mcpserver/start-connect.ts +17 -11
- package/src/domain/memory/__tests__/create-authorization.test.ts +59 -0
- package/src/domain/memory/controller.ts +11 -0
- package/src/domain/memory/steps.ts +41 -0
- package/src/domain/oauthapp/controller.ts +10 -0
- package/src/domain/plugin/controller.ts +22 -36
- package/src/domain/plugin/overlay/sanitize.ts +2 -2
- package/src/domain/schedule/__tests__/create-authorization.test.ts +58 -0
- package/src/domain/schedule/controller.ts +20 -0
- package/src/domain/schedule/steps.ts +45 -2
- package/src/domain/session/__tests__/run-gate.test.ts +19 -52
- package/src/domain/session/__tests__/session.test.ts +27 -203
- package/src/domain/session/controller.ts +10 -30
- package/src/domain/session/run-target.ts +9 -8
- package/src/domain/session/steps.ts +33 -267
- package/src/domain/skill/controller.ts +24 -23
- package/src/domain/workflow/controller.ts +58 -145
- package/src/domain/workflow/version-resolution.ts +166 -0
- package/src/domain/workflowexecution/create-steps.ts +4 -3
- package/src/domain/workflowinstance/__tests__/create-authorization.test.ts +109 -0
- package/src/domain/workflowinstance/controller.ts +19 -0
- package/src/domain/workflowinstance/steps.ts +49 -1
- package/src/extensions/__tests__/composed-support.ts +38 -4
- package/src/extensions/__tests__/identity.test.ts +44 -1
- package/src/extensions/__tests__/list-read-scope-composed.test.ts +87 -1
- package/src/extensions/__tests__/list-read-scope.test.ts +63 -0
- package/src/extensions/identity.ts +23 -5
- package/src/extensions/list-read-scope.ts +47 -7
- package/src/pipeline/apiresource-labels.ts +2 -2
- package/src/pipeline/errors.ts +2 -2
- package/src/pipeline/steps/__tests__/authorize-resolved-target.test.ts +285 -0
- package/src/pipeline/steps/authorize-resolved-target.ts +129 -0
- package/src/pipeline/steps/authorize.ts +70 -13
- package/src/pipeline/steps/guard-memory-capture.ts +2 -1
- package/src/pipeline/steps/guard-reserved-labels.ts +12 -14
- package/src/pipeline/steps/helpers.ts +11 -15
- package/src/pipeline/steps/version-history.ts +34 -0
- package/dist/domain/agent/defaultagent.d.ts +0 -37
- package/dist/domain/agent/defaultagent.d.ts.map +0 -1
- package/dist/domain/agent/defaultagent.js +0 -104
- package/dist/domain/agent/defaultagent.js.map +0 -1
- package/src/domain/agent/defaultagent.ts +0 -135
|
@@ -35,6 +35,7 @@ import { SkillSchema } from "@stigmer/protos/ai/stigmer/agentic/skill/v1/api_pb"
|
|
|
35
35
|
import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
|
|
36
36
|
import type { ApiResourceReference } from "@stigmer/protos/ai/stigmer/commons/apiresource/io_pb";
|
|
37
37
|
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
38
|
+
import { IamPermission } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
|
|
38
39
|
|
|
39
40
|
import {
|
|
40
41
|
failedPreconditionError,
|
|
@@ -44,6 +45,13 @@ import {
|
|
|
44
45
|
} from "../../pipeline/errors.js";
|
|
45
46
|
import type { PipelineStep } from "../../pipeline/pipeline.js";
|
|
46
47
|
import type { RequestContext } from "../../pipeline/request-context.js";
|
|
48
|
+
import type { Authorizer } from "../../extensions/authorizer.js";
|
|
49
|
+
import {
|
|
50
|
+
AUTHORIZATION_UNAVAILABLE_MESSAGE,
|
|
51
|
+
evaluateAuthorizer,
|
|
52
|
+
} from "../../pipeline/steps/authorize.js";
|
|
53
|
+
import type { AuthorizationTarget } from "../../pipeline/steps/authorize.js";
|
|
54
|
+
import type { GetByReferenceDesc } from "../../pipeline/steps/authorize-resolved-target.js";
|
|
47
55
|
import { findResourceBySlug } from "../../pipeline/steps/helpers.js";
|
|
48
56
|
import { EXISTING_RESOURCE_KEY } from "../../pipeline/steps/load-existing.js";
|
|
49
57
|
import type { Store } from "../../store/interface.js";
|
|
@@ -61,10 +69,66 @@ type AgentShareDesc = typeof AgentShareSchema;
|
|
|
61
69
|
/**
|
|
62
70
|
* Context key for the agent resolved from spec.agent_ref during
|
|
63
71
|
* create/apply, so later steps never re-load it — Go referencedAgentKey.
|
|
64
|
-
*
|
|
65
|
-
*
|
|
72
|
+
* ResolveShareDefaults writes it; the create lane's authorization step and
|
|
73
|
+
* StampAgentPin read it.
|
|
66
74
|
*/
|
|
67
|
-
const REFERENCED_AGENT_KEY = "agentShareReferencedAgent";
|
|
75
|
+
export const REFERENCED_AGENT_KEY = "agentShareReferencedAgent";
|
|
76
|
+
|
|
77
|
+
/** The deny copy of the agent's bar, the Java AgentShareCreateHandler's. */
|
|
78
|
+
export const SHARE_AGENT_DENIED_MESSAGE =
|
|
79
|
+
"You don't have permission to share this agent";
|
|
80
|
+
|
|
81
|
+
/** The deny copy of the sharing organization's bar, the Java handler's. */
|
|
82
|
+
export const SHARE_ORGANIZATION_DENIED_MESSAGE =
|
|
83
|
+
"You don't have permission to create agent shares in this organization";
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The create lane's authorization questions, for AuthorizeResolvedTarget
|
|
87
|
+
* after ResolveShareDefaults: the two-arm consent bar the Java handler
|
|
88
|
+
* enforced and the model's comment on can_create_agent_share describes.
|
|
89
|
+
* The RPC is is_skip_authorization because the target is the referenced
|
|
90
|
+
* agent, resolved from a slug, not a request field.
|
|
91
|
+
*
|
|
92
|
+
* A same-org share (spec.agent_ref.org equals metadata.org, after the
|
|
93
|
+
* resolve step made the reference absolute) asks can_edit on the agent:
|
|
94
|
+
* sharing puts the agent in front of a wider audience, an editor's act.
|
|
95
|
+
*
|
|
96
|
+
* A cross-org share of another organization's public agent asks two
|
|
97
|
+
* questions, agent first as Java did: can_execute on the agent (whoever may
|
|
98
|
+
* run it may offer it) and then can_create_agent_share on the SHARING
|
|
99
|
+
* organization — a public share spends that organization's credits on the
|
|
100
|
+
* open internet, an admin-level act. The cross-org validity rules (public
|
|
101
|
+
* agent, no org audience, public dependencies) stay the resolve step's.
|
|
102
|
+
*/
|
|
103
|
+
export function resolveShareCreateTargets(
|
|
104
|
+
ctx: RequestContext<AgentShareDesc>,
|
|
105
|
+
): ReadonlyArray<AuthorizationTarget> {
|
|
106
|
+
const agent = ctx.get(REFERENCED_AGENT_KEY) as Agent | undefined;
|
|
107
|
+
if (agent === undefined) {
|
|
108
|
+
throw new Error("referenced agent not found in context");
|
|
109
|
+
}
|
|
110
|
+
const share = ctx.newState;
|
|
111
|
+
const shareOrg = share.metadata?.org ?? "";
|
|
112
|
+
const crossOrg = (share.spec?.agentRef?.org ?? "") !== shareOrg;
|
|
113
|
+
const agentBar: AuthorizationTarget = {
|
|
114
|
+
permission: crossOrg ? IamPermission.can_execute : IamPermission.can_edit,
|
|
115
|
+
resourceKind: ApiResourceKind.agent,
|
|
116
|
+
resourceId: agent.metadata?.id ?? "",
|
|
117
|
+
deniedMessage: SHARE_AGENT_DENIED_MESSAGE,
|
|
118
|
+
};
|
|
119
|
+
if (!crossOrg) {
|
|
120
|
+
return [agentBar];
|
|
121
|
+
}
|
|
122
|
+
return [
|
|
123
|
+
agentBar,
|
|
124
|
+
{
|
|
125
|
+
permission: IamPermission.can_create_agent_share,
|
|
126
|
+
resourceKind: ApiResourceKind.organization,
|
|
127
|
+
resourceId: shareOrg,
|
|
128
|
+
deniedMessage: SHARE_ORGANIZATION_DENIED_MESSAGE,
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
}
|
|
68
132
|
|
|
69
133
|
/**
|
|
70
134
|
* The single refusal for every anonymous/member resolution miss: share
|
|
@@ -78,6 +142,71 @@ export function sharedNotFound(slug: string): ConnectError {
|
|
|
78
142
|
return notFoundError("Agent", slug);
|
|
79
143
|
}
|
|
80
144
|
|
|
145
|
+
/**
|
|
146
|
+
* AuthorizeMemberAudience — the member-profile lane's gate, membership
|
|
147
|
+
* BEFORE existence: the Java AgentShareGetSharedProfileForMemberHandler
|
|
148
|
+
* order. getSharedProfileForMember is the signed-in resolution path for a
|
|
149
|
+
* share URL, and the proto pins its contract: a share that does not exist,
|
|
150
|
+
* is disabled, or is asked for by someone who is not a member of the
|
|
151
|
+
* sharing organization all answer the SAME NOT_FOUND, so a share URL
|
|
152
|
+
* teaches a non-member nothing — not even whether the share exists. A
|
|
153
|
+
* non-member resolving a public share uses the anonymous lane instead.
|
|
154
|
+
*
|
|
155
|
+
* The question is can_view on the organization named in the reference (the
|
|
156
|
+
* viewer set: members and above), asked live on every call so a revoked
|
|
157
|
+
* member loses access at once; the RPC is is_skip_authorization because
|
|
158
|
+
* the target is the reference's organization, not a request field the
|
|
159
|
+
* annotation can key on. This is deliberately NOT the shared
|
|
160
|
+
* AuthorizeResolvedTarget step: that step answers deny with
|
|
161
|
+
* PERMISSION_DENIED, and this lane's wire contract is the indistinguishable
|
|
162
|
+
* NOT_FOUND above, so the lane keeps its own mapping over the same
|
|
163
|
+
* evaluation (the decision form checkMyPermission uses). An empty org is
|
|
164
|
+
* left to the loader's INVALID_ARGUMENT, the proto's own copy for it; an
|
|
165
|
+
* unavailable authorizer is INTERNAL, never softened into a refusal; the
|
|
166
|
+
* `internal` class is exempt as everywhere.
|
|
167
|
+
*/
|
|
168
|
+
export function newAuthorizeMemberAudienceStep(
|
|
169
|
+
authorizer: Authorizer,
|
|
170
|
+
): PipelineStep<GetByReferenceDesc> {
|
|
171
|
+
return {
|
|
172
|
+
name: "AuthorizeMemberAudience",
|
|
173
|
+
async execute(ctx: RequestContext<GetByReferenceDesc>): Promise<void> {
|
|
174
|
+
const ref = ctx.input;
|
|
175
|
+
if (ref.org === "" || ctx.callerIdentity.callerClass === "internal") {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const decision = await evaluateAuthorizer(
|
|
179
|
+
authorizer,
|
|
180
|
+
ctx.callerIdentity,
|
|
181
|
+
{
|
|
182
|
+
permission: IamPermission.can_view,
|
|
183
|
+
resourceKind: ApiResourceKind.organization,
|
|
184
|
+
resourceId: ref.org,
|
|
185
|
+
},
|
|
186
|
+
);
|
|
187
|
+
switch (decision.kind) {
|
|
188
|
+
case "allow":
|
|
189
|
+
return;
|
|
190
|
+
case "deny":
|
|
191
|
+
case "not-found":
|
|
192
|
+
throw sharedNotFound(ref.slug);
|
|
193
|
+
case "unavailable":
|
|
194
|
+
throw internalError(
|
|
195
|
+
decision.cause,
|
|
196
|
+
AUTHORIZATION_UNAVAILABLE_MESSAGE,
|
|
197
|
+
);
|
|
198
|
+
default: {
|
|
199
|
+
const exhaustive: never = decision;
|
|
200
|
+
throw internalError(
|
|
201
|
+
new Error(`unknown decision ${JSON.stringify(exhaustive)}`),
|
|
202
|
+
AUTHORIZATION_UNAVAILABLE_MESSAGE,
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
81
210
|
/**
|
|
82
211
|
* The link-token predicate — Go sharingLinkTokenAllowed, the mirror of the
|
|
83
212
|
* cloud edition's SharingLinkTokenPolicy:
|
|
@@ -90,7 +219,10 @@ export function sharedNotFound(slug: string): ConnectError {
|
|
|
90
219
|
* guard restores Go's semantics; the length of a token is not secret (all
|
|
91
220
|
* server-minted tokens are 27 chars).
|
|
92
221
|
*/
|
|
93
|
-
export function sharingLinkTokenAllowed(
|
|
222
|
+
export function sharingLinkTokenAllowed(
|
|
223
|
+
presented: string,
|
|
224
|
+
live: string,
|
|
225
|
+
): boolean {
|
|
94
226
|
if (live === "") {
|
|
95
227
|
return true;
|
|
96
228
|
}
|
|
@@ -182,7 +314,9 @@ export async function findShareByOrgAndSlug(
|
|
|
182
314
|
* provided neither — the canonical share keeps the agent's hosted URL.
|
|
183
315
|
* Runs before ResolveSlug, which skips already-set slugs.
|
|
184
316
|
*/
|
|
185
|
-
export function newResolveShareDefaultsStep(
|
|
317
|
+
export function newResolveShareDefaultsStep(
|
|
318
|
+
store: Store,
|
|
319
|
+
): PipelineStep<AgentShareDesc> {
|
|
186
320
|
return {
|
|
187
321
|
name: "ResolveShareDefaults",
|
|
188
322
|
async execute(ctx: RequestContext<AgentShareDesc>): Promise<void> {
|
|
@@ -204,7 +338,11 @@ export function newResolveShareDefaultsStep(store: Store): PipelineStep<AgentSha
|
|
|
204
338
|
agentRef!.org = metadata!.org;
|
|
205
339
|
}
|
|
206
340
|
|
|
207
|
-
const agent = await findAgentByOrgAndSlug(
|
|
341
|
+
const agent = await findAgentByOrgAndSlug(
|
|
342
|
+
store,
|
|
343
|
+
agentRef!.org,
|
|
344
|
+
agentRef!.slug,
|
|
345
|
+
);
|
|
208
346
|
if (agent === undefined) {
|
|
209
347
|
throw notFoundError("Agent", agentRef!.slug);
|
|
210
348
|
}
|
|
@@ -288,7 +426,10 @@ async function findNonPublicDependencies(
|
|
|
288
426
|
|
|
289
427
|
const seen = new Set<string>();
|
|
290
428
|
const deps: DepRef[] = [];
|
|
291
|
-
const add = (
|
|
429
|
+
const add = (
|
|
430
|
+
kind: ApiResourceKind,
|
|
431
|
+
ref: ApiResourceReference | undefined,
|
|
432
|
+
): void => {
|
|
292
433
|
if (ref === undefined || ref.slug === "") {
|
|
293
434
|
return;
|
|
294
435
|
}
|
|
@@ -320,8 +461,20 @@ async function findNonPublicDependencies(
|
|
|
320
461
|
// are the only dependency kinds an agent blueprint declares.
|
|
321
462
|
const resolved =
|
|
322
463
|
dep.kind === ApiResourceKind.skill
|
|
323
|
-
? await findResourceBySlug(
|
|
324
|
-
|
|
464
|
+
? await findResourceBySlug(
|
|
465
|
+
store,
|
|
466
|
+
dep.kind,
|
|
467
|
+
SkillSchema,
|
|
468
|
+
dep.slug,
|
|
469
|
+
dep.org,
|
|
470
|
+
)
|
|
471
|
+
: await findResourceBySlug(
|
|
472
|
+
store,
|
|
473
|
+
dep.kind,
|
|
474
|
+
McpServerSchema,
|
|
475
|
+
dep.slug,
|
|
476
|
+
dep.org,
|
|
477
|
+
);
|
|
325
478
|
visibility = resolved?.metadata?.visibility;
|
|
326
479
|
} catch (error) {
|
|
327
480
|
throw internalError(
|
|
@@ -396,20 +549,28 @@ export function newValidateShareUpdateStep(): PipelineStep<AgentShareDesc> {
|
|
|
396
549
|
// Normalize the input ref's org the same way create does (empty
|
|
397
550
|
// means the share's own org) before comparing.
|
|
398
551
|
const inputOrg =
|
|
399
|
-
(inputRef?.org ?? "") !== ""
|
|
552
|
+
(inputRef?.org ?? "") !== ""
|
|
553
|
+
? inputRef!.org
|
|
554
|
+
: (existing.metadata?.org ?? "");
|
|
400
555
|
|
|
401
556
|
if (
|
|
402
557
|
(inputRef?.slug ?? "") !== (existingRef?.slug ?? "") ||
|
|
403
558
|
inputOrg !== (existingRef?.org ?? "")
|
|
404
559
|
) {
|
|
405
560
|
throw failedPreconditionError(
|
|
406
|
-
agentRefImmutableMessage(
|
|
561
|
+
agentRefImmutableMessage(
|
|
562
|
+
existingRef?.org ?? "",
|
|
563
|
+
existingRef?.slug ?? "",
|
|
564
|
+
),
|
|
407
565
|
);
|
|
408
566
|
}
|
|
409
567
|
|
|
410
|
-
const isCrossOrg =
|
|
568
|
+
const isCrossOrg =
|
|
569
|
+
(existingRef?.org ?? "") !== (existing.metadata?.org ?? "");
|
|
411
570
|
if (isCrossOrg && ctx.input.spec?.audience === AgentShareAudience.org) {
|
|
412
|
-
throw failedPreconditionError(
|
|
571
|
+
throw failedPreconditionError(
|
|
572
|
+
crossOrgAudienceMessage(existingRef?.org ?? ""),
|
|
573
|
+
);
|
|
413
574
|
}
|
|
414
575
|
},
|
|
415
576
|
};
|
|
@@ -430,7 +591,11 @@ export async function buildSharedAgentProfile(
|
|
|
430
591
|
share: AgentShare,
|
|
431
592
|
): Promise<SharedAgentProfile> {
|
|
432
593
|
const ref = share.spec?.agentRef;
|
|
433
|
-
const agent = await findAgentByOrgAndSlug(
|
|
594
|
+
const agent = await findAgentByOrgAndSlug(
|
|
595
|
+
store,
|
|
596
|
+
ref?.org ?? "",
|
|
597
|
+
ref?.slug ?? "",
|
|
598
|
+
);
|
|
434
599
|
if (agent === undefined) {
|
|
435
600
|
throw sharedNotFound(share.metadata?.slug ?? "");
|
|
436
601
|
}
|
|
@@ -440,7 +605,8 @@ export async function buildSharedAgentProfile(
|
|
|
440
605
|
throw sharedNotFound(share.metadata?.slug ?? "");
|
|
441
606
|
}
|
|
442
607
|
|
|
443
|
-
const isCrossOrg =
|
|
608
|
+
const isCrossOrg =
|
|
609
|
+
(share.metadata?.org ?? "") !== (agent.metadata?.org ?? "");
|
|
444
610
|
if (
|
|
445
611
|
isCrossOrg &&
|
|
446
612
|
agent.metadata?.visibility !== ApiResourceVisibility.visibility_public
|
|
@@ -66,9 +66,9 @@ import { callerIdentityOf } from "../../pipeline/interceptors/auth.js";
|
|
|
66
66
|
import { RequestContext } from "../../pipeline/request-context.js";
|
|
67
67
|
import {
|
|
68
68
|
authorizeDirect,
|
|
69
|
-
authorizeResolvedResource,
|
|
70
69
|
newAuthorizeStep,
|
|
71
70
|
} from "../../pipeline/steps/authorize.js";
|
|
71
|
+
import { newAuthorizeResolvedTargetStep } from "../../pipeline/steps/authorize-resolved-target.js";
|
|
72
72
|
import {
|
|
73
73
|
generateId,
|
|
74
74
|
setAuditFieldsForCreate,
|
|
@@ -426,23 +426,18 @@ async function listByExecution(
|
|
|
426
426
|
}
|
|
427
427
|
},
|
|
428
428
|
})
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
// divergence from Java's deny-everyone arm on this lane.
|
|
442
|
-
const input = stepCtx.input;
|
|
443
|
-
await authorizeResolvedResource(
|
|
444
|
-
deps.authorizer,
|
|
445
|
-
stepCtx.callerIdentity,
|
|
429
|
+
// The Java ArtifactListByExecutionHandler's gate: can_view on the PARENT
|
|
430
|
+
// execution — "if you can view an execution, you can list its
|
|
431
|
+
// artifacts." The two-field dispatch (workflow-execution first, the Java
|
|
432
|
+
// order) is why the declarative annotation cannot express this lane and
|
|
433
|
+
// the proto carries is_skip_authorization. Deny copy is the Java
|
|
434
|
+
// handler's byte-pinned error_msg; a nonexistent id answers NOT_FOUND
|
|
435
|
+
// through the authorizer's probe — the ruled uniform unknown-id
|
|
436
|
+
// posture, a recorded divergence from Java's deny-everyone arm here.
|
|
437
|
+
.addStep(
|
|
438
|
+
newAuthorizeResolvedTargetStep(
|
|
439
|
+
deps.authorizer,
|
|
440
|
+
({ input }) => [
|
|
446
441
|
{
|
|
447
442
|
permission: IamPermission.can_view,
|
|
448
443
|
resourceKind:
|
|
@@ -453,11 +448,12 @@ async function listByExecution(
|
|
|
453
448
|
input.workflowExecutionId !== ""
|
|
454
449
|
? input.workflowExecutionId
|
|
455
450
|
: input.agentExecutionId,
|
|
451
|
+
deniedMessage: "unauthorized to list artifacts for this execution",
|
|
456
452
|
},
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
453
|
+
],
|
|
454
|
+
"AuthorizeParentExecution",
|
|
455
|
+
),
|
|
456
|
+
)
|
|
461
457
|
.addStep({
|
|
462
458
|
name: "QueryArtifactsByExecution",
|
|
463
459
|
async execute(stepCtx): Promise<void> {
|
|
@@ -51,6 +51,10 @@ import type { PipelineStep } from "../../pipeline/pipeline.js";
|
|
|
51
51
|
import { callerIdentityOf } from "../../pipeline/interceptors/auth.js";
|
|
52
52
|
import { RequestContext } from "../../pipeline/request-context.js";
|
|
53
53
|
import { newAuthorizeStep } from "../../pipeline/steps/authorize.js";
|
|
54
|
+
import {
|
|
55
|
+
loadedTargetAsMethod,
|
|
56
|
+
newAuthorizeResolvedTargetStep,
|
|
57
|
+
} from "../../pipeline/steps/authorize-resolved-target.js";
|
|
54
58
|
import { newGuardReservedLabelsStep } from "../../pipeline/steps/guard-reserved-labels.js";
|
|
55
59
|
import { newBuildNewStateStep } from "../../pipeline/steps/defaults.js";
|
|
56
60
|
import { newBuildUpdateStateStep } from "../../pipeline/steps/build-update-state.js";
|
|
@@ -369,6 +373,12 @@ async function getByReference(
|
|
|
369
373
|
)
|
|
370
374
|
.addStep(newValidateProtoStep())
|
|
371
375
|
.addStep(newLoadByReferenceStep(deps.store, ChannelAppSchema))
|
|
376
|
+
.addStep(
|
|
377
|
+
newAuthorizeResolvedTargetStep(
|
|
378
|
+
deps.authorizer,
|
|
379
|
+
loadedTargetAsMethod(ChannelAppQueryController.method.get),
|
|
380
|
+
),
|
|
381
|
+
)
|
|
372
382
|
.build()
|
|
373
383
|
.execute(reqCtx);
|
|
374
384
|
const app = reqCtx.get(TARGET_RESOURCE_KEY) as ChannelApp;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pins the shared personal-environment lookup (personal.ts) on the seam
|
|
3
|
+
* both its callers stand on: outcomes are VALUES (no personal environment;
|
|
4
|
+
* resolved with the required keys still missing named), a declaration's
|
|
5
|
+
* is_secret rides onto the value, optional keys are skipped silently in
|
|
6
|
+
* every absent arm, only stored keys are read (existence before the
|
|
7
|
+
* secret read; own-key membership), and a failing list is the one thrown
|
|
8
|
+
* fault. The connect lane's refusals over these outcomes are pinned in
|
|
9
|
+
* mcpserver/__tests__/connect.test.ts; the build's warnings in
|
|
10
|
+
* agentexecution/__tests__/create-execution-context-step.test.ts.
|
|
11
|
+
*/
|
|
12
|
+
import { create } from "@bufbuild/protobuf";
|
|
13
|
+
import { Code, ConnectError } from "@connectrpc/connect";
|
|
14
|
+
import { describe, expect, it } from "vitest";
|
|
15
|
+
|
|
16
|
+
import { EnvironmentSchema } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/api_pb";
|
|
17
|
+
import { EnvironmentListSchema } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/io_pb";
|
|
18
|
+
import {
|
|
19
|
+
EnvVarDeclarationSchema,
|
|
20
|
+
EnvironmentValueSchema,
|
|
21
|
+
} from "@stigmer/protos/ai/stigmer/agentic/environment/v1/spec_pb";
|
|
22
|
+
|
|
23
|
+
import { createLogger } from "../../../boot/logger.js";
|
|
24
|
+
import { PERSONAL_LABEL_KEY, PERSONAL_LABEL_VALUE } from "../constants.js";
|
|
25
|
+
import type { PersonalEnvironmentReader } from "../personal.js";
|
|
26
|
+
import { resolveDeclaredFromPersonalEnvironment } from "../personal.js";
|
|
27
|
+
|
|
28
|
+
const silentLogger = createLogger({
|
|
29
|
+
level: "error",
|
|
30
|
+
pretty: false,
|
|
31
|
+
write: () => {},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/** A declaration literal as the generated message. */
|
|
35
|
+
function decl(init: { isSecret: boolean; optional?: boolean }) {
|
|
36
|
+
return create(EnvVarDeclarationSchema, init);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function readerOver(
|
|
40
|
+
stored: Record<string, string>,
|
|
41
|
+
reads: string[],
|
|
42
|
+
failing: ReadonlySet<string> = new Set(),
|
|
43
|
+
): PersonalEnvironmentReader {
|
|
44
|
+
return {
|
|
45
|
+
list: async (request) => {
|
|
46
|
+
expect(request.labels).toEqual({ [PERSONAL_LABEL_KEY]: PERSONAL_LABEL_VALUE });
|
|
47
|
+
return create(EnvironmentListSchema, {
|
|
48
|
+
totalCount: 1,
|
|
49
|
+
items: [
|
|
50
|
+
create(EnvironmentSchema, {
|
|
51
|
+
metadata: { id: "env_p", org: "acme", slug: "personal" },
|
|
52
|
+
spec: {
|
|
53
|
+
data: Object.fromEntries(
|
|
54
|
+
Object.keys(stored).map((k) => [k, { value: "***", isSecret: true }]),
|
|
55
|
+
),
|
|
56
|
+
},
|
|
57
|
+
}),
|
|
58
|
+
],
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
getSecretValue: async (input) => {
|
|
62
|
+
const key = input.key ?? "";
|
|
63
|
+
reads.push(key);
|
|
64
|
+
if (failing.has(key)) {
|
|
65
|
+
throw new ConnectError("decrypt failed", Code.Internal);
|
|
66
|
+
}
|
|
67
|
+
return create(EnvironmentValueSchema, { value: stored[key] ?? "", isSecret: true });
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
describe("resolveDeclaredFromPersonalEnvironment", () => {
|
|
73
|
+
it("answers no-personal-environment as a value when the org has none", async () => {
|
|
74
|
+
const reader: PersonalEnvironmentReader = {
|
|
75
|
+
list: async () => create(EnvironmentListSchema, { totalCount: 0, items: [] }),
|
|
76
|
+
getSecretValue: async () => {
|
|
77
|
+
throw new Error("unreached");
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
const out = await resolveDeclaredFromPersonalEnvironment(reader, silentLogger, "acme", {
|
|
81
|
+
TOKEN: decl({ isSecret: true }),
|
|
82
|
+
});
|
|
83
|
+
expect(out).toEqual({ kind: "no-personal-environment" });
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("resolves stored keys with the declaration's is_secret, names missing required keys, skips optional ones", async () => {
|
|
87
|
+
const reads: string[] = [];
|
|
88
|
+
const reader = readerOver({ TOKEN: "t-1", PUBLIC_URL: "https://x", EMPTY: "" }, reads);
|
|
89
|
+
const out = await resolveDeclaredFromPersonalEnvironment(reader, silentLogger, "acme", {
|
|
90
|
+
TOKEN: decl({ isSecret: true }),
|
|
91
|
+
PUBLIC_URL: decl({ isSecret: false }),
|
|
92
|
+
EMPTY: decl({ isSecret: true }),
|
|
93
|
+
ABSENT_REQUIRED: decl({ isSecret: true }),
|
|
94
|
+
ABSENT_OPTIONAL: decl({ isSecret: true, optional: true }),
|
|
95
|
+
});
|
|
96
|
+
expect(out.kind).toBe("resolved");
|
|
97
|
+
if (out.kind !== "resolved") return;
|
|
98
|
+
expect(out.values["TOKEN"]?.value).toBe("t-1");
|
|
99
|
+
expect(out.values["TOKEN"]?.isSecret).toBe(true);
|
|
100
|
+
expect(out.values["PUBLIC_URL"]?.value).toBe("https://x");
|
|
101
|
+
expect(out.values["PUBLIC_URL"]?.isSecret).toBe(false);
|
|
102
|
+
// An empty stored value is absent; required → missing.
|
|
103
|
+
expect(out.values["EMPTY"]).toBeUndefined();
|
|
104
|
+
expect([...out.missing].sort()).toEqual(["ABSENT_REQUIRED", "EMPTY"]);
|
|
105
|
+
// Only stored keys are read: absent declarations cost no secret read.
|
|
106
|
+
expect(reads.sort()).toEqual(["EMPTY", "PUBLIC_URL", "TOKEN"]);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("treats a failing secret read as absent: missing when required, skipped when optional", async () => {
|
|
110
|
+
const reads: string[] = [];
|
|
111
|
+
const reader = readerOver({ A: "a", B: "b" }, reads, new Set(["A", "B"]));
|
|
112
|
+
const out = await resolveDeclaredFromPersonalEnvironment(reader, silentLogger, "acme", {
|
|
113
|
+
A: decl({ isSecret: true }),
|
|
114
|
+
B: decl({ isSecret: true, optional: true }),
|
|
115
|
+
});
|
|
116
|
+
expect(out).toEqual({ kind: "resolved", values: {}, missing: ["A"] });
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("never reads a prototype name as a stored key", async () => {
|
|
120
|
+
const reads: string[] = [];
|
|
121
|
+
const reader = readerOver({}, reads);
|
|
122
|
+
const out = await resolveDeclaredFromPersonalEnvironment(reader, silentLogger, "acme", {
|
|
123
|
+
constructor: decl({ isSecret: true }),
|
|
124
|
+
toString: decl({ isSecret: true, optional: true }),
|
|
125
|
+
});
|
|
126
|
+
expect(out).toEqual({ kind: "resolved", values: {}, missing: ["constructor"] });
|
|
127
|
+
expect(reads).toEqual([]);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("throws Internal when the personal environment cannot be listed", async () => {
|
|
131
|
+
const reader: PersonalEnvironmentReader = {
|
|
132
|
+
list: async () => {
|
|
133
|
+
throw new Error("store offline");
|
|
134
|
+
},
|
|
135
|
+
getSecretValue: async () => {
|
|
136
|
+
throw new Error("unreached");
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
await expect(
|
|
140
|
+
resolveDeclaredFromPersonalEnvironment(reader, silentLogger, "acme", { A: decl({ isSecret: true }) }),
|
|
141
|
+
).rejects.toMatchObject({ code: Code.Internal });
|
|
142
|
+
});
|
|
143
|
+
});
|
|
@@ -58,6 +58,10 @@ import type { PipelineStep } from "../../pipeline/pipeline.js";
|
|
|
58
58
|
import { callerIdentityOf } from "../../pipeline/interceptors/auth.js";
|
|
59
59
|
import { RequestContext } from "../../pipeline/request-context.js";
|
|
60
60
|
import { newAuthorizeStep } from "../../pipeline/steps/authorize.js";
|
|
61
|
+
import {
|
|
62
|
+
loadedTargetAsMethod,
|
|
63
|
+
newAuthorizeResolvedTargetStep,
|
|
64
|
+
} from "../../pipeline/steps/authorize-resolved-target.js";
|
|
61
65
|
import { newGuardReservedLabelsStep } from "../../pipeline/steps/guard-reserved-labels.js";
|
|
62
66
|
import { newAuthorizeVisibilityTransitionStep } from "../../pipeline/steps/visibility-gates.js";
|
|
63
67
|
import {
|
|
@@ -682,6 +686,12 @@ async function getByReference(
|
|
|
682
686
|
)
|
|
683
687
|
.addStep(newValidateProtoStep())
|
|
684
688
|
.addStep(newLoadByReferenceStep(deps.store, EnvironmentSchema))
|
|
689
|
+
.addStep(
|
|
690
|
+
newAuthorizeResolvedTargetStep(
|
|
691
|
+
deps.authorizer,
|
|
692
|
+
loadedTargetAsMethod(EnvironmentQueryController.method.get),
|
|
693
|
+
),
|
|
694
|
+
)
|
|
685
695
|
.build()
|
|
686
696
|
.execute(reqCtx);
|
|
687
697
|
const env = reqCtx.get(TARGET_RESOURCE_KEY) as Environment;
|