feat(edp): provision Foundry reader grants
This commit is contained in:
@@ -11,6 +11,13 @@ const MANAGED_REQUEST_KEYS = new Set([
|
||||
"generation",
|
||||
"capabilityDigest",
|
||||
]);
|
||||
const MANAGED_CONSUMER_REQUEST_KEYS = new Set([
|
||||
"allowedDataProductIds",
|
||||
"expiresAt",
|
||||
"generation",
|
||||
"capabilityDigest",
|
||||
]);
|
||||
const MANAGED_CONSUMER_PLAN_KEYS = new Set(["allowedDataProductIds"]);
|
||||
const SOURCE_KEYS = new Set(["tenantId", "connectionId", "providerId"]);
|
||||
const SHA256_DIGEST = /^[a-f0-9]{64}$/;
|
||||
|
||||
@@ -82,6 +89,38 @@ export function normalizeManagedReaderBindingRequest(value) {
|
||||
});
|
||||
}
|
||||
|
||||
export function normalizeManagedConsumerReaderPlanRequest(value) {
|
||||
if (!isPlainObject(value) || containsSecretLikeKey(value) || !hasOnlyKeys(value, MANAGED_CONSUMER_PLAN_KEYS)) {
|
||||
throw readerError("managed_consumer_reader_plan_request_invalid");
|
||||
}
|
||||
const allowedDataProductIds = uniqueIdentifiers(value.allowedDataProductIds);
|
||||
if (!allowedDataProductIds.length) throw readerError("managed_consumer_reader_scope_invalid");
|
||||
return Object.freeze({ allowedDataProductIds: Object.freeze([...allowedDataProductIds].sort()) });
|
||||
}
|
||||
|
||||
export function normalizeManagedConsumerReaderBindingRequest(value) {
|
||||
if (!isPlainObject(value) || containsSecretLikeKey(value) || !hasOnlyKeys(value, MANAGED_CONSUMER_REQUEST_KEYS)) {
|
||||
throw readerError("managed_consumer_reader_binding_request_invalid");
|
||||
}
|
||||
const allowedDataProductIds = uniqueIdentifiers(value.allowedDataProductIds);
|
||||
if (!allowedDataProductIds.length) throw readerError("managed_consumer_reader_scope_invalid");
|
||||
if (value.expiresAt !== null) throw readerError("managed_consumer_reader_must_be_durable");
|
||||
const generation = Number(value.generation);
|
||||
if (!Number.isSafeInteger(generation) || generation < 1 || generation > 2_147_483_647) {
|
||||
throw readerError("managed_consumer_reader_generation_invalid");
|
||||
}
|
||||
const capabilityDigest = String(value.capabilityDigest || "").toLowerCase();
|
||||
if (!SHA256_DIGEST.test(capabilityDigest)) {
|
||||
throw readerError("managed_consumer_reader_capability_digest_invalid");
|
||||
}
|
||||
return Object.freeze({
|
||||
allowedDataProductIds: Object.freeze([...allowedDataProductIds].sort()),
|
||||
expiresAt: null,
|
||||
generation,
|
||||
capabilityDigest,
|
||||
});
|
||||
}
|
||||
|
||||
export function readerBindingRequestHash(policy) {
|
||||
const canonical = JSON.stringify({
|
||||
tenantId: policy.tenantId,
|
||||
@@ -95,6 +134,15 @@ export function readerBindingRequestHash(policy) {
|
||||
return createHash("sha256").update(canonical, "utf8").digest("hex");
|
||||
}
|
||||
|
||||
export function consumerReaderBindingRequestHash(policy) {
|
||||
return createHash("sha256").update(JSON.stringify({
|
||||
allowedDataProductIds: [...policy.allowedDataProductIds].sort(),
|
||||
expiresAt: null,
|
||||
generation: policy.generation,
|
||||
capabilityDigest: policy.capabilityDigest,
|
||||
}), "utf8").digest("hex");
|
||||
}
|
||||
|
||||
export function assertReaderProduct(binding, dataProductId, now = new Date()) {
|
||||
const expired = binding?.expiresAt !== null && new Date(binding?.expiresAt) <= now;
|
||||
if (!isPlainObject(binding) || binding.active !== true || expired) {
|
||||
@@ -124,6 +172,21 @@ export function safeReaderBinding(binding) {
|
||||
};
|
||||
}
|
||||
|
||||
export function safeManagedConsumerReaderBinding(binding) {
|
||||
return {
|
||||
id: binding.id,
|
||||
bindingKey: binding.bindingKey,
|
||||
generation: Number(binding.generation),
|
||||
allowedDataProductIds: uniqueIdentifiers(binding.allowedDataProductIds),
|
||||
active: binding.active === true,
|
||||
expiresAt: binding.expiresAt === null ? null : new Date(binding.expiresAt).toISOString(),
|
||||
createdAt: binding.createdAt ? new Date(binding.createdAt).toISOString() : undefined,
|
||||
rotatedAt: binding.rotatedAt ? new Date(binding.rotatedAt).toISOString() : undefined,
|
||||
revokedAt: binding.revokedAt ? new Date(binding.revokedAt).toISOString() : undefined,
|
||||
sourceScope: "resolved-server-side",
|
||||
};
|
||||
}
|
||||
|
||||
function readerError(code, status = 400) {
|
||||
return Object.assign(new Error(code), { status, code });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user