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 }); } });