feat(device-core): add restricted identity references
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
import {
|
||||
normalizeNdcCredentialReference,
|
||||
} from "../../../../packages/external-provider-contract/src/credential-reference.mjs";
|
||||
|
||||
export const DEVICE_SENSITIVE_REFERENCE_COMMAND_KINDS = Object.freeze([
|
||||
"device_credential_binding.upsert",
|
||||
"device_credential_binding.revoke",
|
||||
]);
|
||||
|
||||
const commandKindSet = new Set(DEVICE_SENSITIVE_REFERENCE_COMMAND_KINDS);
|
||||
const purposePattern = /^[a-z][a-z0-9._-]{1,63}$/;
|
||||
const resolutionPattern = /^[a-z][a-z0-9._-]{1,63}$/;
|
||||
|
||||
export function isSensitiveReferenceManagementCommand(kind) {
|
||||
return commandKindSet.has(kind);
|
||||
}
|
||||
|
||||
export function normalizeSensitiveReferenceManagementCommand(kind, input) {
|
||||
if (!commandKindSet.has(kind)) {
|
||||
throw new TypeError("device_sensitive_reference_command_kind_invalid");
|
||||
}
|
||||
assertPlainObject(input);
|
||||
|
||||
if (kind === "device_credential_binding.upsert") {
|
||||
assertAllowedKeys(input, [
|
||||
"projectRef",
|
||||
"deviceRef",
|
||||
"purpose",
|
||||
"credentialRef",
|
||||
]);
|
||||
return Object.freeze({
|
||||
projectId: normalizeEntityRef(input.projectRef, "project"),
|
||||
deviceId: normalizeEntityRef(input.deviceRef, "device"),
|
||||
purpose: normalizePattern(
|
||||
input.purpose,
|
||||
purposePattern,
|
||||
"device_credential_purpose_invalid",
|
||||
),
|
||||
credentialRef: normalizeNdcCredentialReference(input.credentialRef),
|
||||
});
|
||||
}
|
||||
|
||||
assertAllowedKeys(input, [
|
||||
"projectRef",
|
||||
"deviceRef",
|
||||
"purpose",
|
||||
"resolutionCode",
|
||||
]);
|
||||
return Object.freeze({
|
||||
projectId: normalizeEntityRef(input.projectRef, "project"),
|
||||
deviceId: normalizeEntityRef(input.deviceRef, "device"),
|
||||
purpose: normalizePattern(
|
||||
input.purpose,
|
||||
purposePattern,
|
||||
"device_credential_purpose_invalid",
|
||||
),
|
||||
resolutionCode: normalizePattern(
|
||||
input.resolutionCode,
|
||||
resolutionPattern,
|
||||
"device_credential_resolution_code_invalid",
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeEntityRef(value, prefix) {
|
||||
if (typeof value !== "string") {
|
||||
throw new TypeError(`device_${prefix}_ref_invalid`);
|
||||
}
|
||||
const match = value.match(new RegExp(
|
||||
`^${prefix}:([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$`,
|
||||
"i",
|
||||
));
|
||||
if (!match) throw new TypeError(`device_${prefix}_ref_invalid`);
|
||||
return match[1].toLowerCase();
|
||||
}
|
||||
|
||||
function normalizePattern(value, pattern, code) {
|
||||
if (typeof value !== "string" || !pattern.test(value)) {
|
||||
throw new TypeError(code);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function assertPlainObject(value) {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
throw new TypeError("device_sensitive_reference_command_invalid");
|
||||
}
|
||||
}
|
||||
|
||||
function assertAllowedKeys(input, allowed) {
|
||||
const allowedSet = new Set(allowed);
|
||||
for (const key of Object.keys(input)) {
|
||||
if (!allowedSet.has(key)) {
|
||||
throw new TypeError(`device_management_command_field_unexpected:${key}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user