@vellumai/credential-executor 0.10.7 → 0.10.8-dev.202607102228.5945895
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/Dockerfile +1 -1
- package/node_modules/@vellumai/service-contracts/package.json +1 -2
- package/node_modules/@vellumai/service-contracts/src/__tests__/attachment-naming.test.ts +104 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/contracts.test.ts +0 -2
- package/node_modules/@vellumai/service-contracts/src/attachment-naming.ts +118 -0
- package/node_modules/@vellumai/service-contracts/src/credential-rpc.ts +3 -5
- package/node_modules/@vellumai/service-contracts/src/index.ts +2 -4
- package/node_modules/@vellumai/service-contracts/src/rpc.ts +4 -447
- package/package.json +2 -3
- package/src/__tests__/bulk-set-credentials.test.ts +1 -1
- package/src/__tests__/local-standalone.test.ts +5 -36
- package/src/__tests__/managed-integration.test.ts +112 -91
- package/src/__tests__/managed-reconnect.test.ts +2 -2
- package/src/__tests__/transport.test.ts +23 -27
- package/src/cli.ts +1 -1
- package/src/index.ts +8 -88
- package/src/main.ts +228 -340
- package/src/paths.ts +4 -20
- package/src/server.ts +52 -469
- package/node_modules/@vellumai/service-contracts/src/__tests__/grants.test.ts +0 -686
- package/node_modules/@vellumai/service-contracts/src/grants.ts +0 -184
- package/node_modules/@vellumai/service-contracts/src/rendering.ts +0 -135
- package/src/__tests__/command-executor.test.ts +0 -1879
- package/src/__tests__/command-validator.test.ts +0 -1405
- package/src/__tests__/command-workspace.test.ts +0 -1050
- package/src/__tests__/grant-store.test.ts +0 -689
- package/src/__tests__/http-executor.test.ts +0 -1336
- package/src/__tests__/http-policy.test.ts +0 -1069
- package/src/__tests__/local-materializers.test.ts +0 -860
- package/src/__tests__/local-token-refresh.test.ts +0 -361
- package/src/__tests__/manage-secure-command-tool.test.ts +0 -134
- package/src/__tests__/managed-lazy-getters.test.ts +0 -359
- package/src/__tests__/managed-materializers.test.ts +0 -1028
- package/src/__tests__/managed-rejection.test.ts +0 -43
- package/src/__tests__/toolstore.test.ts +0 -773
- package/src/audit/store.ts +0 -188
- package/src/commands/auth-adapters.ts +0 -169
- package/src/commands/egress-hooks.ts +0 -203
- package/src/commands/executor.ts +0 -1155
- package/src/commands/output-scan.ts +0 -157
- package/src/commands/profiles.ts +0 -286
- package/src/commands/validator.ts +0 -702
- package/src/commands/workspace.ts +0 -550
- package/src/grants/index.ts +0 -17
- package/src/grants/persistent-store.ts +0 -309
- package/src/grants/rpc-handlers.ts +0 -293
- package/src/grants/temporary-store.ts +0 -289
- package/src/http/audit.ts +0 -84
- package/src/http/executor.ts +0 -684
- package/src/http/path-template.ts +0 -245
- package/src/http/policy.ts +0 -238
- package/src/http/response-filter.ts +0 -233
- package/src/managed-errors.ts +0 -9
- package/src/managed-lazy-getters.ts +0 -106
- package/src/managed-main.ts +0 -822
- package/src/materializers/local-oauth-lookup.ts +0 -98
- package/src/materializers/local-token-refresh.ts +0 -287
- package/src/materializers/local.ts +0 -316
- package/src/materializers/managed-platform.ts +0 -295
- package/src/subjects/local.ts +0 -177
- package/src/subjects/managed.ts +0 -311
- package/src/subjects/policy.ts +0 -79
- package/src/toolstore/integrity.ts +0 -94
- package/src/toolstore/manifest.ts +0 -154
- package/src/toolstore/publish.ts +0 -571
|
@@ -1,295 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Managed platform OAuth materializer.
|
|
3
|
-
*
|
|
4
|
-
* Materializes a `platform_oauth` handle into a short-lived access token
|
|
5
|
-
* by calling the platform's CES token-materialization endpoint. The
|
|
6
|
-
* materialized token is returned to the caller for immediate use (e.g.
|
|
7
|
-
* injection into an HTTP request or command environment) but is **never**
|
|
8
|
-
* persisted to any local storage — it exists only in memory for the
|
|
9
|
-
* duration of the execution.
|
|
10
|
-
*
|
|
11
|
-
* Security invariants:
|
|
12
|
-
* - Materialized tokens are never written to disk.
|
|
13
|
-
* - Materialized tokens are never logged (not even partially).
|
|
14
|
-
* - Platform errors are surfaced as structured errors without leaking secrets.
|
|
15
|
-
* - If the platform cannot be reached, materialization fails closed.
|
|
16
|
-
*
|
|
17
|
-
* The materializer expects a resolved `ManagedSubject` from
|
|
18
|
-
* `subjects/managed.ts`. It does not perform handle parsing or catalog
|
|
19
|
-
* lookup — that is the resolver's responsibility.
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
import type { ManagedSubject } from "../subjects/managed.js";
|
|
23
|
-
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
// Materialization result
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Successful materialization result.
|
|
30
|
-
*
|
|
31
|
-
* The `accessToken` field contains the short-lived token obtained from
|
|
32
|
-
* the platform. Callers MUST NOT persist this value — it should be used
|
|
33
|
-
* immediately for request injection and then discarded.
|
|
34
|
-
*/
|
|
35
|
-
export interface MaterializedToken {
|
|
36
|
-
/** The short-lived access token. */
|
|
37
|
-
accessToken: string;
|
|
38
|
-
/** Token type (typically "Bearer"). */
|
|
39
|
-
tokenType: string;
|
|
40
|
-
/** Epoch ms when the token expires (null if the platform didn't report expiry). */
|
|
41
|
-
expiresAt: number | null;
|
|
42
|
-
/** Provider key (mirrored from the subject for convenience). */
|
|
43
|
-
provider: string;
|
|
44
|
-
/** Connection ID (mirrored from the subject for convenience). */
|
|
45
|
-
connectionId: string;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export type MaterializeResult =
|
|
49
|
-
| { ok: true; token: MaterializedToken }
|
|
50
|
-
| { ok: false; error: MaterializationError };
|
|
51
|
-
|
|
52
|
-
// ---------------------------------------------------------------------------
|
|
53
|
-
// Materialization errors
|
|
54
|
-
// ---------------------------------------------------------------------------
|
|
55
|
-
|
|
56
|
-
export class MaterializationError extends Error {
|
|
57
|
-
readonly code: string;
|
|
58
|
-
|
|
59
|
-
constructor(code: string, message: string) {
|
|
60
|
-
super(message);
|
|
61
|
-
this.name = "MaterializationError";
|
|
62
|
-
this.code = code;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// ---------------------------------------------------------------------------
|
|
67
|
-
// Platform token response shape
|
|
68
|
-
// ---------------------------------------------------------------------------
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Shape of the platform's CES token-materialization response.
|
|
72
|
-
*
|
|
73
|
-
* Field names match the platform's ManagedTokenMaterializeResponseSerializer:
|
|
74
|
-
* access_token, token_type, expires_at, provider, handle
|
|
75
|
-
*
|
|
76
|
-
* The platform issues a short-lived access token for the specified
|
|
77
|
-
* connection. The token is pre-authorized for the scopes granted on
|
|
78
|
-
* the connection.
|
|
79
|
-
*/
|
|
80
|
-
interface PlatformTokenResponse {
|
|
81
|
-
access_token: string;
|
|
82
|
-
token_type?: string;
|
|
83
|
-
/** ISO-8601 datetime when the token expires (null if no expiry). */
|
|
84
|
-
expires_at?: string | null;
|
|
85
|
-
provider?: string;
|
|
86
|
-
handle?: string;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// ---------------------------------------------------------------------------
|
|
90
|
-
// Materializer options
|
|
91
|
-
// ---------------------------------------------------------------------------
|
|
92
|
-
|
|
93
|
-
export interface ManagedMaterializerOptions {
|
|
94
|
-
/**
|
|
95
|
-
* Platform base URL (without trailing slash).
|
|
96
|
-
*/
|
|
97
|
-
platformBaseUrl: string;
|
|
98
|
-
/**
|
|
99
|
-
* Assistant API key for authenticating with the platform.
|
|
100
|
-
*/
|
|
101
|
-
assistantApiKey: string;
|
|
102
|
-
/**
|
|
103
|
-
* Platform-assigned assistant UUID. Required for building the
|
|
104
|
-
* platform materialize URL: /v1/assistants/<id>/oauth/managed/materialize/
|
|
105
|
-
*/
|
|
106
|
-
assistantId: string;
|
|
107
|
-
/**
|
|
108
|
-
* Optional custom fetch implementation (for testing).
|
|
109
|
-
*/
|
|
110
|
-
fetch?: typeof globalThis.fetch;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// ---------------------------------------------------------------------------
|
|
114
|
-
// Materializer implementation
|
|
115
|
-
// ---------------------------------------------------------------------------
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Materialize a managed OAuth subject into a short-lived access token
|
|
119
|
-
* by calling the platform's token-materialization endpoint.
|
|
120
|
-
*
|
|
121
|
-
* The endpoint is:
|
|
122
|
-
* POST {platformBaseUrl}/v1/assistants/{assistantId}/oauth/managed/materialize/
|
|
123
|
-
*
|
|
124
|
-
* The request body contains `{ connection_id: <uuid> }`.
|
|
125
|
-
*
|
|
126
|
-
* The platform validates the assistant API key, checks that the connection
|
|
127
|
-
* is active, and returns a fresh access token (refreshing upstream if
|
|
128
|
-
* needed).
|
|
129
|
-
*
|
|
130
|
-
* Fail-closed: any error results in a structured `MaterializationError`
|
|
131
|
-
* rather than a partial or fallback result.
|
|
132
|
-
*/
|
|
133
|
-
export async function materializeManagedToken(
|
|
134
|
-
subject: ManagedSubject,
|
|
135
|
-
options: ManagedMaterializerOptions
|
|
136
|
-
): Promise<MaterializeResult> {
|
|
137
|
-
// -- Validate prerequisites -----------------------------------------------
|
|
138
|
-
if (!options.platformBaseUrl) {
|
|
139
|
-
return {
|
|
140
|
-
ok: false,
|
|
141
|
-
error: new MaterializationError(
|
|
142
|
-
"MISSING_PLATFORM_URL",
|
|
143
|
-
"Platform base URL is required for managed token materialization"
|
|
144
|
-
),
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (!options.assistantApiKey) {
|
|
149
|
-
return {
|
|
150
|
-
ok: false,
|
|
151
|
-
error: new MaterializationError(
|
|
152
|
-
"MISSING_API_KEY",
|
|
153
|
-
"Assistant API key is required for managed token materialization"
|
|
154
|
-
),
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (!options.assistantId) {
|
|
159
|
-
return {
|
|
160
|
-
ok: false,
|
|
161
|
-
error: new MaterializationError(
|
|
162
|
-
"MISSING_ASSISTANT_ID",
|
|
163
|
-
"Assistant ID is required for managed token materialization"
|
|
164
|
-
),
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// -- Call platform token endpoint -----------------------------------------
|
|
169
|
-
const fetchFn = options.fetch ?? globalThis.fetch;
|
|
170
|
-
const materializeUrl = `${
|
|
171
|
-
options.platformBaseUrl
|
|
172
|
-
}/v1/assistants/${encodeURIComponent(
|
|
173
|
-
options.assistantId
|
|
174
|
-
)}/oauth/managed/materialize/`;
|
|
175
|
-
|
|
176
|
-
let response: Response;
|
|
177
|
-
try {
|
|
178
|
-
response = await fetchFn(materializeUrl, {
|
|
179
|
-
method: "POST",
|
|
180
|
-
headers: {
|
|
181
|
-
Authorization: `Api-Key ${options.assistantApiKey}`,
|
|
182
|
-
Accept: "application/json",
|
|
183
|
-
"Content-Type": "application/json",
|
|
184
|
-
},
|
|
185
|
-
body: JSON.stringify({ connection_id: subject.connectionId }),
|
|
186
|
-
});
|
|
187
|
-
} catch (err) {
|
|
188
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
189
|
-
return {
|
|
190
|
-
ok: false,
|
|
191
|
-
error: new MaterializationError(
|
|
192
|
-
"PLATFORM_UNREACHABLE",
|
|
193
|
-
`Failed to reach platform token endpoint: ${sanitizeError(message)}`
|
|
194
|
-
),
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// -- Handle error responses -----------------------------------------------
|
|
199
|
-
if (!response.ok) {
|
|
200
|
-
return {
|
|
201
|
-
ok: false,
|
|
202
|
-
error: mapPlatformError(response.status, subject.connectionId),
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// -- Parse token response -------------------------------------------------
|
|
207
|
-
let body: PlatformTokenResponse;
|
|
208
|
-
try {
|
|
209
|
-
body = (await response.json()) as PlatformTokenResponse;
|
|
210
|
-
} catch {
|
|
211
|
-
return {
|
|
212
|
-
ok: false,
|
|
213
|
-
error: new MaterializationError(
|
|
214
|
-
"INVALID_TOKEN_RESPONSE",
|
|
215
|
-
"Platform token endpoint returned invalid JSON"
|
|
216
|
-
),
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (!body.access_token || typeof body.access_token !== "string") {
|
|
221
|
-
return {
|
|
222
|
-
ok: false,
|
|
223
|
-
error: new MaterializationError(
|
|
224
|
-
"INVALID_TOKEN_RESPONSE",
|
|
225
|
-
"Platform token response missing access_token"
|
|
226
|
-
),
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// -- Build materialized token ---------------------------------------------
|
|
231
|
-
const expiresAt = parseExpiresAt(body.expires_at);
|
|
232
|
-
|
|
233
|
-
const token: MaterializedToken = {
|
|
234
|
-
accessToken: body.access_token,
|
|
235
|
-
tokenType: body.token_type ?? "Bearer",
|
|
236
|
-
expiresAt,
|
|
237
|
-
provider: subject.provider,
|
|
238
|
-
connectionId: subject.connectionId,
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
return { ok: true, token };
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// ---------------------------------------------------------------------------
|
|
245
|
-
// Helpers
|
|
246
|
-
// ---------------------------------------------------------------------------
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Map a platform HTTP error status to a structured MaterializationError.
|
|
250
|
-
*/
|
|
251
|
-
function mapPlatformError(
|
|
252
|
-
status: number,
|
|
253
|
-
connectionId: string
|
|
254
|
-
): MaterializationError {
|
|
255
|
-
switch (status) {
|
|
256
|
-
case 401:
|
|
257
|
-
return new MaterializationError(
|
|
258
|
-
"PLATFORM_AUTH_FAILED",
|
|
259
|
-
"Assistant API key is invalid or expired (HTTP 401)"
|
|
260
|
-
);
|
|
261
|
-
case 403:
|
|
262
|
-
return new MaterializationError(
|
|
263
|
-
"PLATFORM_FORBIDDEN",
|
|
264
|
-
"Assistant is not authorized to materialize this connection (HTTP 403)"
|
|
265
|
-
);
|
|
266
|
-
case 404:
|
|
267
|
-
return new MaterializationError(
|
|
268
|
-
"CONNECTION_NOT_FOUND",
|
|
269
|
-
`Connection ${connectionId} not found on the platform (HTTP 404)`
|
|
270
|
-
);
|
|
271
|
-
default:
|
|
272
|
-
return new MaterializationError(
|
|
273
|
-
`PLATFORM_HTTP_${status}`,
|
|
274
|
-
`Platform token endpoint returned HTTP ${status}`
|
|
275
|
-
);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Parse an ISO-8601 `expires_at` datetime string into epoch milliseconds.
|
|
281
|
-
* Returns null if the value is missing or invalid.
|
|
282
|
-
*/
|
|
283
|
-
function parseExpiresAt(expiresAt: string | null | undefined): number | null {
|
|
284
|
-
if (expiresAt == null) return null;
|
|
285
|
-
const ts = new Date(expiresAt).getTime();
|
|
286
|
-
if (Number.isNaN(ts)) return null;
|
|
287
|
-
return ts;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Sanitize error messages to avoid leaking secrets.
|
|
292
|
-
*/
|
|
293
|
-
function sanitizeError(message: string): string {
|
|
294
|
-
return message.replace(/Api-Key\s+\S+/gi, "Api-Key [REDACTED]");
|
|
295
|
-
}
|
package/src/subjects/local.ts
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CES local subject resolution.
|
|
3
|
-
*
|
|
4
|
-
* Resolves CES credential handles to their underlying storage subjects
|
|
5
|
-
* using the shared `@vellumai/credential-storage` primitives. This module
|
|
6
|
-
* is the CES-side counterpart to the assistant's credential resolver, but
|
|
7
|
-
* operates independently — it never imports from the assistant daemon.
|
|
8
|
-
*
|
|
9
|
-
* Subject resolution is the first phase of credential materialisation:
|
|
10
|
-
* 1. Parse the handle (via `@vellumai/service-contracts`)
|
|
11
|
-
* 2. Look up the metadata/connection record in local storage
|
|
12
|
-
* 3. Return a resolved subject that the materialiser can consume
|
|
13
|
-
*
|
|
14
|
-
* Both `local_static` and `local_oauth` handle types are supported.
|
|
15
|
-
* Unknown or disconnected handles fail before any outbound work starts.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import {
|
|
19
|
-
credentialKey,
|
|
20
|
-
type OAuthConnectionRecord,
|
|
21
|
-
type StaticCredentialRecord,
|
|
22
|
-
StaticCredentialMetadataStore,
|
|
23
|
-
} from "@vellumai/credential-storage";
|
|
24
|
-
import {
|
|
25
|
-
HandleType,
|
|
26
|
-
parseHandle,
|
|
27
|
-
type LocalOAuthHandle,
|
|
28
|
-
type LocalStaticHandle,
|
|
29
|
-
} from "@vellumai/service-contracts/credential-rpc";
|
|
30
|
-
|
|
31
|
-
// ---------------------------------------------------------------------------
|
|
32
|
-
// Resolved subject types
|
|
33
|
-
// ---------------------------------------------------------------------------
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* A resolved local static credential subject. Contains the metadata record
|
|
37
|
-
* and the secure-key storage key needed to materialise the secret value.
|
|
38
|
-
*/
|
|
39
|
-
export interface ResolvedStaticSubject {
|
|
40
|
-
type: typeof HandleType.LocalStatic;
|
|
41
|
-
/** The parsed handle. */
|
|
42
|
-
handle: LocalStaticHandle;
|
|
43
|
-
/** Non-secret metadata record from the credential store. */
|
|
44
|
-
metadata: StaticCredentialRecord;
|
|
45
|
-
/** Secure-key path where the secret value is stored. */
|
|
46
|
-
storageKey: string;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* A resolved local OAuth credential subject. Contains the connection record
|
|
51
|
-
* and the connection ID needed to materialise the access token.
|
|
52
|
-
*/
|
|
53
|
-
export interface ResolvedOAuthSubject {
|
|
54
|
-
type: typeof HandleType.LocalOAuth;
|
|
55
|
-
/** The parsed handle. */
|
|
56
|
-
handle: LocalOAuthHandle;
|
|
57
|
-
/** OAuth connection record from local persistence. */
|
|
58
|
-
connection: OAuthConnectionRecord;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export type ResolvedLocalSubject =
|
|
62
|
-
| ResolvedStaticSubject
|
|
63
|
-
| ResolvedOAuthSubject;
|
|
64
|
-
|
|
65
|
-
// ---------------------------------------------------------------------------
|
|
66
|
-
// Resolution result
|
|
67
|
-
// ---------------------------------------------------------------------------
|
|
68
|
-
|
|
69
|
-
export type SubjectResolutionResult =
|
|
70
|
-
| { ok: true; subject: ResolvedLocalSubject }
|
|
71
|
-
| { ok: false; error: string };
|
|
72
|
-
|
|
73
|
-
// ---------------------------------------------------------------------------
|
|
74
|
-
// OAuth connection lookup abstraction
|
|
75
|
-
// ---------------------------------------------------------------------------
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Abstraction for looking up local OAuth connection records.
|
|
79
|
-
*
|
|
80
|
-
* CES does not import the assistant's SQLite-backed oauth-store. Instead,
|
|
81
|
-
* callers provide a lightweight lookup interface that can be backed by
|
|
82
|
-
* any persistence mechanism (JSON file, SQLite, in-memory map).
|
|
83
|
-
*/
|
|
84
|
-
export interface OAuthConnectionLookup {
|
|
85
|
-
/** Look up a connection by its ID. Returns undefined if not found. */
|
|
86
|
-
getById(connectionId: string): OAuthConnectionRecord | undefined;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// ---------------------------------------------------------------------------
|
|
90
|
-
// Local subject resolver
|
|
91
|
-
// ---------------------------------------------------------------------------
|
|
92
|
-
|
|
93
|
-
export interface LocalSubjectResolverDeps {
|
|
94
|
-
/** Metadata store for local static credentials. */
|
|
95
|
-
metadataStore: StaticCredentialMetadataStore;
|
|
96
|
-
/** Lookup for local OAuth connections. */
|
|
97
|
-
oauthConnections: OAuthConnectionLookup;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Resolve a CES credential handle to a local subject.
|
|
102
|
-
*
|
|
103
|
-
* Supports `local_static` and `local_oauth` handle types. Returns a
|
|
104
|
-
* discriminated result so callers can inspect errors without catching
|
|
105
|
-
* exceptions.
|
|
106
|
-
*
|
|
107
|
-
* Resolution is fail-closed: unknown handle types, missing metadata,
|
|
108
|
-
* and disconnected OAuth connections all return errors before any
|
|
109
|
-
* outbound work starts.
|
|
110
|
-
*/
|
|
111
|
-
export function resolveLocalSubject(
|
|
112
|
-
rawHandle: string,
|
|
113
|
-
deps: LocalSubjectResolverDeps,
|
|
114
|
-
): SubjectResolutionResult {
|
|
115
|
-
const parseResult = parseHandle(rawHandle);
|
|
116
|
-
if (!parseResult.ok) {
|
|
117
|
-
return { ok: false, error: parseResult.error };
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const parsed = parseResult.handle;
|
|
121
|
-
|
|
122
|
-
switch (parsed.type) {
|
|
123
|
-
case HandleType.LocalStatic: {
|
|
124
|
-
const metadata = deps.metadataStore.getByServiceField(
|
|
125
|
-
parsed.service,
|
|
126
|
-
parsed.field,
|
|
127
|
-
);
|
|
128
|
-
if (!metadata) {
|
|
129
|
-
return {
|
|
130
|
-
ok: false,
|
|
131
|
-
error: `No local static credential found for service="${parsed.service}", field="${parsed.field}"`,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
const storageKey = credentialKey(parsed.service, parsed.field);
|
|
135
|
-
return {
|
|
136
|
-
ok: true,
|
|
137
|
-
subject: {
|
|
138
|
-
type: HandleType.LocalStatic,
|
|
139
|
-
handle: parsed,
|
|
140
|
-
metadata,
|
|
141
|
-
storageKey,
|
|
142
|
-
},
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
case HandleType.LocalOAuth: {
|
|
147
|
-
const connection = deps.oauthConnections.getById(parsed.connectionId);
|
|
148
|
-
if (!connection) {
|
|
149
|
-
return {
|
|
150
|
-
ok: false,
|
|
151
|
-
error: `No local OAuth connection found for connectionId="${parsed.connectionId}"`,
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
// Verify the provider key matches the connection's provider
|
|
155
|
-
if (connection.providerKey !== parsed.providerKey) {
|
|
156
|
-
return {
|
|
157
|
-
ok: false,
|
|
158
|
-
error: `OAuth connection "${parsed.connectionId}" has providerKey="${connection.providerKey}" but handle specifies "${parsed.providerKey}"`,
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
return {
|
|
162
|
-
ok: true,
|
|
163
|
-
subject: {
|
|
164
|
-
type: HandleType.LocalOAuth,
|
|
165
|
-
handle: parsed,
|
|
166
|
-
connection,
|
|
167
|
-
},
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
default:
|
|
172
|
-
return {
|
|
173
|
-
ok: false,
|
|
174
|
-
error: `Handle type "${parsed.type}" is not a local handle and cannot be resolved by the local subject resolver`,
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
}
|