feat(device-core): add restricted identity references
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
export const NDC_CREDENTIAL_REFERENCE_OWNER = "ndc_l2_credentials";
|
||||
|
||||
const CREDENTIAL_REFERENCE_PATTERN =
|
||||
/^ndc-credref:[A-Za-z0-9][A-Za-z0-9._:-]{7,240}$/;
|
||||
|
||||
export function normalizeNdcCredentialReference(input) {
|
||||
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
||||
throw new TypeError("ndc_credential_reference_invalid");
|
||||
}
|
||||
for (const key of Object.keys(input)) {
|
||||
if (!new Set(["owner", "reference"]).has(key)) {
|
||||
throw new TypeError(`ndc_credential_reference_field_unexpected:${key}`);
|
||||
}
|
||||
}
|
||||
if (input.owner !== NDC_CREDENTIAL_REFERENCE_OWNER) {
|
||||
throw new TypeError("ndc_credential_reference_owner_invalid");
|
||||
}
|
||||
if (!isNdcCredentialReferenceValue(input.reference)) {
|
||||
throw new TypeError("ndc_credential_reference_value_invalid");
|
||||
}
|
||||
return Object.freeze({
|
||||
owner: NDC_CREDENTIAL_REFERENCE_OWNER,
|
||||
reference: input.reference,
|
||||
});
|
||||
}
|
||||
|
||||
export function isNdcCredentialReferenceValue(value) {
|
||||
return typeof value === "string" && CREDENTIAL_REFERENCE_PATTERN.test(value);
|
||||
}
|
||||
@@ -1,6 +1,12 @@
|
||||
import { EXTERNAL_PROVIDER_CONTRACT_VERSION } from "./contract-version.mjs";
|
||||
import { isNdcCredentialReferenceValue } from "./credential-reference.mjs";
|
||||
|
||||
export { EXTERNAL_PROVIDER_CONTRACT_VERSION } from "./contract-version.mjs";
|
||||
export {
|
||||
NDC_CREDENTIAL_REFERENCE_OWNER,
|
||||
isNdcCredentialReferenceValue,
|
||||
normalizeNdcCredentialReference,
|
||||
} from "./credential-reference.mjs";
|
||||
export { validateIntakeBatch } from "./intake-batch.mjs";
|
||||
export {
|
||||
DATA_PRODUCT_GEOMETRY_TYPES,
|
||||
@@ -199,6 +205,12 @@ export function validateConnectionProfile(value) {
|
||||
if (value?.credentialRef?.owner && value.credentialRef.owner !== "ndc_l2_credentials") {
|
||||
errors.push("credentialRef.owner_must_be_ndc_l2_credentials");
|
||||
}
|
||||
if (
|
||||
typeof value?.credentialRef?.reference === "string"
|
||||
&& !isNdcCredentialReferenceValue(value.credentialRef.reference)
|
||||
) {
|
||||
errors.push("credentialRef.reference_must_be_canonical_ndc_ref");
|
||||
}
|
||||
if (containsSecretLikeMaterial(value)) errors.push("profile_must_not_contain_secret_material");
|
||||
if (value?.scope !== undefined) {
|
||||
if (!isPlainObject(value.scope)) {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { createHash } from "node:crypto";
|
||||
|
||||
import { isNdcCredentialReferenceValue } from "./credential-reference.mjs";
|
||||
import { validateProviderPackage } from "./provider-package.mjs";
|
||||
import {
|
||||
compileTelemetryFieldProjection,
|
||||
@@ -20,7 +22,6 @@ export const L2_EXECUTION_PLAN_SUPPORTED_COMPILER_VERSIONS = Object.freeze([
|
||||
|
||||
const HASH = /^(?:sha256:)?[a-f0-9]{64}$/;
|
||||
const IDENTIFIER = /^[a-z][a-z0-9._:-]{2,127}$/;
|
||||
const CREDENTIAL_REF = /^ndc-credref:[A-Za-z0-9._:-]{8,255}$/;
|
||||
const COMPILE_OPTION_KEYS = new Set(["telemetryFieldRegistry"]);
|
||||
const RECEIPT_OPTION_KEYS = new Set(["graphRevision", "graphDigest", "materializedStepIds"]);
|
||||
const STEP_RUNTIME_KINDS = Object.freeze({
|
||||
@@ -478,7 +479,7 @@ function validateConnectionInstance(providerPackage, value) {
|
||||
if (typeof value.connectionId !== "string" || !IDENTIFIER.test(value.connectionId)) {
|
||||
throw new Error("l2_execution_plan_connection_id_invalid");
|
||||
}
|
||||
if (!CREDENTIAL_REF.test(String(value.credentialRefs?.provider?.reference || ""))
|
||||
if (!isNdcCredentialReferenceValue(value.credentialRefs?.provider?.reference)
|
||||
|| value.credentialRefs?.provider?.owner !== "ndc_l2_credentials") {
|
||||
throw new Error("l2_execution_plan_provider_credential_ref_invalid");
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ export const L2_CONNECTION_INSTANCE_SCHEMA_VERSION = "nodedc.l2-connection-insta
|
||||
export const SEMANTIC_MAPPING_SCHEMA_VERSION = "nodedc.semantic-mapping/v1";
|
||||
|
||||
import { SECRET_LIKE_VALUE as SECRET_VALUE } from "./sensitive-field-policy.mjs";
|
||||
import { isNdcCredentialReferenceValue } from "./credential-reference.mjs";
|
||||
import { isBoundedTelemetryReadings } from "./telemetry-readings.mjs";
|
||||
|
||||
const IDENTIFIER = /^[a-z][a-z0-9._:-]{2,127}$/;
|
||||
@@ -1319,7 +1320,7 @@ function requiredString(value, path, errors) {
|
||||
}
|
||||
|
||||
function requiredOpaqueReference(value, path, errors) {
|
||||
if (typeof value !== "string" || !/^ndc-credref:[A-Za-z0-9][A-Za-z0-9._:-]{7,240}$/.test(value)) errors.push(`${path}_invalid`);
|
||||
if (!isNdcCredentialReferenceValue(value)) errors.push(`${path}_invalid`);
|
||||
}
|
||||
|
||||
function validateProviderBaseUrl(value, path, errors) {
|
||||
|
||||
@@ -2,7 +2,10 @@ import assert from "node:assert/strict";
|
||||
import {
|
||||
EXTERNAL_PROVIDER_CONTRACT_VERSION,
|
||||
FOUNDRY_BINDING_UPSERT_SCHEMA_VERSION,
|
||||
NDC_CREDENTIAL_REFERENCE_OWNER,
|
||||
assertValid,
|
||||
isNdcCredentialReferenceValue,
|
||||
normalizeNdcCredentialReference,
|
||||
validateCollectionProfile,
|
||||
validateConnectionProfile,
|
||||
validateDataProduct,
|
||||
@@ -19,6 +22,29 @@ assert.equal(validateConnectionProfile({
|
||||
...geliosPositionsCurrentExample.connection,
|
||||
credentialRef: { ...geliosPositionsCurrentExample.connection.credentialRef, owner: "engine" },
|
||||
}).errors.includes("credentialRef.owner_must_be_ndc_l2_credentials"), true);
|
||||
assert.deepEqual(normalizeNdcCredentialReference({
|
||||
owner: NDC_CREDENTIAL_REFERENCE_OWNER,
|
||||
reference: "ndc-credref:provider-example-0001",
|
||||
}), {
|
||||
owner: "ndc_l2_credentials",
|
||||
reference: "ndc-credref:provider-example-0001",
|
||||
});
|
||||
assert.equal(isNdcCredentialReferenceValue("ndc-credref:provider-example-0001"), true);
|
||||
assert.equal(isNdcCredentialReferenceValue("secret://provider-example"), false);
|
||||
assert.equal(validateConnectionProfile({
|
||||
...geliosPositionsCurrentExample.connection,
|
||||
credentialRef: {
|
||||
owner: "ndc_l2_credentials",
|
||||
reference: "provider-example-0001",
|
||||
},
|
||||
}).errors.includes("credentialRef.reference_must_be_canonical_ndc_ref"), true);
|
||||
assert.throws(
|
||||
() => normalizeNdcCredentialReference({
|
||||
owner: "device_core",
|
||||
reference: "ndc-credref:provider-example-0001",
|
||||
}),
|
||||
/ndc_credential_reference_owner_invalid/,
|
||||
);
|
||||
assert.equal(validateCollectionProfile(geliosPositionsCurrentExample.collectionProfile).ok, true);
|
||||
assert.equal(validateDataProduct(geliosPositionsCurrentExample.dataProduct).ok, true);
|
||||
assert.equal(validateFoundryBinding(geliosPositionsCurrentExample.foundryBinding).ok, true);
|
||||
|
||||
Reference in New Issue
Block a user