NODEDC_DESIGN_GUIDELINE/server/foundry-reader-grant-provis...

128 lines
5.4 KiB
JavaScript

import assert from "node:assert/strict";
import { createHash, generateKeyPairSync, verify } from "node:crypto";
import { chmod, mkdtemp, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import { createFoundryReaderGrantProvisioner } from "./foundry-reader-grant-provisioner.mjs";
const signatureSchema = "nodedc.external-data-plane.managed-provisioner-request/v1";
const audience = "nodedc-external-data-plane.managed-provisioning.v1";
const serviceId = "nodedc-module-foundry";
const keyId = "foundry-edp-managed-provisioner-v1";
const target = {
application: { id: "11111111-1111-4111-8111-111111111111" },
page: { id: "map" },
binding: { id: "fleet-current", dataProductId: "fleet.positions.current.v1" },
};
test("managed Foundry grant provisioning signs digest-only requests and persists the token privately", async () => {
const root = await mkdtemp(join(tmpdir(), "foundry-reader-grant-"));
const privateKeyFile = join(root, "private-key.pem");
const grantsDir = join(root, "grants");
const { privateKey, publicKey } = generateKeyPairSync("ed25519");
await writeFile(privateKeyFile, privateKey.export({ type: "pkcs8", format: "pem" }), { mode: 0o400 });
await chmod(privateKeyFile, 0o400);
const requestBodies = [];
let capabilityDigest = null;
const fetchImpl = async (input, options) => {
const url = new URL(input);
const body = String(options.body);
const parsed = JSON.parse(body);
requestBodies.push(body);
const bodySha256 = createHash("sha256").update(body, "utf8").digest("hex");
assert.equal(options.headers["x-nodedc-content-sha256"], bodySha256);
const signedPayload = JSON.stringify({
schemaVersion: signatureSchema,
audience,
serviceId,
keyId,
method: options.method,
path: url.pathname,
timestamp: options.headers["x-nodedc-request-timestamp"],
nonce: options.headers["x-nodedc-request-nonce"],
bodySha256,
});
assert.equal(verify(
null,
Buffer.from(signedPayload, "utf8"),
publicKey,
Buffer.from(options.headers["x-nodedc-request-signature"], "base64url"),
), true);
assert.equal(options.headers["x-nodedc-engine-service-id"], serviceId);
assert.equal(options.headers["x-nodedc-engine-key-id"], keyId);
assert.equal(options.headers["x-nodedc-request-audience"], audience);
assert.deepEqual(parsed.allowedDataProductIds, ["fleet.positions.current.v1"]);
for (const forbidden of ["provider", "providerId", "tenant", "tenantId", "connection", "connectionId", "token", "capability"]) {
assert.equal(Object.hasOwn(parsed, forbidden), false);
}
if (url.pathname.endsWith("/plan")) {
assert.deepEqual(Object.keys(parsed), ["allowedDataProductIds"]);
return Response.json({
ok: true,
sourceScope: "resolved-server-side",
dataProducts: [{
id: "fleet.positions.current.v1",
version: "1.0.0",
deliveryMode: "snapshot+patch",
semanticTypes: ["map.moving_object"],
active: true,
}],
});
}
assert.match(url.pathname, /\/consumer-reader-bindings\/by-key\/fndrc-[0-9a-f]{64}$/);
assert.deepEqual(Object.keys(parsed).sort(), ["allowedDataProductIds", "capabilityDigest", "expiresAt", "generation"].sort());
assert.match(parsed.capabilityDigest, /^[0-9a-f]{64}$/);
capabilityDigest ||= parsed.capabilityDigest;
assert.equal(parsed.capabilityDigest, capabilityDigest);
const bindingKey = decodeURIComponent(url.pathname.split("/").at(-1));
return Response.json({
ok: true,
idempotent: capabilityDigest === parsed.capabilityDigest,
readerBinding: {
bindingKey,
generation: 1,
active: true,
expiresAt: null,
sourceScope: "resolved-server-side",
allowedDataProductIds: parsed.allowedDataProductIds,
},
});
};
const provisioner = createFoundryReaderGrantProvisioner({
dataPlaneUrl: "http://edp.test",
privateKeyFile,
grantsDir,
serviceId,
keyId,
audience,
fetchImpl,
now: () => new Date("2026-07-19T14:00:00.000Z"),
randomBytesImpl: () => Buffer.alloc(32, 7),
randomUUIDImpl: () => "22222222-2222-4222-8222-222222222222",
production: false,
});
try {
assert.equal(provisioner.configured, true);
const planned = await provisioner.plan(target);
assert.equal(planned.product.id, "fleet.positions.current.v1");
const first = await provisioner.ensure(target);
const second = await provisioner.ensure(target);
assert.equal(first.ensured, true);
assert.equal(second.ensured, true);
const files = await readdir(grantsDir);
assert.equal(files.length, 1);
const tokenPath = join(grantsDir, files[0]);
const token = (await readFile(tokenPath, "utf8")).trim();
assert.match(token, /^ndc_edprb_/);
assert.equal((await stat(tokenPath)).mode & 0o777, 0o400);
assert.equal(createHash("sha256").update(token, "utf8").digest("hex"), capabilityDigest);
assert.equal(await provisioner.readToken(target), token);
assert.equal(requestBodies.some((body) => body.includes(token)), false);
assert.equal(JSON.stringify([planned, first, second]).includes(token), false);
assert.equal(requestBodies.some((body) => /provider|tenant|connection/i.test(body)), false);
} finally {
await rm(root, { recursive: true, force: true });
}
});