@go-to-k/cdkd 0.132.4 → 0.133.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/cli.js +83 -69
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -44085,7 +44085,7 @@ function attachAuthorizers(stacks, routes) {
|
|
|
44085
44085
|
const out = [];
|
|
44086
44086
|
const errors = [];
|
|
44087
44087
|
for (const route of routes) {
|
|
44088
|
-
if (route.unsupported || route.mockCors
|
|
44088
|
+
if (route.unsupported || route.mockCors) {
|
|
44089
44089
|
out.push({ route });
|
|
44090
44090
|
continue;
|
|
44091
44091
|
}
|
|
@@ -44115,45 +44115,6 @@ function attachAuthorizers(stacks, routes) {
|
|
|
44115
44115
|
return out;
|
|
44116
44116
|
}
|
|
44117
44117
|
/**
|
|
44118
|
-
* Walk every discovered route and return the service-integration routes
|
|
44119
|
-
* whose source CFn resource declares an authorizer (HTTP API v2 routes
|
|
44120
|
-
* with `AuthorizationType !== 'NONE'`). Service-integration routes only
|
|
44121
|
-
* exist on `AWS::ApiGatewayV2::Route`; REST v1 service integrations are
|
|
44122
|
-
* a different shape and not yet supported.
|
|
44123
|
-
*/
|
|
44124
|
-
function findIgnoredServiceIntegrationAuthorizers(stacks, routes) {
|
|
44125
|
-
const stackByRoute = /* @__PURE__ */ new Map();
|
|
44126
|
-
for (const stack of stacks) {
|
|
44127
|
-
const prefix = `${stack.stackName}/`;
|
|
44128
|
-
for (const route of routes) if (route.declaredAt.startsWith(prefix)) stackByRoute.set(route.declaredAt, stack);
|
|
44129
|
-
}
|
|
44130
|
-
const out = [];
|
|
44131
|
-
for (const route of routes) {
|
|
44132
|
-
if (!route.serviceIntegration) continue;
|
|
44133
|
-
const stack = stackByRoute.get(route.declaredAt);
|
|
44134
|
-
if (!stack) continue;
|
|
44135
|
-
const slash = route.declaredAt.indexOf("/");
|
|
44136
|
-
if (slash < 0) continue;
|
|
44137
|
-
const logicalId = route.declaredAt.slice(slash + 1);
|
|
44138
|
-
const resource = stack.template.Resources?.[logicalId];
|
|
44139
|
-
if (!resource || resource.Type !== "AWS::ApiGatewayV2::Route") continue;
|
|
44140
|
-
const props = resource.Properties ?? {};
|
|
44141
|
-
const authType = props["AuthorizationType"];
|
|
44142
|
-
if (typeof authType !== "string" || authType === "" || authType.toUpperCase() === "NONE") continue;
|
|
44143
|
-
let authorizerName;
|
|
44144
|
-
if (authType === "AWS_IAM") authorizerName = "AWS_IAM";
|
|
44145
|
-
else {
|
|
44146
|
-
const ref = props["AuthorizerId"];
|
|
44147
|
-
authorizerName = (ref && typeof ref === "object" && "Ref" in ref && typeof ref.Ref === "string" ? ref.Ref : void 0) ?? `<authType=${authType}, AuthorizerId malformed>`;
|
|
44148
|
-
}
|
|
44149
|
-
out.push({
|
|
44150
|
-
declaredAt: route.declaredAt,
|
|
44151
|
-
authorizerName
|
|
44152
|
-
});
|
|
44153
|
-
}
|
|
44154
|
-
return out;
|
|
44155
|
-
}
|
|
44156
|
-
/**
|
|
44157
44118
|
* Detect the authorizer (if any) attached to a discovered route.
|
|
44158
44119
|
* Walks the original CFn resource for the route in `stack.template`.
|
|
44159
44120
|
*/
|
|
@@ -45424,6 +45385,11 @@ function resolveSingleReference(ref, ctx) {
|
|
|
45424
45385
|
}
|
|
45425
45386
|
if (ref.startsWith("$context.")) {
|
|
45426
45387
|
const key = ref.substring(9);
|
|
45388
|
+
if (key === "authorizer" || key.startsWith("authorizer.")) {
|
|
45389
|
+
if (!ctx.authorizer) return "";
|
|
45390
|
+
if (key === "authorizer") return JSON.stringify(ctx.authorizer);
|
|
45391
|
+
return resolveAuthorizerPath(ctx.authorizer, key.substring(11));
|
|
45392
|
+
}
|
|
45427
45393
|
return ctx.context[key] ?? "";
|
|
45428
45394
|
}
|
|
45429
45395
|
if (ref.startsWith("$stageVariables.")) {
|
|
@@ -45467,6 +45433,36 @@ function resolveBodyJsonPath(body, path) {
|
|
|
45467
45433
|
if (typeof cursor === "string") return cursor;
|
|
45468
45434
|
return JSON.stringify(cursor);
|
|
45469
45435
|
}
|
|
45436
|
+
/**
|
|
45437
|
+
* Walk a dot-separated path against the authorizer verdict map (#502).
|
|
45438
|
+
* Used by `$context.authorizer.X` selection expressions on
|
|
45439
|
+
* service-integration routes. Mirrors `resolveBodyJsonPath`'s shape:
|
|
45440
|
+
* - Empty leaf / undefined → `""`.
|
|
45441
|
+
* - String leaf → returned verbatim.
|
|
45442
|
+
* - Non-string leaf → `JSON.stringify`'d (matches AWS-deployed
|
|
45443
|
+
* behavior; `$context.authorizer.jwt.claims` resolves to the JSON
|
|
45444
|
+
* object as a string).
|
|
45445
|
+
*
|
|
45446
|
+
* Array indexing via `[N]` is supported (rare for authorizer
|
|
45447
|
+
* verdicts but cheap to support).
|
|
45448
|
+
*/
|
|
45449
|
+
function resolveAuthorizerPath(authorizer, path) {
|
|
45450
|
+
if (path === "") return "";
|
|
45451
|
+
const segments = path.split(/\.|\[(\d+)\]/).filter((s) => s !== void 0 && s !== "");
|
|
45452
|
+
let cursor = authorizer;
|
|
45453
|
+
for (const seg of segments) {
|
|
45454
|
+
if (cursor === null || cursor === void 0) return "";
|
|
45455
|
+
if (typeof cursor !== "object") return "";
|
|
45456
|
+
if (Array.isArray(cursor)) {
|
|
45457
|
+
const idx = Number(seg);
|
|
45458
|
+
if (!Number.isInteger(idx)) return "";
|
|
45459
|
+
cursor = cursor[idx];
|
|
45460
|
+
} else cursor = cursor[seg];
|
|
45461
|
+
}
|
|
45462
|
+
if (cursor === void 0 || cursor === null) return "";
|
|
45463
|
+
if (typeof cursor === "string") return cursor;
|
|
45464
|
+
return JSON.stringify(cursor);
|
|
45465
|
+
}
|
|
45470
45466
|
|
|
45471
45467
|
//#endregion
|
|
45472
45468
|
//#region src/local/http-server.ts
|
|
@@ -45568,10 +45564,6 @@ async function handleRequest(req, res, state, opts) {
|
|
|
45568
45564
|
writeNotImplemented(res, match.route.unsupported.reason);
|
|
45569
45565
|
return;
|
|
45570
45566
|
}
|
|
45571
|
-
if (match.route.serviceIntegration) {
|
|
45572
|
-
await handleServiceIntegrationRequest(req, res, match, bodyBuf, opts);
|
|
45573
|
-
return;
|
|
45574
|
-
}
|
|
45575
45567
|
const authorizer = state.routes.find((r) => r.route.declaredAt === match.route.declaredAt && r.route.method === match.route.method)?.authorizer;
|
|
45576
45568
|
const clientCert = opts.mtls ? extractClientCert(req) : void 0;
|
|
45577
45569
|
const snapshot = {
|
|
@@ -45606,6 +45598,10 @@ async function handleRequest(req, res, state, opts) {
|
|
|
45606
45598
|
const overlay = buildOverlay(authorizer, authResult);
|
|
45607
45599
|
if (overlay) baseEvent = applyAuthorizerOverlay(baseEvent, overlay);
|
|
45608
45600
|
}
|
|
45601
|
+
if (match.route.serviceIntegration) {
|
|
45602
|
+
await handleServiceIntegrationRequest(req, res, match, bodyBuf, opts, authorizer, authResult);
|
|
45603
|
+
return;
|
|
45604
|
+
}
|
|
45609
45605
|
if (match.route.restV1Integration) {
|
|
45610
45606
|
try {
|
|
45611
45607
|
writeIntegrationOutcome(res, await dispatchRestV1Integration(match.route.restV1Integration, snapshot, matchCtx, state, opts));
|
|
@@ -46171,7 +46167,7 @@ function writeError(res, statusCode, body = "{\"message\":\"Internal server erro
|
|
|
46171
46167
|
* auth pass. A follow-up PR can hoist the auth pass earlier — keeping it
|
|
46172
46168
|
* out of this PR limits the blast radius.
|
|
46173
46169
|
*/
|
|
46174
|
-
async function handleServiceIntegrationRequest(req, res, match, bodyBuf, opts) {
|
|
46170
|
+
async function handleServiceIntegrationRequest(req, res, match, bodyBuf, opts, authorizer, authResult) {
|
|
46175
46171
|
const route = match.route;
|
|
46176
46172
|
const svc = route.serviceIntegration;
|
|
46177
46173
|
if (!svc) {
|
|
@@ -46183,6 +46179,7 @@ async function handleServiceIntegrationRequest(req, res, match, bodyBuf, opts) {
|
|
|
46183
46179
|
const queryString = parseQueryStringSingular(rawUrl);
|
|
46184
46180
|
const requestPath = rawUrl.split("?")[0] ?? "/";
|
|
46185
46181
|
const context = buildServiceIntegrationContextVars(req, route);
|
|
46182
|
+
const authorizerCtx = buildAuthorizerContextForServiceIntegration(authorizer, authResult);
|
|
46186
46183
|
const ctx = {
|
|
46187
46184
|
headers: headersFlat,
|
|
46188
46185
|
queryString,
|
|
@@ -46190,7 +46187,8 @@ async function handleServiceIntegrationRequest(req, res, match, bodyBuf, opts) {
|
|
|
46190
46187
|
requestPath,
|
|
46191
46188
|
body: bodyBuf.toString("utf8"),
|
|
46192
46189
|
context,
|
|
46193
|
-
stageVariables: route.stageVariables ?? {}
|
|
46190
|
+
stageVariables: route.stageVariables ?? {},
|
|
46191
|
+
...authorizerCtx && { authorizer: authorizerCtx }
|
|
46194
46192
|
};
|
|
46195
46193
|
const outcome = resolveServiceIntegrationParameters(svc.requestParameters, ctx);
|
|
46196
46194
|
if (outcome.kind === "error") {
|
|
@@ -46261,6 +46259,44 @@ function buildServiceIntegrationContextVars(req, route) {
|
|
|
46261
46259
|
};
|
|
46262
46260
|
}
|
|
46263
46261
|
/**
|
|
46262
|
+
* Build the `authorizer` field for the parameter-mapping context on
|
|
46263
|
+
* service-integration routes (#502). Surfaces the authorizer's verdict
|
|
46264
|
+
* in the same shape `applyAuthorizerOverlay` writes onto the Lambda
|
|
46265
|
+
* event so users can reference `$context.authorizer.X` /
|
|
46266
|
+
* `$context.authorizer.jwt.claims.X` / `$context.authorizer.claims.X`
|
|
46267
|
+
* in `RequestParameters`.
|
|
46268
|
+
*
|
|
46269
|
+
* Per-kind shape:
|
|
46270
|
+
* - Lambda authorizers (`lambda-token` / `lambda-request` / `iam`):
|
|
46271
|
+
* `principalId` + flat `context` fields land at the top level
|
|
46272
|
+
* (`$context.authorizer.principalId`, `$context.authorizer.<key>`).
|
|
46273
|
+
* - Cognito (REST v1): claims under `$context.authorizer.claims.X`.
|
|
46274
|
+
* - JWT (HTTP v2): claims under `$context.authorizer.jwt.claims.X` +
|
|
46275
|
+
* `$context.authorizer.jwt.scopes`.
|
|
46276
|
+
*
|
|
46277
|
+
* Returns `undefined` when no authorizer fired (route had
|
|
46278
|
+
* `AuthorizationType: NONE` / no authorizer attached).
|
|
46279
|
+
*/
|
|
46280
|
+
function buildAuthorizerContextForServiceIntegration(authorizer, result) {
|
|
46281
|
+
if (!authorizer || !result) return void 0;
|
|
46282
|
+
if (authorizer.kind === "lambda-token" || authorizer.kind === "lambda-request") {
|
|
46283
|
+
const ctx = {};
|
|
46284
|
+
if (result.principalId !== void 0) ctx["principalId"] = result.principalId;
|
|
46285
|
+
if (result.context) Object.assign(ctx, result.context);
|
|
46286
|
+
return ctx;
|
|
46287
|
+
}
|
|
46288
|
+
if (authorizer.kind === "iam") {
|
|
46289
|
+
const ctx = {};
|
|
46290
|
+
if (result.principalId !== void 0) ctx["principalId"] = result.principalId;
|
|
46291
|
+
return ctx;
|
|
46292
|
+
}
|
|
46293
|
+
if (authorizer.kind === "cognito") return { claims: { ...result.context ?? {} } };
|
|
46294
|
+
return { jwt: {
|
|
46295
|
+
claims: { ...result.context ?? {} },
|
|
46296
|
+
scopes: []
|
|
46297
|
+
} };
|
|
46298
|
+
}
|
|
46299
|
+
/**
|
|
46264
46300
|
* Write the 501 Not Implemented response surfaced for routes the
|
|
46265
46301
|
* discovery layer flagged as `unsupported`. The integration's reason
|
|
46266
46302
|
* (e.g. "MOCK integration is not emulated", "WebSocket APIs are not
|
|
@@ -47022,7 +47058,6 @@ async function localStartApiCommand(target, options) {
|
|
|
47022
47058
|
warnVpcConfigLambdas(initialMaterial.routes, initialMaterial.stacks ?? []);
|
|
47023
47059
|
sigV4CredentialsLoader = defaultCredentialsLoader();
|
|
47024
47060
|
warnIamRoutes(initialMaterial.routes);
|
|
47025
|
-
warnIgnoredServiceIntegrationAuthorizers(initialMaterial.routes, initialMaterial.stacks ?? []);
|
|
47026
47061
|
let maxTimeoutSec = 0;
|
|
47027
47062
|
for (const spec of initialMaterial.specs.values()) if (spec.lambda.timeoutSec > maxTimeoutSec) maxTimeoutSec = spec.lambda.timeoutSec;
|
|
47028
47063
|
const rieTimeoutMs = Math.max(3e4, maxTimeoutSec * 2 * 1e3);
|
|
@@ -47284,27 +47319,6 @@ function warnIamRoutes(routesWithAuth) {
|
|
|
47284
47319
|
return true;
|
|
47285
47320
|
}
|
|
47286
47321
|
/**
|
|
47287
|
-
* #458 / PR #500 review: emit a one-line warn naming every service-
|
|
47288
|
-
* integration route whose source CFn resource declares an authorizer
|
|
47289
|
-
* (HTTP API v2 routes with `AuthorizationType !== 'NONE'`). The
|
|
47290
|
-
* dispatcher in `http-server.ts` runs the SDK call BEFORE the
|
|
47291
|
-
* authorizer pass would fire, so without this warn a CDK app that
|
|
47292
|
-
* wires JWT / Lambda / Cognito / IAM authorizers onto service
|
|
47293
|
-
* integrations would silently let every local request reach the SDK
|
|
47294
|
-
* call without auth. Threading the auth pass through the
|
|
47295
|
-
* service-integration dispatcher is a follow-up issue. Returns the
|
|
47296
|
-
* number of warned routes so tests can assert the firing path; the
|
|
47297
|
-
* value is otherwise unused.
|
|
47298
|
-
*/
|
|
47299
|
-
function warnIgnoredServiceIntegrationAuthorizers(routesWithAuth, stacks) {
|
|
47300
|
-
const logger = getLogger();
|
|
47301
|
-
const ignored = findIgnoredServiceIntegrationAuthorizers(stacks, routesWithAuth.map((entry) => entry.route));
|
|
47302
|
-
if (ignored.length === 0) return 0;
|
|
47303
|
-
logger.warn(`${ignored.length} HTTP API v2 service-integration route(s) declare an authorizer but cdkd local start-api dispatches the SDK call BEFORE the authorizer pass — every local request reaches the SDK call WITHOUT authentication. This is a deferred feature; see https://github.com/go-to-k/cdkd/issues/502 for the follow-up tracking issue.`);
|
|
47304
|
-
for (const entry of ignored) logger.warn(` - ${entry.declaredAt}: authorizer '${entry.authorizerName}' is configured but ignored`);
|
|
47305
|
-
return ignored.length;
|
|
47306
|
-
}
|
|
47307
|
-
/**
|
|
47308
47322
|
* Build the per-Lambda container spec — code dir, env vars (template +
|
|
47309
47323
|
* --env-vars overlay), STS-issued creds when --assume-role names this
|
|
47310
47324
|
* Lambda, optional --debug-port reservation. Errors out with a clear
|
|
@@ -51873,7 +51887,7 @@ function reorderArgs(argv) {
|
|
|
51873
51887
|
*/
|
|
51874
51888
|
async function main() {
|
|
51875
51889
|
const program = new Command();
|
|
51876
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
51890
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.133.0");
|
|
51877
51891
|
program.addCommand(createBootstrapCommand());
|
|
51878
51892
|
program.addCommand(createSynthCommand());
|
|
51879
51893
|
program.addCommand(createListCommand());
|