feat(data-plane): add signed managed writer bindings

This commit is contained in:
Codex
2026-07-17 18:09:15 +03:00
parent 3415674e76
commit a0a4d36fa2
13 changed files with 1146 additions and 21 deletions
@@ -1,10 +1,16 @@
import assert from "node:assert/strict";
import { spawn } from "node:child_process";
import { createHash, generateKeyPairSync, randomBytes, sign } from "node:crypto";
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import net from "node:net";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { validateDataProductPatch, validateDataProductSnapshot } from "@nodedc/external-provider-contract";
import { validateDataProductPatch, validateDataProductSnapshot } from "@nodedc/external-provider-contract/data-plane";
import {
MANAGED_PROVISIONER_HEADERS,
managedProvisionerSigningPayload,
sha256RawBody,
} from "../src/managed-provisioner-auth.mjs";
const databaseUrl = process.env.EXTERNAL_DATA_PLANE_TEST_DATABASE_URL;
if (!databaseUrl) throw new Error("EXTERNAL_DATA_PLANE_TEST_DATABASE_URL_required");
@@ -13,7 +19,17 @@ const baseUrl = `http://127.0.0.1:${port}`;
const directory = await mkdtemp(join(tmpdir(), "nodedc-edp-api-test-"));
const provisionerSecret = "nodedc_edp_integration_provisioner_secret_0123456789abcdef";
const secretPath = join(directory, "provisioner-token");
const managedProvisionerServiceId = "nodedc-engine";
const managedProvisionerKeyId = "engine-edp-managed-provisioner-v1";
const managedProvisionerAudience = "nodedc-external-data-plane.managed-provisioning.v1";
const managedProvisionerPublicKeyPath = join(directory, "engine-public-key.pem");
const { privateKey: managedProvisionerPrivateKey, publicKey: managedProvisionerPublicKey } = generateKeyPairSync("ed25519");
await writeFile(secretPath, `${provisionerSecret}\n`, { mode: 0o600 });
await writeFile(
managedProvisionerPublicKeyPath,
managedProvisionerPublicKey.export({ type: "spki", format: "pem" }),
{ mode: 0o600 },
);
const child = spawn(process.execPath, ["src/server.mjs"], {
cwd: new URL("..", import.meta.url),
@@ -22,7 +38,12 @@ const child = spawn(process.execPath, ["src/server.mjs"], {
PORT: String(port),
EXTERNAL_DATA_PLANE_DATABASE_URL: databaseUrl,
EXTERNAL_DATA_PLANE_PROVISIONING_ENABLED: "true",
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONING_ENABLED: "true",
EXTERNAL_DATA_PLANE_PROVISIONER_TOKEN_FILE: secretPath,
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_PUBLIC_KEY_FILE: managedProvisionerPublicKeyPath,
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_SERVICE_ID: managedProvisionerServiceId,
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_KEY_ID: managedProvisionerKeyId,
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_AUDIENCE: managedProvisionerAudience,
EXTERNAL_DATA_PLANE_RETENTION_SWEEP_MS: "60000",
EXTERNAL_DATA_PLANE_STREAM_POLL_MS: "250",
EXTERNAL_DATA_PLANE_STREAM_HEARTBEAT_MS: "5000",
@@ -52,10 +73,139 @@ try {
const expiry = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString();
const scope = { tenantId: "api-tenant", connectionId: "api-connection", providerId: "api-provider" };
const managedToken = "ndc_edpwb_managed_api_test_0123456789abcdefghijklmnopqrstuvwxyz";
const managedBody = {
source: scope,
allowedDataProductIds: [productId],
expiresAt: expiry,
generation: 1,
capabilityDigest: createHash("sha256").update(managedToken, "utf8").digest("hex"),
};
const managedPath = "/internal/data-plane/v1/writer-bindings/by-key/engine.api-connection.positions";
const bearerOnlyManaged = await fetch(`${baseUrl}${managedPath}`, {
method: "PUT",
headers: { Authorization: `Bearer ${provisionerSecret}`, "Content-Type": "application/json" },
body: JSON.stringify(managedBody),
});
assert.equal(bearerOnlyManaged.status, 401);
assert.equal((await bearerOnlyManaged.json()).error, "managed_provisioner_header_invalid");
const managedResults = await Promise.all([
rawJsonRequest(managedPath, {
method: "PUT",
managedSignature: true,
body: managedBody,
}),
rawJsonRequest(managedPath, {
method: "PUT",
managedSignature: true,
body: managedBody,
}),
]);
assert.deepEqual(managedResults.map(({ value }) => value.idempotent).sort(), [false, true]);
assert.equal(managedResults[0].value.writerBinding.id, managedResults[1].value.writerBinding.id);
assert.equal("token" in managedResults[0].value, false);
assert.equal(JSON.stringify(managedResults[0].value).includes(managedBody.capabilityDigest), false);
const managedCatalog = await jsonRequest("/internal/data-plane/v1/writer/data-products", { token: managedToken });
assert.deepEqual(managedCatalog.dataProducts.map((value) => value.id), [productId]);
const replayBody = JSON.stringify(managedBody);
const replayHeaders = signedManagedHeaders(managedPath, "PUT", Buffer.from(replayBody, "utf8"));
const firstReplayAttempt = await fetch(`${baseUrl}${managedPath}`, {
method: "PUT", headers: { ...replayHeaders, "Content-Type": "application/json" }, body: replayBody,
});
assert.equal(firstReplayAttempt.status, 200);
const rejectedReplay = await fetch(`${baseUrl}${managedPath}`, {
method: "PUT", headers: { ...replayHeaders, "Content-Type": "application/json" }, body: replayBody,
});
assert.equal(rejectedReplay.status, 409);
assert.equal((await rejectedReplay.json()).error, "managed_provisioner_request_replayed");
const tamperHeaders = signedManagedHeaders(managedPath, "PUT", Buffer.from(replayBody, "utf8"));
const tamperedBody = await fetch(`${baseUrl}${managedPath}`, {
method: "PUT",
headers: { ...tamperHeaders, "Content-Type": "application/json" },
body: `${replayBody} `,
});
assert.equal(tamperedBody.status, 401);
assert.equal((await tamperedBody.json()).error, "managed_provisioner_body_hash_mismatch");
const pathHeaders = signedManagedHeaders(managedPath, "PUT", Buffer.from(replayBody, "utf8"));
const tamperedPath = await fetch(`${baseUrl}${managedPath}?unexpected=true`, {
method: "PUT", headers: { ...pathHeaders, "Content-Type": "application/json" }, body: replayBody,
});
assert.equal(tamperedPath.status, 401);
assert.equal((await tamperedPath.json()).error, "managed_provisioner_signature_invalid");
const audienceHeaders = signedManagedHeaders(managedPath, "PUT", Buffer.from(replayBody, "utf8"));
audienceHeaders[MANAGED_PROVISIONER_HEADERS.audience] = "other-audience";
const wrongAudience = await fetch(`${baseUrl}${managedPath}`, {
method: "PUT", headers: { ...audienceHeaders, "Content-Type": "application/json" }, body: replayBody,
});
assert.equal(wrongAudience.status, 401);
assert.equal((await wrongAudience.json()).error, "managed_provisioner_identity_mismatch");
const managedConflict = await signedFetch(managedPath, {
method: "PUT",
body: {
...managedBody,
capabilityDigest: createHash("sha256").update(`${managedToken}-different`, "utf8").digest("hex"),
},
});
assert.equal(managedConflict.status, 409);
assert.equal((await managedConflict.json()).error, "managed_writer_binding_request_conflict");
const legacyManagedRevoke = await fetch(
`${baseUrl}/internal/data-plane/v1/writer-bindings/${managedResults[0].value.writerBinding.id}/revoke`,
{ method: "POST", headers: { Authorization: `Bearer ${provisionerSecret}` } },
);
assert.equal(legacyManagedRevoke.status, 404);
const managedTokenGeneration2 = `${managedToken}_generation_2`;
const managedGeneration2 = await jsonRequest(
"/internal/data-plane/v1/writer-bindings/by-key/engine.api-connection.positions",
{
method: "PUT",
managedSignature: true,
body: {
...managedBody,
generation: 2,
capabilityDigest: createHash("sha256").update(managedTokenGeneration2, "utf8").digest("hex"),
},
},
);
assert.equal(managedGeneration2.writerBinding.generation, 2);
assert.deepEqual(
(await jsonRequest("/internal/data-plane/v1/writer/data-products", { token: managedTokenGeneration2 })).dataProducts.map((value) => value.id),
[productId],
);
const managedRevoke = await jsonRequest(
"/internal/data-plane/v1/writer-bindings/by-key/engine.api-connection.positions/generations/1/revoke",
{ method: "POST", managedSignature: true },
);
assert.equal(managedRevoke.idempotent, false);
assert.equal(managedRevoke.writerBinding.active, false);
const managedRevokeRetry = await jsonRequest(
"/internal/data-plane/v1/writer-bindings/by-key/engine.api-connection.positions/generations/1/revoke",
{ method: "POST", managedSignature: true },
);
assert.equal(managedRevokeRetry.idempotent, true);
const rejectedManagedGeneration1 = await fetch(`${baseUrl}/internal/data-plane/v1/writer/data-products`, {
headers: { Authorization: `Bearer ${managedToken}` },
});
assert.equal(rejectedManagedGeneration1.status, 401);
const manualWriterBody = { source: scope, allowedDataProductIds: [productId], expiresAt: expiry };
const signedOnlyManualProvisioning = await signedFetch("/internal/data-plane/v1/writer-bindings", {
method: "POST",
body: manualWriterBody,
});
assert.equal(signedOnlyManualProvisioning.status, 401);
assert.equal((await signedOnlyManualProvisioning.json()).error, "provisioner_unauthorized");
const writerIssuance = await rawJsonRequest("/internal/data-plane/v1/writer-bindings", {
method: "POST",
token: provisionerSecret,
body: { source: scope, allowedDataProductIds: [productId], expiresAt: expiry },
body: manualWriterBody,
});
const readerIssuance = await rawJsonRequest("/internal/data-plane/v1/reader-bindings", {
method: "POST",
@@ -176,26 +326,66 @@ function publish(runId, observedAt, longitude) {
};
}
async function jsonRequest(path, { method = "GET", token, body } = {}) {
const { response, value } = await rawJsonRequest(path, { method, token, body });
async function jsonRequest(path, { method = "GET", token, managedSignature = false, body } = {}) {
const { response, value } = await rawJsonRequest(path, { method, token, managedSignature, body });
if (!response.ok) throw new Error(`request_failed:${response.status}:${value.error}`);
return value;
}
async function rawJsonRequest(path, { method = "GET", token, body } = {}) {
async function rawJsonRequest(path, { method = "GET", token, managedSignature = false, body } = {}) {
const rawBody = body === undefined ? Buffer.alloc(0) : Buffer.from(JSON.stringify(body), "utf8");
const response = await fetch(`${baseUrl}${path}`, {
method,
headers: {
...(token ? { Authorization: `Bearer ${token}` } : {}),
...(body ? { "Content-Type": "application/json" } : {}),
...(managedSignature ? signedManagedHeaders(path, method, rawBody) : {}),
...(body === undefined ? {} : { "Content-Type": "application/json" }),
},
body: body ? JSON.stringify(body) : undefined,
body: body === undefined ? undefined : rawBody,
});
const value = await response.json();
if (!response.ok) throw new Error(`request_failed:${response.status}:${value.error}`);
return { response, value };
}
async function signedFetch(path, { method, body } = {}) {
const rawBody = body === undefined ? Buffer.alloc(0) : Buffer.from(JSON.stringify(body), "utf8");
return fetch(`${baseUrl}${path}`, {
method,
headers: {
...signedManagedHeaders(path, method, rawBody),
...(body === undefined ? {} : { "Content-Type": "application/json" }),
},
body: body === undefined ? undefined : rawBody,
});
}
function signedManagedHeaders(path, method, rawBody) {
const timestamp = new Date().toISOString();
const nonce = randomBytes(24).toString("base64url");
const bodySha256 = sha256RawBody(rawBody);
const payload = managedProvisionerSigningPayload({
audience: managedProvisionerAudience,
serviceId: managedProvisionerServiceId,
keyId: managedProvisionerKeyId,
method,
path,
timestamp,
nonce,
bodySha256,
});
const signature = sign(null, Buffer.from(payload, "utf8"), managedProvisionerPrivateKey).toString("base64url");
return {
[MANAGED_PROVISIONER_HEADERS.serviceId]: managedProvisionerServiceId,
[MANAGED_PROVISIONER_HEADERS.keyId]: managedProvisionerKeyId,
[MANAGED_PROVISIONER_HEADERS.audience]: managedProvisionerAudience,
[MANAGED_PROVISIONER_HEADERS.timestamp]: timestamp,
[MANAGED_PROVISIONER_HEADERS.nonce]: nonce,
[MANAGED_PROVISIONER_HEADERS.bodySha256]: bodySha256,
[MANAGED_PROVISIONER_HEADERS.signature]: signature,
};
}
function assertOneTimeCapabilityResponse(response) {
assert.equal(response.headers.get("cache-control"), "no-store, max-age=0");
assert.equal(response.headers.get("pragma"), "no-cache");
@@ -1,4 +1,5 @@
import assert from "node:assert/strict";
import { generateKeyPairSync } from "node:crypto";
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
@@ -11,9 +12,12 @@ const base = {
const directory = await mkdtemp(join(tmpdir(), "nodedc-edp-config-"));
const secretPath = join(directory, "provisioner-token");
const publicKeyPath = join(directory, "engine-public-key.pem");
const secret = "provisioner_secret_is_separate_from_legacy_internal_token_123456";
try {
await writeFile(secretPath, `${secret}\n`, { encoding: "utf8", mode: 0o600 });
const { publicKey } = generateKeyPairSync("ed25519");
await writeFile(publicKeyPath, publicKey.export({ type: "spki", format: "pem" }), { mode: 0o600 });
const config = readConfig({
...base,
EXTERNAL_DATA_PLANE_PROVISIONING_ENABLED: "true",
@@ -22,6 +26,8 @@ try {
EXTERNAL_DATA_PLANE_RETENTION_SWEEP_MS: "60000",
});
assert.equal(config.provisionerAccessToken, secret);
assert.equal(config.provisionerApiEnabled, true);
assert.equal(config.managedProvisionerApiEnabled, false);
assert.equal(config.maxFutureSkewSeconds, 120);
assert.equal(config.retentionSweepMs, 60000);
assert.equal(config.maxPatchBytes, 262144);
@@ -39,10 +45,49 @@ try {
EXTERNAL_DATA_PLANE_PROVISIONING_ENABLED: "true",
EXTERNAL_DATA_PLANE_PROVISIONER_TOKEN_FILE: join(directory, "missing"),
}), /external_data_plane_provisioner_token_file_unreadable/);
const managedConfig = readConfig({
...base,
EXTERNAL_DATA_PLANE_PROVISIONING_ENABLED: "false",
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONING_ENABLED: "true",
EXTERNAL_DATA_PLANE_PROVISIONER_TOKEN_FILE: join(directory, "missing-token-is-ignored"),
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_PUBLIC_KEY_FILE: publicKeyPath,
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_SERVICE_ID: "nodedc-engine",
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_KEY_ID: "engine-edp-managed-provisioner-v1",
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_AUDIENCE: "nodedc-external-data-plane.managed-provisioning.v1",
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_MAX_SKEW_SECONDS: "45",
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_REPLAY_CACHE_MAX_ENTRIES: "500",
});
assert.equal(managedConfig.provisionerApiEnabled, false);
assert.equal(managedConfig.managedProvisionerApiEnabled, true);
assert.equal(managedConfig.provisionerAccessToken, "");
assert.equal(managedConfig.managedProvisionerPublicKey.asymmetricKeyType, "ed25519");
assert.equal(managedConfig.managedProvisionerServiceId, "nodedc-engine");
assert.equal(managedConfig.managedProvisionerKeyId, "engine-edp-managed-provisioner-v1");
assert.equal(managedConfig.managedProvisionerAudience, "nodedc-external-data-plane.managed-provisioning.v1");
assert.equal(managedConfig.managedProvisionerMaxSkewMs, 45_000);
assert.equal(managedConfig.managedProvisionerReplayCacheMaxEntries, 500);
assert.throws(() => readConfig({
...base,
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONING_ENABLED: "true",
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_PUBLIC_KEY_FILE: join(directory, "missing"),
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_SERVICE_ID: "nodedc-engine",
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_KEY_ID: "engine-edp-managed-provisioner-v1",
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_AUDIENCE: "nodedc-external-data-plane.managed-provisioning.v1",
}), /external_data_plane_managed_provisioner_public_key_file_unreadable/);
assert.throws(() => readConfig({
...base,
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONING_ENABLED: "true",
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_PUBLIC_KEY_FILE: publicKeyPath,
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_SERVICE_ID: "invalid service id",
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_KEY_ID: "engine-edp-managed-provisioner-v1",
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_AUDIENCE: "nodedc-external-data-plane.managed-provisioning.v1",
}), /external_data_plane_managed_provisioner_service_id_invalid/);
assert.equal(readConfig({
...base,
EXTERNAL_DATA_PLANE_PROVISIONING_ENABLED: "false",
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONING_ENABLED: "false",
EXTERNAL_DATA_PLANE_PROVISIONER_TOKEN_FILE: join(directory, "missing"),
EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_PUBLIC_KEY_FILE: join(directory, "missing"),
}).provisionerAccessToken, "");
} finally {
await rm(directory, { recursive: true, force: true });
@@ -1,6 +1,6 @@
import assert from "node:assert/strict";
import { Pool } from "pg";
import { validateDataProductPatch, validateDataProductSnapshot } from "@nodedc/external-provider-contract";
import { validateDataProductPatch, validateDataProductSnapshot } from "@nodedc/external-provider-contract/data-plane";
import {
loadDataProductDefinition,
persistDataProductDefinition,
@@ -0,0 +1,217 @@
import assert from "node:assert/strict";
import {
generateKeyPairSync,
randomBytes,
sign,
} from "node:crypto";
import { chmod, mkdtemp, rm, symlink, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import {
createManagedProvisionerRequestVerifier,
loadManagedProvisionerPublicKeyFile,
MANAGED_PROVISIONER_HEADERS,
managedProvisionerSigningPayload,
ManagedProvisionerReplayCache,
sha256RawBody,
} from "../src/managed-provisioner-auth.mjs";
const serviceId = "nodedc-engine";
const keyId = "engine-edp-managed-provisioner-v1";
const audience = "nodedc-external-data-plane.managed-provisioning.v1";
const nowMs = Date.parse("2026-07-16T12:00:00.000Z");
const { privateKey, publicKey } = generateKeyPairSync("ed25519");
let nonceSequence = 0;
const replayCache = new ManagedProvisionerReplayCache({ maxEntries: 100 });
const verify = createManagedProvisionerRequestVerifier({
publicKey,
serviceId,
keyId,
audience,
maxSkewMs: 60_000,
replayCache,
now: () => nowMs,
});
const valid = signedRequest();
assert.deepEqual(verify(valid), { serviceId, keyId });
assert.throws(() => verify(valid), hasCode("managed_provisioner_request_replayed", 409));
const whitespaceTamper = signedRequest();
whitespaceTamper.rawBody = Buffer.from(`${whitespaceTamper.rawBody.toString("utf8")} `, "utf8");
assert.throws(() => verify(whitespaceTamper), hasCode("managed_provisioner_body_hash_mismatch", 401));
const methodTamper = signedRequest();
methodTamper.method = "POST";
assert.throws(() => verify(methodTamper), hasCode("managed_provisioner_signature_invalid", 401));
const pathTamper = signedRequest();
pathTamper.originalUrl = `${pathTamper.originalUrl}?unexpected=true`;
assert.throws(() => verify(pathTamper), hasCode("managed_provisioner_signature_invalid", 401));
const audienceTamper = signedRequest();
setHeader(audienceTamper, MANAGED_PROVISIONER_HEADERS.audience, "other-audience");
assert.throws(() => verify(audienceTamper), hasCode("managed_provisioner_identity_mismatch", 401));
const serviceTamper = signedRequest();
setHeader(serviceTamper, MANAGED_PROVISIONER_HEADERS.serviceId, "other-engine");
assert.throws(() => verify(serviceTamper), hasCode("managed_provisioner_identity_mismatch", 401));
const keyTamper = signedRequest();
setHeader(keyTamper, MANAGED_PROVISIONER_HEADERS.keyId, "other-key-v1");
assert.throws(() => verify(keyTamper), hasCode("managed_provisioner_identity_mismatch", 401));
const forbiddenBearer = signedRequest();
forbiddenBearer.headers.authorization = "Bearer value-that-must-not-appear-in-errors";
forbiddenBearer.rawHeaders.push("Authorization", forbiddenBearer.headers.authorization);
assert.throws(
() => verify(forbiddenBearer),
(error) => hasCode("managed_provisioner_authorization_header_forbidden", 401)(error)
&& !error.message.includes("value-that-must-not-appear-in-errors"),
);
const stale = signedRequest({ timestamp: new Date(nowMs - 60_000).toISOString() });
assert.throws(() => verify(stale), hasCode("managed_provisioner_timestamp_outside_window", 401));
const future = signedRequest({ timestamp: new Date(nowMs + 60_000).toISOString() });
assert.throws(() => verify(future), hasCode("managed_provisioner_timestamp_outside_window", 401));
const invalidTimestamp = signedRequest();
setHeader(invalidTimestamp, MANAGED_PROVISIONER_HEADERS.timestamp, "2026-07-16T12:00:00Z");
assert.throws(() => verify(invalidTimestamp), hasCode("managed_provisioner_timestamp_invalid", 401));
const duplicateHeader = signedRequest();
duplicateHeader.rawHeaders.push(MANAGED_PROVISIONER_HEADERS.nonce, header(duplicateHeader, MANAGED_PROVISIONER_HEADERS.nonce));
assert.throws(() => verify(duplicateHeader), hasCode("managed_provisioner_header_invalid", 401));
const uncaptured = signedRequest();
uncaptured.rawBodyCaptured = false;
uncaptured.headers["content-length"] = String(uncaptured.rawBody.length);
assert.throws(() => verify(uncaptured), hasCode("managed_provisioner_raw_body_unavailable", 401));
const badSignature = signedRequest();
setHeader(badSignature, MANAGED_PROVISIONER_HEADERS.signature, randomBytes(64).toString("base64url"));
assert.throws(() => verify(badSignature), hasCode("managed_provisioner_signature_invalid", 401));
const paddedSignature = signedRequest();
setHeader(
paddedSignature,
MANAGED_PROVISIONER_HEADERS.signature,
`${header(paddedSignature, MANAGED_PROVISIONER_HEADERS.signature)}=`,
);
assert.throws(() => verify(paddedSignature), hasCode("managed_provisioner_signature_invalid", 401));
const uppercaseHash = signedRequest();
setHeader(
uppercaseHash,
MANAGED_PROVISIONER_HEADERS.bodySha256,
header(uppercaseHash, MANAGED_PROVISIONER_HEADERS.bodySha256).toUpperCase(),
);
assert.throws(() => verify(uppercaseHash), hasCode("managed_provisioner_body_hash_invalid", 401));
const cache = new ManagedProvisionerReplayCache({ maxEntries: 1 });
cache.consume("first", nowMs + 1000, nowMs);
assert.throws(() => cache.consume("second", nowMs + 1000, nowMs), hasCode("managed_provisioner_replay_cache_exhausted", 503));
assert.throws(() => cache.consume("first", nowMs + 1000, nowMs), hasCode("managed_provisioner_request_replayed", 409));
cache.consume("second", nowMs + 2000, nowMs + 1000);
const directory = await mkdtemp(join(tmpdir(), "nodedc-edp-managed-auth-"));
try {
const publicKeyPath = join(directory, "engine-public-key.pem");
const rsaPublicKeyPath = join(directory, "rsa-public-key.pem");
const privateKeyPath = join(directory, "engine-private-key.pem");
const symlinkPath = join(directory, "engine-public-key-link.pem");
await writeFile(publicKeyPath, publicKey.export({ type: "spki", format: "pem" }), { mode: 0o600 });
assert.equal(loadManagedProvisionerPublicKeyFile(publicKeyPath).asymmetricKeyType, "ed25519");
assert.throws(
() => loadManagedProvisionerPublicKeyFile("relative-public-key.pem"),
/external_data_plane_managed_provisioner_public_key_file_must_be_absolute/,
);
assert.throws(
() => loadManagedProvisionerPublicKeyFile(join(directory, "missing.pem")),
/external_data_plane_managed_provisioner_public_key_file_unreadable/,
);
await symlink(publicKeyPath, symlinkPath);
assert.throws(
() => loadManagedProvisionerPublicKeyFile(symlinkPath),
/external_data_plane_managed_provisioner_public_key_file_must_be_regular/,
);
await chmod(publicKeyPath, 0o622);
assert.throws(
() => loadManagedProvisionerPublicKeyFile(publicKeyPath),
/external_data_plane_managed_provisioner_public_key_file_permissions_invalid/,
);
await chmod(publicKeyPath, 0o600);
const { publicKey: rsaPublicKey } = generateKeyPairSync("rsa", { modulusLength: 2048 });
await writeFile(rsaPublicKeyPath, rsaPublicKey.export({ type: "spki", format: "pem" }), { mode: 0o600 });
assert.throws(
() => loadManagedProvisionerPublicKeyFile(rsaPublicKeyPath),
/external_data_plane_managed_provisioner_public_key_algorithm_invalid/,
);
await writeFile(privateKeyPath, privateKey.export({ type: "pkcs8", format: "pem" }), { mode: 0o600 });
assert.throws(
() => loadManagedProvisionerPublicKeyFile(privateKeyPath),
/external_data_plane_managed_provisioner_public_key_file_format_invalid/,
);
} finally {
await rm(directory, { recursive: true, force: true });
}
console.log("external-data-plane managed provisioner auth: ok");
function signedRequest({
method = "PUT",
path = "/internal/data-plane/v1/writer-bindings/by-key/engine.test.positions",
rawBody = Buffer.from('{"generation":1}', "utf8"),
timestamp = new Date(nowMs).toISOString(),
nonce = nextNonce(),
} = {}) {
const bodySha256 = sha256RawBody(rawBody);
const signature = sign(null, Buffer.from(managedProvisionerSigningPayload({
audience,
serviceId,
keyId,
method,
path,
timestamp,
nonce,
bodySha256,
}), "utf8"), privateKey).toString("base64url");
const headers = {
[MANAGED_PROVISIONER_HEADERS.serviceId]: serviceId,
[MANAGED_PROVISIONER_HEADERS.keyId]: keyId,
[MANAGED_PROVISIONER_HEADERS.audience]: audience,
[MANAGED_PROVISIONER_HEADERS.timestamp]: timestamp,
[MANAGED_PROVISIONER_HEADERS.nonce]: nonce,
[MANAGED_PROVISIONER_HEADERS.bodySha256]: bodySha256,
[MANAGED_PROVISIONER_HEADERS.signature]: signature,
"content-length": String(rawBody.length),
};
return {
method,
originalUrl: path,
headers,
rawHeaders: Object.entries(headers).flat(),
rawBody,
rawBodyCaptured: true,
};
}
function nextNonce() {
nonceSequence += 1;
return Buffer.from(`managed-auth-test-${String(nonceSequence).padStart(6, "0")}`).toString("base64url");
}
function header(req, name) {
return req.headers[name];
}
function setHeader(req, name, value) {
req.headers[name] = value;
const index = req.rawHeaders.findIndex((candidate) => String(candidate).toLowerCase() === name);
req.rawHeaders[index + 1] = value;
}
function hasCode(code, status) {
return (error) => error?.code === code && error?.status === status;
}
@@ -1,12 +1,14 @@
import assert from "node:assert/strict";
import { validateIntakeBatch } from "../../../packages/external-provider-contract/src/index.mjs";
import { validateIntakeBatch } from "../../../packages/external-provider-contract/src/data-plane.mjs";
import {
createWriterToken,
hashWriterToken,
materializeDataProductPublish,
materializeWriterBoundBatch,
normalizeManagedWriterBindingRequest,
normalizeWriterBindingRequest,
safeWriterBinding,
writerBindingRequestHash,
} from "../src/writer-binding.mjs";
const now = new Date("2026-07-15T12:00:00.000Z");
@@ -34,6 +36,39 @@ assert.match(token, /^ndc_edpwb_[A-Za-z0-9_-]{40,}$/);
assert.equal(hashWriterToken(token), hashWriterToken(token));
assert.notEqual(hashWriterToken(token), hashWriterToken(`${token}x`));
const managedPolicy = normalizeManagedWriterBindingRequest({
...request,
allowedDataProductIds: ["fleet.positions.current.v1", "asset.status.current.v1"],
generation: 1,
capabilityDigest: hashWriterToken(token),
}, { now, maxTtlDays: 90 });
assert.deepEqual(managedPolicy.allowedDataProductIds, [
"asset.status.current.v1",
"fleet.positions.current.v1",
]);
assert.equal(managedPolicy.generation, 1);
assert.equal(managedPolicy.capabilityDigest, hashWriterToken(token));
assert.equal(writerBindingRequestHash(managedPolicy), writerBindingRequestHash({
...managedPolicy,
allowedDataProductIds: [...managedPolicy.allowedDataProductIds].reverse(),
}));
assert.throws(() => normalizeManagedWriterBindingRequest({
...request,
generation: 0,
capabilityDigest: hashWriterToken(token),
}, { now, maxTtlDays: 90 }), /managed_writer_binding_generation_invalid/);
assert.throws(() => normalizeManagedWriterBindingRequest({
...request,
generation: 1,
capabilityDigest: "not-a-digest",
}, { now, maxTtlDays: 90 }), /managed_writer_binding_capability_digest_invalid/);
assert.throws(() => normalizeManagedWriterBindingRequest({
...request,
generation: 1,
capabilityDigest: hashWriterToken(token),
token,
}, { now, maxTtlDays: 90 }), /managed_writer_binding_secret_material_forbidden/);
const unscopedBatch = {
schemaVersion: "nodedc.external-provider-contract/v1",
source: { providerId: "example-provider" },