UNPKG

@cerbos/files

Version:
181 lines 5.37 kB
"use strict"; /** * Load Cerbos policies from YAML or JSON files. * * @packageDocumentation */ Object.defineProperty(exports, "__esModule", { value: true }); exports.parsePolicy = parsePolicy; exports.readPolicy = readPolicy; exports.serializePolicy = serializePolicy; exports.writePolicy = writePolicy; exports.readSchema = readSchema; exports.readDirectory = readDirectory; const promises_1 = require("fs/promises"); const path_1 = require("path"); const yaml_1 = require("yaml"); const core_1 = require("@cerbos/core"); const policy_1 = require("./protobuf/cerbos/policy/v1/policy"); /** * Parse a policy from a YAML- or JSON-encoded string. * * @param contents - the YAML- or JSON-encoded policy definition. * * @public */ function parsePolicy(contents) { return (0, core_1._policyFromProtobuf)(policy_1.Policy.fromJSON((0, yaml_1.parse)(contents))); } /** * Read a policy from a YAML or JSON file. * * @param path - the path to the policy file. * * @public */ async function readPolicy(path) { return parsePolicy(await (0, promises_1.readFile)(path, { encoding: "utf8" })); } /** * Serialize a policy to a JSON-encoded string. * * @param policy - the policy definition. * * @public */ function serializePolicy(policy) { return `${JSON.stringify(policy_1.Policy.toJSON((0, core_1._policyToProtobuf)(policy)), null, 2)}\n`; } /** * Write a policy to a JSON file. * * @param path - the path to the policy file. * @param policy - the policy definition. * * @public */ async function writePolicy(path, policy) { await (0, promises_1.writeFile)(path, serializePolicy(policy), { encoding: "utf8" }); } /** * Read a schema from a JSON file. * * @param path - the path to the schema file. * @param options - additional settings. * * @public */ async function readSchema(path, options = {}) { return { id: options.id ?? schemaIdFromPath(path), definition: await (0, promises_1.readFile)(path, { encoding: "utf8" }), }; } function schemaIdFromPath(path) { const absolutePath = (0, path_1.resolve)(path); const segments = absolutePath.split(path_1.sep); while (segments.length > 1) { const segment = segments.shift(); if (segment === "_schemas") { return segments.join("/"); } } return (0, path_1.basename)(absolutePath); } const fileHandlers = { policies: { extensions: new Set([".json", ".yaml", ".yml"]), add(path, { policies }) { policies.push(readPolicy(path)); }, }, schemas: { extensions: new Set([".json"]), add(path, { schemas }) { schemas.push(readSchema(path)); }, }, }; /** * Read the policy and schema files in a directory and its subdirectories. * * @param path - the path to the directory. * * @remarks * This function looks for policies and schemas stored in the * {@link https://docs.cerbos.dev/cerbos/latest/policies/best_practices#_policy_repository_layout | standard Cerbos directory layout}. * * @public */ async function readDirectory(path) { const pending = { policies: [], schemas: [], }; await walk(path, ".", "policies", pending); return { policies: (await Promise.all(pending.policies)).sort(comparePolicies), schemas: (await Promise.all(pending.schemas)).sort(compareSchemas), }; } async function walk(root, subdir, type, pending) { const subdirs = []; for await (const entry of await (0, promises_1.opendir)((0, path_1.join)(root, subdir))) { if (entry.name.startsWith(".")) { continue; } const path = (0, path_1.join)(subdir, entry.name); if (entry.isDirectory()) { if (path === "_schemas") { subdirs.push(walk(root, path, "schemas", pending)); } else if (!(type === "policies" && entry.name === "testdata")) { subdirs.push(walk(root, path, type, pending)); } } else { const handler = fileHandlers[type]; if (handler.extensions.has((0, path_1.extname)(entry.name))) { handler.add((0, path_1.join)(root, path), pending); } } } await Promise.all(subdirs); } function comparePolicies(a, b) { return compareStrings(policySortKey(a), policySortKey(b)); } function policySortKey(policy) { let segments; if ((0, core_1.policyIsExportVariables)(policy)) { segments = [0, policy.exportVariables.name]; } else if ((0, core_1.policyIsDerivedRoles)(policy)) { segments = [1, policy.derivedRoles.name]; } else if ((0, core_1.policyIsResourcePolicy)(policy)) { const { resource, version, scope } = policy.resourcePolicy; segments = [2, resource, version, scope ?? ""]; } else if ((0, core_1.policyIsPrincipalPolicy)(policy)) { const { principal, version, scope } = policy.principalPolicy; segments = [3, principal, version, scope ?? ""]; } else { throw new Error("Unexpected policy type"); } return segments.join("\0"); } function compareSchemas(a, b) { return compareStrings(a.id, b.id); } function compareStrings(a, b) { if (a < b) { return -1; } if (a > b) { return 1; } return 0; } //# sourceMappingURL=index.js.map