@nmnmcc/ability 2.0.5 → 2.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Condition.d.ts +4 -4
- package/dist/Condition.d.ts.map +1 -1
- package/dist/Condition.js.map +1 -1
- package/dist/Domain.d.ts +1 -1
- package/dist/Policy.d.ts +2 -2
- package/dist/Policy.d.ts.map +1 -1
- package/dist/Policy.js.map +1 -1
- package/dist/internal/algebra.js +28 -6
- package/dist/internal/algebra.js.map +1 -1
- package/dist/internal/authorizer.d.ts.map +1 -1
- package/dist/internal/authorizer.js +37 -14
- package/dist/internal/authorizer.js.map +1 -1
- package/dist/internal/bind.js +7 -8
- package/dist/internal/bind.js.map +1 -1
- package/dist/internal/catalog.d.ts +6 -0
- package/dist/internal/catalog.d.ts.map +1 -1
- package/dist/internal/catalog.js +12 -9
- package/dist/internal/catalog.js.map +1 -1
- package/dist/internal/decide.js +1 -1
- package/dist/internal/domain.js +1 -1
- package/dist/internal/json.js +2 -10
- package/dist/internal/json.js.map +1 -1
- package/dist/internal/nodes.d.ts +4 -1
- package/dist/internal/nodes.d.ts.map +1 -1
- package/dist/internal/nodes.js +17 -7
- package/dist/internal/nodes.js.map +1 -1
- package/dist/internal/patterns.js +1 -1
- package/dist/internal/patterns.js.map +1 -1
- package/dist/internal/policy-json.js +2 -8
- package/dist/internal/policy-json.js.map +1 -1
- package/dist/internal/policy-nodes.d.ts +4 -1
- package/dist/internal/policy-nodes.d.ts.map +1 -1
- package/dist/internal/policy-nodes.js +13 -4
- package/dist/internal/policy-nodes.js.map +1 -1
- package/dist/internal/search.js +4 -4
- package/dist/internal/search.js.map +1 -1
- package/package.json +1 -1
- package/src/Condition.ts +5 -2
- package/src/Policy.ts +1 -2
- package/src/internal/algebra.ts +44 -8
- package/src/internal/authorizer.ts +56 -14
- package/src/internal/bind.ts +18 -14
- package/src/internal/catalog.ts +40 -27
- package/src/internal/json.ts +2 -11
- package/src/internal/nodes.ts +21 -11
- package/src/internal/patterns.ts +7 -3
- package/src/internal/policy-json.ts +2 -9
- package/src/internal/policy-nodes.ts +14 -6
- package/src/internal/search.ts +13 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"algebra.js","names":["Nodes","Nodes.And","Nodes.Or","Nodes.Compare","Nodes.Not","Nodes.isScalar","Nodes.runOf","Nodes.literal","Nodes.In","Nodes.Predicate"],"sources":["../../src/internal/algebra.ts"],"sourcesContent":["/**\n * The work on condition nodes: smart constructors, simplification, the step\n * that puts the principal in, tests on a value, and folds.\n *\n * The constructors of the nodes keep each condition small and low, so each\n * walk here is a plain walk with no memo. A shared child is walked again for\n * each place, and the limits of the nodes keep that work small.\n */\n// A predicate gives an Effect with an error type and a service type that this\n// code does not know, so they are `unknown` here.\n// @effect-diagnostics anyUnknownInErrorContext:off\nimport * as Arr from \"effect/Array\"\nimport type * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Option from \"effect/Option\"\nimport type * as Resource from \"../Resource.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport { readPath } from \"./read.ts\"\n\ntype Node = Nodes.Node\n\nconst { always, never } = Nodes\n\n// Keys of structure\n\n/** The text of an operand. The type is in it, so `\"1\"` is not `1`. */\nconst operandText = (operand: Nodes.Operand): ReadonlyArray<string> =>\n\toperand._tag === \"Literal\"\n\t\t? [typeof operand.value, String(operand.value)]\n\t\t: [\"principal\", operand.path]\n\n/** The parts of the text of a condition with no predicate. */\nconst shapeOf = (node: Node): unknown => {\n\tswitch (node._tag) {\n\t\tcase \"True\":\n\t\tcase \"False\":\n\t\t\treturn [node._tag]\n\t\tcase \"Compare\":\n\t\t\treturn [node._tag, node.op, node.field, operandText(node.value)]\n\t\tcase \"In\":\n\t\t\treturn [node._tag, node.field, Arr.map(node.values, operandText)]\n\t\tcase \"Exists\":\n\t\t\treturn [node._tag, node.field]\n\t\tcase \"Under\":\n\t\t\treturn [node._tag, Arr.map(node.prefix, operandText)]\n\t\tcase \"And\":\n\t\tcase \"Or\":\n\t\t\treturn [node._tag, Arr.map(node.conditions, shapeOf)]\n\t\tcase \"Not\":\n\t\t\treturn [node._tag, shapeOf(node.condition)]\n\t\tcase \"Predicate\":\n\t\t\t// A smart constructor and a search do not ask for the text of a\n\t\t\t// predicate: it is equal only to itself.\n\t\t\tthrow new Error(\"A predicate has no text of structure\")\n\t}\n}\n\n/**\n * The text of the structure of a condition with no predicate. Two conditions\n * with one text give the same answer for every value. It reads all of the\n * condition, so a search counts its size in the budget.\n */\nexport const structureText = (node: Node): string =>\n\tJSON.stringify(shapeOf(node))\n\n/**\n * The most nodes of a condition that a smart constructor reads for its key.\n * A larger child is its own key, so the work of a constructor stays small.\n */\nconst keyNodes = 64\n\n/**\n * The key of a child in a smart constructor: the text of its structure, or\n * the node itself when it is large. The keys go into a native `Set`, not a\n * `HashSet` of Effect. The hash of a string in Effect has no random seed, so\n * a policy could hold very many strings with one hash, and each test of the\n * set would read all of them. A native `Set` finds a node by the node itself,\n * and it hashes a string with the random seed of the engine.\n */\nconst keyOf = (node: Node): string | Node =>\n\tnode.size > keyNodes ? node : structureText(node)\n\n/**\n * Keeps the first node of each structure, in order. A large node is a copy\n * only of itself.\n */\nconst dedupe = (nodes: ReadonlyArray<Node>): ReadonlyArray<Node> => {\n\t// The set belongs to this call.\n\tconst seen = new Set<string | Node>()\n\treturn Arr.filter(nodes, (node) => {\n\t\tconst key = keyOf(node)\n\t\tif (seen.has(key)) {\n\t\t\treturn false\n\t\t}\n\t\tseen.add(key)\n\t\treturn true\n\t})\n}\n\n/**\n * Removes the children that the other children make useless. In an `Or`,\n * `a OR (a AND b)` is `a`. In an `And`, `a AND (a OR b)` is `a`.\n */\nconst absorb = (\n\tinner: \"And\" | \"Or\",\n\tnodes: ReadonlyArray<Node>,\n): ReadonlyArray<Node> => {\n\t// A native `Set`, as in `dedupe`.\n\tconst all = new Set(Arr.map(nodes, keyOf))\n\treturn Arr.filter(\n\t\tnodes,\n\t\t(node) =>\n\t\t\tnode._tag !== inner ||\n\t\t\t!Arr.some(node.conditions, (part) => all.has(keyOf(part))),\n\t)\n}\n\n// Smart constructors\n\n/**\n * Gives the result of an `And` with a `False` child, or of an `Or` with a\n * `True` child. The children are tested in order, so a predicate before the\n * constant still runs, and it may fail. When there is such a predicate, the\n * children before the constant stay, so the failure is not lost.\n */\nconst shortCircuit = (\n\ttag: \"And\" | \"Or\",\n\tchildren: ReadonlyArray<Node>,\n\tindex: number,\n): Node => {\n\tconst constant = tag === \"And\" ? never : always\n\tconst neutral = tag === \"And\" ? \"True\" : \"False\"\n\tconst before = Arr.filter(\n\t\tArr.take(children, index),\n\t\t(child) => child._tag !== neutral,\n\t)\n\tif (!Arr.some(before, (child) => child.opaque)) {\n\t\treturn constant\n\t}\n\treturn tag === \"And\"\n\t\t? new Nodes.And([...before, constant])\n\t\t: new Nodes.Or([...before, constant])\n}\n\n/**\n * Makes an `And` or an `Or` of simple children, and makes it simple. It looks\n * one level down only:\n * - it puts the children of a child of the same kind into the node;\n * - it stops at a `False` in an `And`, or at a `True` in an `Or`;\n * - it removes `True` from an `And`, `False` from an `Or`, copies, and the\n * children that other children make useless.\n *\n * A predicate may fail, and it may give another answer when it runs again. So\n * when a child has a predicate, the children stay as they are, and each one\n * runs in its place. A child with a predicate is not put into the node either.\n */\nconst junction = (tag: \"And\" | \"Or\", children: ReadonlyArray<Node>): Node => {\n\tconst constant = tag === \"And\" ? \"False\" : \"True\"\n\tconst neutral = tag === \"And\" ? \"True\" : \"False\"\n\tconst flat = Arr.flatMap(children, (child) =>\n\t\tchild._tag === tag && !child.opaque ? child.conditions : [child],\n\t)\n\treturn Option.match(\n\t\tArr.findFirstIndex(flat, (child) => child._tag === constant),\n\t\t{\n\t\t\tonSome: (stop) => shortCircuit(tag, flat, stop),\n\t\t\tonNone: () => {\n\t\t\t\tconst kept = Arr.filter(flat, (child) => child._tag !== neutral)\n\t\t\t\tconst unique = Arr.some(kept, (child) => child.opaque)\n\t\t\t\t\t? kept\n\t\t\t\t\t: absorb(tag === \"And\" ? \"Or\" : \"And\", dedupe(kept))\n\t\t\t\treturn unique.length === 0\n\t\t\t\t\t? tag === \"And\"\n\t\t\t\t\t\t? always\n\t\t\t\t\t\t: never\n\t\t\t\t\t: unique.length === 1\n\t\t\t\t\t\t? unique[0]!\n\t\t\t\t\t\t: tag === \"And\"\n\t\t\t\t\t\t\t? new Nodes.And(unique)\n\t\t\t\t\t\t\t: new Nodes.Or(unique)\n\t\t\t},\n\t\t},\n\t)\n}\n\n/** An `And` of simple children, made simple. */\nexport const and = (children: ReadonlyArray<Node>): Node =>\n\tjunction(\"And\", children)\n\n/** An `Or` of simple children, made simple. */\nexport const or = (children: ReadonlyArray<Node>): Node =>\n\tjunction(\"Or\", children)\n\n/** A `Not` of a simple child, made simple. */\nexport const not = (child: Node): Node => {\n\tswitch (child._tag) {\n\t\tcase \"True\":\n\t\t\treturn never\n\t\tcase \"False\":\n\t\t\treturn always\n\t\tcase \"Not\":\n\t\t\treturn child.condition\n\t\tcase \"Compare\":\n\t\t\t// A principal field with no value makes `eq` and `ne` both false, so\n\t\t\t// only a fixed value lets `not eq` become `ne`.\n\t\t\treturn child.value._tag === \"Literal\" &&\n\t\t\t\t(child.op === \"eq\" || child.op === \"ne\")\n\t\t\t\t? new Nodes.Compare({\n\t\t\t\t\t\top: child.op === \"eq\" ? \"ne\" : \"eq\",\n\t\t\t\t\t\tfield: child.field,\n\t\t\t\t\t\tvalue: child.value,\n\t\t\t\t\t})\n\t\t\t\t: new Nodes.Not(child)\n\t\tdefault:\n\t\t\treturn new Nodes.Not(child)\n\t}\n}\n\n/** Makes a condition simple, from its leaves up. */\nexport const simplify = (node: Node): Node => {\n\tswitch (node._tag) {\n\t\tcase \"And\":\n\t\tcase \"Or\":\n\t\t\treturn junction(node._tag, Arr.map(node.conditions, simplify))\n\t\tcase \"Not\":\n\t\t\treturn node.condition.pipe(simplify, not)\n\t\tcase \"In\":\n\t\t\treturn node.values.length === 0 ? never : node\n\t\tdefault:\n\t\t\treturn node\n\t}\n}\n\n// Tests on a value\n\n/** The data that a condition needs for a test. */\nexport interface Input {\n\treadonly value: object\n\treadonly principal?: object | undefined\n\treadonly path?: ReadonlyArray<Resource.PathSegment> | undefined\n}\n\n/**\n * Gets a field of a value by a path with dots. It reads only plain data: see\n * `readPart`. A missing field, or a field under a value that is not an object,\n * gives `null`.\n */\nconst getField = (value: unknown, path: string): unknown =>\n\treadPath(value, path.split(\".\"))\n\n/**\n * Gives the value of an operand. A principal field that is `null`, missing, or\n * not a scalar gives `undefined`. No field of a resource is `undefined`, so a\n * test with it is never true. This keeps the check closed: a principal with no\n * id never matches a resource with no owner.\n */\nconst resolve = (operand: Nodes.Operand, principal: unknown): unknown => {\n\tif (operand._tag === \"Literal\") {\n\t\treturn operand.value\n\t}\n\tconst value = getField(principal, operand.path)\n\treturn value !== null && Nodes.isScalar(value) ? value : undefined\n}\n\nexport const testCompare = (\n\top: Nodes.CompareOperator,\n\tleft: unknown,\n\tright: unknown,\n): boolean => {\n\tif (right === undefined) {\n\t\treturn false\n\t}\n\tswitch (op) {\n\t\tcase \"eq\":\n\t\t\treturn left === right\n\t\tcase \"ne\":\n\t\t\treturn left !== right\n\t\tdefault: {\n\t\t\tconst ordered =\n\t\t\t\t(typeof left === \"number\" && typeof right === \"number\") ||\n\t\t\t\t(typeof left === \"string\" && typeof right === \"string\")\n\t\t\tif (!ordered) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst a = left as number | string\n\t\t\tconst b = right as number | string\n\t\t\tswitch (op) {\n\t\t\t\tcase \"lt\":\n\t\t\t\t\treturn a < b\n\t\t\t\tcase \"lte\":\n\t\t\t\t\treturn a <= b\n\t\t\t\tcase \"gt\":\n\t\t\t\t\treturn a > b\n\t\t\t\tcase \"gte\":\n\t\t\t\t\treturn a >= b\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Gives the values of the path of a resource, or `undefined` for no path. */\nconst pathValues = (\n\tvalue: unknown,\n\tpath: ReadonlyArray<Resource.PathSegment> | undefined,\n): ReadonlyArray<unknown> | undefined =>\n\tpath?.map((segment) =>\n\t\ttypeof segment === \"string\" ? segment : getField(value, segment.field),\n\t)\n\nconst testUnder = (\n\tprefix: ReadonlyArray<Nodes.Operand>,\n\tinput: Input,\n): boolean => {\n\tconst values = pathValues(input.value, input.path)\n\tif (values === undefined || prefix.length > values.length) {\n\t\treturn false\n\t}\n\t// An operand with no value never matches, also a hole in a path that gives\n\t// `undefined` too.\n\treturn prefix.every((operand, index) => {\n\t\tconst value = resolve(operand, input.principal)\n\t\treturn value !== undefined && value === values[index]\n\t})\n}\n\n/** Tests a simple condition with no predicate on a value. */\nconst testSync = (node: Node, input: Input): boolean => {\n\tswitch (node._tag) {\n\t\tcase \"True\":\n\t\t\treturn true\n\t\tcase \"False\":\n\t\t\treturn false\n\t\tcase \"Compare\":\n\t\t\treturn testCompare(\n\t\t\t\tnode.op,\n\t\t\t\tgetField(input.value, node.field),\n\t\t\t\tresolve(node.value, input.principal),\n\t\t\t)\n\t\tcase \"In\": {\n\t\t\tconst field = getField(input.value, node.field)\n\t\t\treturn node.values.some(\n\t\t\t\t(operand) => resolve(operand, input.principal) === field,\n\t\t\t)\n\t\t}\n\t\tcase \"Exists\":\n\t\t\treturn getField(input.value, node.field) !== null\n\t\tcase \"Under\":\n\t\t\treturn testUnder(node.prefix, input)\n\t\tcase \"And\":\n\t\t\treturn Arr.every(node.conditions, (child) => testSync(child, input))\n\t\tcase \"Or\":\n\t\t\treturn Arr.some(node.conditions, (child) => testSync(child, input))\n\t\tcase \"Not\":\n\t\t\treturn !testSync(node.condition, input)\n\t\tcase \"Predicate\":\n\t\t\t// The types stop this. A predicate needs `evaluate`.\n\t\t\tthrow new Error(\n\t\t\t\t`The predicate \"${node.name}\" needs an Effect. Use Condition.evaluate.`,\n\t\t\t)\n\t}\n}\n\n/**\n * Tests a simple condition on a value. The children of `And` and `Or` run in\n * order, and they stop at the first child that gives the answer.\n */\nconst test = (\n\tnode: Node,\n\tinput: Input,\n): Effect.Effect<boolean, unknown, unknown> => {\n\tif (!node.opaque) {\n\t\treturn Effect.sync(() => testSync(node, input))\n\t}\n\tswitch (node._tag) {\n\t\tcase \"Predicate\":\n\t\t\treturn Nodes.runOf(node)(input.value, input.principal)\n\t\tcase \"Not\":\n\t\t\treturn Effect.map(test(node.condition, input), (value) => !value)\n\t\tcase \"And\":\n\t\t\treturn Effect.map(\n\t\t\t\tEffect.findFirst(node.conditions, (child) =>\n\t\t\t\t\tEffect.map(test(child, input), (value) => !value),\n\t\t\t\t),\n\t\t\t\tOption.isNone,\n\t\t\t)\n\t\tcase \"Or\":\n\t\t\treturn Effect.map(\n\t\t\t\tEffect.findFirst(node.conditions, (child) => test(child, input)),\n\t\t\t\tOption.isSome,\n\t\t\t)\n\t\tdefault:\n\t\t\treturn Effect.sync(() => testSync(node, input))\n\t}\n}\n\n/**\n * Tests a condition with no predicate on a value. The test runs on the\n * simple form of the condition with the principal in it, as a residual does.\n * So a test reads only the fields that the residual reads: a read of a field\n * that is not plain data is a defect, and a field that the answer does not\n * need, as in `x AND False`, is not read.\n */\nexport const evaluateSync = (node: Node, input: Input): boolean =>\n\ttestSync(partial(node, input), input)\n\n/** Tests a condition on a value, as {@link evaluateSync} does. */\nexport const evaluate = (\n\tnode: Node,\n\tinput: Input,\n): Effect.Effect<boolean, unknown, unknown> =>\n\tEffect.suspend(() => test(partial(node, input), input))\n\n// The principal and the path\n\n/**\n * Puts the value of the principal into an operand. It gives `undefined` for a\n * principal field with no value. See {@link resolve}.\n */\nconst bindOperand = (\n\toperand: Nodes.Operand,\n\tprincipal: unknown,\n): Nodes.Literal | undefined => {\n\tif (operand._tag === \"Literal\") {\n\t\treturn operand\n\t}\n\tconst value = resolve(operand, principal)\n\treturn value === undefined ? undefined : Nodes.literal(value as Nodes.Scalar)\n}\n\nconst expandUnder = (\n\tprefix: ReadonlyArray<Nodes.Operand>,\n\tprincipal: unknown,\n\tpath: ReadonlyArray<Resource.PathSegment> | undefined,\n): Node => {\n\tif (path === undefined || prefix.length > path.length) {\n\t\treturn never\n\t}\n\treturn and(\n\t\tArr.map(prefix, (operand, index): Node => {\n\t\t\tconst value = resolve(operand, principal)\n\t\t\tconst segment = path[index]!\n\t\t\treturn value === undefined\n\t\t\t\t? never\n\t\t\t\t: typeof segment === \"string\"\n\t\t\t\t\t? segment === value\n\t\t\t\t\t\t? always\n\t\t\t\t\t\t: never\n\t\t\t\t\t: new Nodes.Compare({\n\t\t\t\t\t\t\top: \"eq\",\n\t\t\t\t\t\t\tfield: segment.field,\n\t\t\t\t\t\t\tvalue: Nodes.literal(value as Nodes.Scalar),\n\t\t\t\t\t\t})\n\t\t}),\n\t)\n}\n\n/** The principal and the path of the resource, for {@link partial}. */\nexport interface PartialInput {\n\treadonly principal?: object | undefined\n\treadonly path?: ReadonlyArray<Resource.PathSegment> | undefined\n}\n\n/**\n * Puts the principal into a condition and expands `Under` with the path of the\n * resource. The result is simple. It has no principal fields and no `Under`\n * nodes. A predicate keeps the principal.\n */\nexport const partial = (node: Node, input: PartialInput): Node => {\n\tconst bind = (current: Node): Node => {\n\t\tswitch (current._tag) {\n\t\t\tcase \"Compare\": {\n\t\t\t\tconst value = bindOperand(current.value, input.principal)\n\t\t\t\treturn value === undefined\n\t\t\t\t\t? never\n\t\t\t\t\t: value === current.value\n\t\t\t\t\t\t? current\n\t\t\t\t\t\t: new Nodes.Compare({ op: current.op, field: current.field, value })\n\t\t\t}\n\t\t\tcase \"In\": {\n\t\t\t\tconst values = Arr.flatMap(current.values, (operand) =>\n\t\t\t\t\tOption.toArray(\n\t\t\t\t\t\tOption.fromUndefinedOr(bindOperand(operand, input.principal)),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\treturn values.length === 0\n\t\t\t\t\t? never\n\t\t\t\t\t: new Nodes.In({ field: current.field, values })\n\t\t\t}\n\t\t\tcase \"Under\":\n\t\t\t\treturn expandUnder(current.prefix, input.principal, input.path)\n\t\t\tcase \"And\":\n\t\t\t\treturn and(Arr.map(current.conditions, bind))\n\t\t\tcase \"Or\":\n\t\t\t\treturn or(Arr.map(current.conditions, bind))\n\t\t\tcase \"Not\":\n\t\t\t\treturn current.condition.pipe(bind, not)\n\t\t\tcase \"Predicate\": {\n\t\t\t\tconst run = Nodes.runOf(current)\n\t\t\t\treturn new Nodes.Predicate({\n\t\t\t\t\tname: current.name,\n\t\t\t\t\trun: (value) => run(value, input.principal),\n\t\t\t\t})\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn current\n\t\t}\n\t}\n\treturn bind(node)\n}\n\n/** Gives the services in `context` to every predicate of a condition. */\nexport const provide = (node: Node, context: Context.Context<never>): Node => {\n\tif (!node.opaque) {\n\t\treturn node\n\t}\n\tswitch (node._tag) {\n\t\tcase \"Predicate\": {\n\t\t\tconst run = Nodes.runOf(node)\n\t\t\treturn new Nodes.Predicate({\n\t\t\t\tname: node.name,\n\t\t\t\trun: (value, principal) =>\n\t\t\t\t\tEffect.provideContext(run(value, principal), context),\n\t\t\t})\n\t\t}\n\t\tcase \"And\":\n\t\t\treturn new Nodes.And(\n\t\t\t\tArr.map(node.conditions, (child) => provide(child, context)),\n\t\t\t)\n\t\tcase \"Or\":\n\t\t\treturn new Nodes.Or(\n\t\t\t\tArr.map(node.conditions, (child) => provide(child, context)),\n\t\t\t)\n\t\tcase \"Not\":\n\t\t\treturn new Nodes.Not(provide(node.condition, context))\n\t\tdefault:\n\t\t\treturn node\n\t}\n}\n\n/** The fields that a condition tests, in the order that they first come. */\nexport const fields = (node: Node): ReadonlyArray<string> => {\n\tconst all = (current: Node): ReadonlyArray<string> => {\n\t\tswitch (current._tag) {\n\t\t\tcase \"Compare\":\n\t\t\tcase \"In\":\n\t\t\tcase \"Exists\":\n\t\t\t\treturn [current.field]\n\t\t\tcase \"And\":\n\t\t\tcase \"Or\":\n\t\t\t\treturn Arr.flatMap(current.conditions, all)\n\t\t\tcase \"Not\":\n\t\t\t\treturn all(current.condition)\n\t\t\tdefault:\n\t\t\t\treturn []\n\t\t}\n\t}\n\t// A native `Set`, as in `dedupe`: a field of a condition that is not bound\n\t// to a domain may be any string.\n\treturn Array.from(new Set(all(node)))\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"algebra.js","names":["Nodes","Nodes.And","Nodes.Or","Nodes.Compare","Nodes.Not","Nodes.isScalar","Nodes.runOf","Nodes.literal","Nodes.In","Nodes.Predicate"],"sources":["../../src/internal/algebra.ts"],"sourcesContent":["/**\n * The work on condition nodes: smart constructors, simplification, the step\n * that puts the principal in, tests on a value, and folds.\n *\n * The constructors of the nodes keep each condition small and low, so each\n * walk here is a plain walk with no memo. A shared child is walked again for\n * each place, and the limits of the nodes keep that work small.\n */\n// A predicate gives an Effect with an error type and a service type that this\n// code does not know, so they are `unknown` here.\n// @effect-diagnostics anyUnknownInErrorContext:off\nimport * as Arr from \"effect/Array\"\nimport type * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Option from \"effect/Option\"\nimport type * as Resource from \"../Resource.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport { readPath } from \"./read.ts\"\n\ntype Node = Nodes.Node\n\nconst { always, never } = Nodes\n\n// Keys of structure\n\n/** The text of an operand. The type is in it, so `\"1\"` is not `1`. */\nconst operandText = (operand: Nodes.Operand): ReadonlyArray<string> =>\n\toperand._tag === \"Literal\"\n\t\t? [typeof operand.value, String(operand.value)]\n\t\t: [\"principal\", operand.path]\n\n/** The parts of the text of a condition with no predicate. */\nconst shapeOf = (node: Node): unknown => {\n\tswitch (node._tag) {\n\t\tcase \"True\":\n\t\tcase \"False\":\n\t\t\treturn [node._tag]\n\t\tcase \"Compare\":\n\t\t\treturn [node._tag, node.op, node.field, operandText(node.value)]\n\t\tcase \"In\":\n\t\t\treturn [node._tag, node.field, Arr.map(node.values, operandText)]\n\t\tcase \"Exists\":\n\t\t\treturn [node._tag, node.field]\n\t\tcase \"Under\":\n\t\t\treturn [node._tag, Arr.map(node.prefix, operandText)]\n\t\tcase \"And\":\n\t\tcase \"Or\":\n\t\t\treturn [node._tag, Arr.map(node.conditions, shapeOf)]\n\t\tcase \"Not\":\n\t\t\treturn [node._tag, shapeOf(node.condition)]\n\t\tcase \"Predicate\":\n\t\t\t// A smart constructor and a search do not ask for the text of a\n\t\t\t// predicate: it is equal only to itself.\n\t\t\tthrow new Error(\"A predicate has no text of structure\")\n\t}\n}\n\n/**\n * The text of the structure of a condition with no predicate. Two conditions\n * with one text give the same answer for every value. It reads all of the\n * condition, so a search counts its size in the budget.\n */\nconst structureText = (node: Node): string => JSON.stringify(shapeOf(node))\n\n/**\n * The work of the text of the structure of a condition: each node, and each\n * value of an `In` test or an `Under` test. A node counts as one in `size`,\n * also an `In` test with very many values. This walk reads the nodes only,\n * so it takes `size` steps.\n */\nconst weightOf = (node: Node): number => {\n\tswitch (node._tag) {\n\t\tcase \"In\":\n\t\t\treturn 1 + node.values.length\n\t\tcase \"Under\":\n\t\t\treturn 1 + node.prefix.length\n\t\tcase \"And\":\n\t\tcase \"Or\":\n\t\t\treturn Arr.reduce(\n\t\t\t\tnode.conditions,\n\t\t\t\t1,\n\t\t\t\t(total, child) => total + weightOf(child),\n\t\t\t)\n\t\tcase \"Not\":\n\t\t\treturn 1 + weightOf(node.condition)\n\t\tdefault:\n\t\t\treturn 1\n\t}\n}\n\n/**\n * The most nodes of a condition that a smart constructor reads for its key,\n * and the most children of a child that it puts into a node. A larger child\n * is its own key, and it stays one child, so the work of a constructor stays\n * small. A deep chain of `And` nodes, for one, does not copy all its leaves\n * at each level.\n */\nconst keyNodes = 64\n\n/** The most work of a key, with the values of its tests: see {@link weightOf}. */\nconst keyWeight = 256\n\n/**\n * The key of a child in a smart constructor: the text of its structure, or\n * the node itself when it is large. The keys go into a native `Set`, not a\n * `HashSet` of Effect. The hash of a string in Effect has no random seed, so\n * a policy could hold very many strings with one hash, and each test of the\n * set would read all of them. A native `Set` finds a node by the node itself,\n * and it hashes a string with the random seed of the engine.\n */\nexport const keyOf = (node: Node): string | Node =>\n\tnode.size > keyNodes || weightOf(node) > keyWeight\n\t\t? node\n\t\t: structureText(node)\n\n/**\n * Keeps the first node of each structure, in order. A large node is a copy\n * only of itself.\n */\nconst dedupe = (nodes: ReadonlyArray<Node>): ReadonlyArray<Node> => {\n\t// The set belongs to this call.\n\tconst seen = new Set<string | Node>()\n\treturn Arr.filter(nodes, (node) => {\n\t\tconst key = keyOf(node)\n\t\tif (seen.has(key)) {\n\t\t\treturn false\n\t\t}\n\t\tseen.add(key)\n\t\treturn true\n\t})\n}\n\n/**\n * Removes the children that the other children make useless. In an `Or`,\n * `a OR (a AND b)` is `a`. In an `And`, `a AND (a OR b)` is `a`.\n */\nconst absorb = (\n\tinner: \"And\" | \"Or\",\n\tnodes: ReadonlyArray<Node>,\n): ReadonlyArray<Node> => {\n\t// A native `Set`, as in `dedupe`.\n\tconst all = new Set(Arr.map(nodes, keyOf))\n\treturn Arr.filter(\n\t\tnodes,\n\t\t(node) =>\n\t\t\tnode._tag !== inner ||\n\t\t\t!Arr.some(node.conditions, (part) => all.has(keyOf(part))),\n\t)\n}\n\n// Smart constructors\n\n/**\n * Gives the result of an `And` with a `False` child, or of an `Or` with a\n * `True` child. The children are tested in order, so a predicate before the\n * constant still runs, and it may fail. When there is such a predicate, the\n * children before the constant stay, so the failure is not lost.\n */\nconst shortCircuit = (\n\ttag: \"And\" | \"Or\",\n\tchildren: ReadonlyArray<Node>,\n\tindex: number,\n): Node => {\n\tconst constant = tag === \"And\" ? never : always\n\tconst neutral = tag === \"And\" ? \"True\" : \"False\"\n\tconst before = Arr.filter(\n\t\tArr.take(children, index),\n\t\t(child) => child._tag !== neutral,\n\t)\n\tif (!Arr.some(before, (child) => child.opaque)) {\n\t\treturn constant\n\t}\n\treturn tag === \"And\"\n\t\t? new Nodes.And([...before, constant])\n\t\t: new Nodes.Or([...before, constant])\n}\n\n/**\n * Makes an `And` or an `Or` of simple children, and makes it simple. It looks\n * one level down only:\n * - it puts the children of a child of the same kind into the node, when\n * the child has at most `keyNodes` of them;\n * - it stops at a `False` in an `And`, or at a `True` in an `Or`;\n * - it removes `True` from an `And`, `False` from an `Or`, copies, and the\n * children that other children make useless.\n *\n * A predicate may fail, and it may give another answer when it runs again. So\n * when a child has a predicate, the children stay as they are, and each one\n * runs in its place. A child with a predicate is not put into the node either.\n */\nconst junction = (tag: \"And\" | \"Or\", children: ReadonlyArray<Node>): Node => {\n\tconst constant = tag === \"And\" ? \"False\" : \"True\"\n\tconst neutral = tag === \"And\" ? \"True\" : \"False\"\n\tconst flat = Arr.flatMap(children, (child) =>\n\t\tchild._tag === tag && !child.opaque && child.conditions.length <= keyNodes\n\t\t\t? child.conditions\n\t\t\t: [child],\n\t)\n\treturn Option.match(\n\t\tArr.findFirstIndex(flat, (child) => child._tag === constant),\n\t\t{\n\t\t\tonSome: (stop) => shortCircuit(tag, flat, stop),\n\t\t\tonNone: () => {\n\t\t\t\tconst kept = Arr.filter(flat, (child) => child._tag !== neutral)\n\t\t\t\tconst unique = Arr.some(kept, (child) => child.opaque)\n\t\t\t\t\t? kept\n\t\t\t\t\t: absorb(tag === \"And\" ? \"Or\" : \"And\", dedupe(kept))\n\t\t\t\treturn unique.length === 0\n\t\t\t\t\t? tag === \"And\"\n\t\t\t\t\t\t? always\n\t\t\t\t\t\t: never\n\t\t\t\t\t: unique.length === 1\n\t\t\t\t\t\t? unique[0]!\n\t\t\t\t\t\t: tag === \"And\"\n\t\t\t\t\t\t\t? new Nodes.And(unique)\n\t\t\t\t\t\t\t: new Nodes.Or(unique)\n\t\t\t},\n\t\t},\n\t)\n}\n\n/** An `And` of simple children, made simple. */\nexport const and = (children: ReadonlyArray<Node>): Node =>\n\tjunction(\"And\", children)\n\n/** An `Or` of simple children, made simple. */\nexport const or = (children: ReadonlyArray<Node>): Node =>\n\tjunction(\"Or\", children)\n\n/** A `Not` of a simple child, made simple. */\nexport const not = (child: Node): Node => {\n\tswitch (child._tag) {\n\t\tcase \"True\":\n\t\t\treturn never\n\t\tcase \"False\":\n\t\t\treturn always\n\t\tcase \"Not\":\n\t\t\treturn child.condition\n\t\tcase \"Compare\":\n\t\t\t// A principal field with no value makes `eq` and `ne` both false, so\n\t\t\t// only a fixed value lets `not eq` become `ne`.\n\t\t\treturn child.value._tag === \"Literal\" &&\n\t\t\t\t(child.op === \"eq\" || child.op === \"ne\")\n\t\t\t\t? new Nodes.Compare({\n\t\t\t\t\t\top: child.op === \"eq\" ? \"ne\" : \"eq\",\n\t\t\t\t\t\tfield: child.field,\n\t\t\t\t\t\tvalue: child.value,\n\t\t\t\t\t})\n\t\t\t\t: new Nodes.Not(child)\n\t\tdefault:\n\t\t\treturn new Nodes.Not(child)\n\t}\n}\n\n/** Makes a condition simple, from its leaves up. */\nexport const simplify = (node: Node): Node => {\n\tswitch (node._tag) {\n\t\tcase \"And\":\n\t\tcase \"Or\":\n\t\t\treturn junction(node._tag, Arr.map(node.conditions, simplify))\n\t\tcase \"Not\":\n\t\t\treturn node.condition.pipe(simplify, not)\n\t\tcase \"In\":\n\t\t\treturn node.values.length === 0 ? never : node\n\t\tdefault:\n\t\t\treturn node\n\t}\n}\n\n// Tests on a value\n\n/** The data that a condition needs for a test. */\nexport interface Input {\n\treadonly value: object\n\treadonly principal?: object | undefined\n\treadonly path?: ReadonlyArray<Resource.PathSegment> | undefined\n}\n\n/**\n * Gets a field of a value by a path with dots. It reads only plain data: see\n * `readPart`. A missing field, or a field under a value that is not an object,\n * gives `null`.\n */\nconst getField = (value: unknown, path: string): unknown =>\n\treadPath(value, path.split(\".\"))\n\n/**\n * Gives the value of an operand. A principal field that is `null`, missing, or\n * not a scalar gives `undefined`. No field of a resource is `undefined`, so a\n * test with it is never true. This keeps the check closed: a principal with no\n * id never matches a resource with no owner.\n */\nconst resolve = (operand: Nodes.Operand, principal: unknown): unknown => {\n\tif (operand._tag === \"Literal\") {\n\t\treturn operand.value\n\t}\n\tconst value = getField(principal, operand.path)\n\treturn value !== null && Nodes.isScalar(value) ? value : undefined\n}\n\nexport const testCompare = (\n\top: Nodes.CompareOperator,\n\tleft: unknown,\n\tright: unknown,\n): boolean => {\n\tif (right === undefined) {\n\t\treturn false\n\t}\n\tswitch (op) {\n\t\tcase \"eq\":\n\t\t\treturn left === right\n\t\tcase \"ne\":\n\t\t\treturn left !== right\n\t\tdefault: {\n\t\t\tconst ordered =\n\t\t\t\t(typeof left === \"number\" && typeof right === \"number\") ||\n\t\t\t\t(typeof left === \"string\" && typeof right === \"string\")\n\t\t\tif (!ordered) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst a = left as number | string\n\t\t\tconst b = right as number | string\n\t\t\tswitch (op) {\n\t\t\t\tcase \"lt\":\n\t\t\t\t\treturn a < b\n\t\t\t\tcase \"lte\":\n\t\t\t\t\treturn a <= b\n\t\t\t\tcase \"gt\":\n\t\t\t\t\treturn a > b\n\t\t\t\tcase \"gte\":\n\t\t\t\t\treturn a >= b\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Gives the values of the path of a resource, or `undefined` for no path. */\nconst pathValues = (\n\tvalue: unknown,\n\tpath: ReadonlyArray<Resource.PathSegment> | undefined,\n): ReadonlyArray<unknown> | undefined =>\n\tpath?.map((segment) =>\n\t\ttypeof segment === \"string\" ? segment : getField(value, segment.field),\n\t)\n\nconst testUnder = (\n\tprefix: ReadonlyArray<Nodes.Operand>,\n\tinput: Input,\n): boolean => {\n\tconst values = pathValues(input.value, input.path)\n\tif (values === undefined || prefix.length > values.length) {\n\t\treturn false\n\t}\n\t// An operand with no value never matches, also a hole in a path that gives\n\t// `undefined` too.\n\treturn prefix.every((operand, index) => {\n\t\tconst value = resolve(operand, input.principal)\n\t\treturn value !== undefined && value === values[index]\n\t})\n}\n\n/** Tests a simple condition with no predicate on a value. */\nconst testSync = (node: Node, input: Input): boolean => {\n\tswitch (node._tag) {\n\t\tcase \"True\":\n\t\t\treturn true\n\t\tcase \"False\":\n\t\t\treturn false\n\t\tcase \"Compare\":\n\t\t\treturn testCompare(\n\t\t\t\tnode.op,\n\t\t\t\tgetField(input.value, node.field),\n\t\t\t\tresolve(node.value, input.principal),\n\t\t\t)\n\t\tcase \"In\": {\n\t\t\tconst field = getField(input.value, node.field)\n\t\t\treturn node.values.some(\n\t\t\t\t(operand) => resolve(operand, input.principal) === field,\n\t\t\t)\n\t\t}\n\t\tcase \"Exists\":\n\t\t\treturn getField(input.value, node.field) !== null\n\t\tcase \"Under\":\n\t\t\treturn testUnder(node.prefix, input)\n\t\tcase \"And\":\n\t\t\treturn Arr.every(node.conditions, (child) => testSync(child, input))\n\t\tcase \"Or\":\n\t\t\treturn Arr.some(node.conditions, (child) => testSync(child, input))\n\t\tcase \"Not\":\n\t\t\treturn !testSync(node.condition, input)\n\t\tcase \"Predicate\":\n\t\t\t// The types stop this. A predicate needs `evaluate`.\n\t\t\tthrow new Error(\n\t\t\t\t`The predicate \"${node.name}\" needs an Effect. Use Condition.evaluate.`,\n\t\t\t)\n\t}\n}\n\n/**\n * Tests a simple condition on a value. The children of `And` and `Or` run in\n * order, and they stop at the first child that gives the answer.\n */\nconst test = (\n\tnode: Node,\n\tinput: Input,\n): Effect.Effect<boolean, unknown, unknown> => {\n\tif (!node.opaque) {\n\t\treturn Effect.sync(() => testSync(node, input))\n\t}\n\tswitch (node._tag) {\n\t\tcase \"Predicate\":\n\t\t\treturn Nodes.runOf(node)(input.value, input.principal)\n\t\tcase \"Not\":\n\t\t\treturn Effect.map(test(node.condition, input), (value) => !value)\n\t\tcase \"And\":\n\t\t\treturn Effect.map(\n\t\t\t\tEffect.findFirst(node.conditions, (child) =>\n\t\t\t\t\tEffect.map(test(child, input), (value) => !value),\n\t\t\t\t),\n\t\t\t\tOption.isNone,\n\t\t\t)\n\t\tcase \"Or\":\n\t\t\treturn Effect.map(\n\t\t\t\tEffect.findFirst(node.conditions, (child) => test(child, input)),\n\t\t\t\tOption.isSome,\n\t\t\t)\n\t\tdefault:\n\t\t\treturn Effect.sync(() => testSync(node, input))\n\t}\n}\n\n/**\n * Tests a condition with no predicate on a value. The test runs on the\n * simple form of the condition with the principal in it, as a residual does.\n * So a test reads only the fields that the residual reads: a read of a field\n * that is not plain data is a defect, and a field that the answer does not\n * need, as in `x AND False`, is not read.\n */\nexport const evaluateSync = (node: Node, input: Input): boolean =>\n\ttestSync(partial(node, input), input)\n\n/** Tests a condition on a value, as {@link evaluateSync} does. */\nexport const evaluate = (\n\tnode: Node,\n\tinput: Input,\n): Effect.Effect<boolean, unknown, unknown> =>\n\tEffect.suspend(() => test(partial(node, input), input))\n\n// The principal and the path\n\n/**\n * Puts the value of the principal into an operand. It gives `undefined` for a\n * principal field with no value. See {@link resolve}.\n */\nconst bindOperand = (\n\toperand: Nodes.Operand,\n\tprincipal: unknown,\n): Nodes.Literal | undefined => {\n\tif (operand._tag === \"Literal\") {\n\t\treturn operand\n\t}\n\tconst value = resolve(operand, principal)\n\treturn value === undefined ? undefined : Nodes.literal(value as Nodes.Scalar)\n}\n\nconst expandUnder = (\n\tprefix: ReadonlyArray<Nodes.Operand>,\n\tprincipal: unknown,\n\tpath: ReadonlyArray<Resource.PathSegment> | undefined,\n): Node => {\n\tif (path === undefined || prefix.length > path.length) {\n\t\treturn never\n\t}\n\treturn and(\n\t\tArr.map(prefix, (operand, index): Node => {\n\t\t\tconst value = resolve(operand, principal)\n\t\t\tconst segment = path[index]!\n\t\t\treturn value === undefined\n\t\t\t\t? never\n\t\t\t\t: typeof segment === \"string\"\n\t\t\t\t\t? segment === value\n\t\t\t\t\t\t? always\n\t\t\t\t\t\t: never\n\t\t\t\t\t: new Nodes.Compare({\n\t\t\t\t\t\t\top: \"eq\",\n\t\t\t\t\t\t\tfield: segment.field,\n\t\t\t\t\t\t\tvalue: Nodes.literal(value as Nodes.Scalar),\n\t\t\t\t\t\t})\n\t\t}),\n\t)\n}\n\n/** The principal and the path of the resource, for {@link partial}. */\nexport interface PartialInput {\n\treadonly principal?: object | undefined\n\treadonly path?: ReadonlyArray<Resource.PathSegment> | undefined\n}\n\n/**\n * Puts the principal into a condition and expands `Under` with the path of the\n * resource. The result is simple. It has no principal fields and no `Under`\n * nodes. A predicate keeps the principal.\n */\nexport const partial = (node: Node, input: PartialInput): Node => {\n\tconst bind = (current: Node): Node => {\n\t\tswitch (current._tag) {\n\t\t\tcase \"Compare\": {\n\t\t\t\tconst value = bindOperand(current.value, input.principal)\n\t\t\t\treturn value === undefined\n\t\t\t\t\t? never\n\t\t\t\t\t: value === current.value\n\t\t\t\t\t\t? current\n\t\t\t\t\t\t: new Nodes.Compare({ op: current.op, field: current.field, value })\n\t\t\t}\n\t\t\tcase \"In\": {\n\t\t\t\tconst values = Arr.flatMap(current.values, (operand) =>\n\t\t\t\t\tOption.toArray(\n\t\t\t\t\t\tOption.fromUndefinedOr(bindOperand(operand, input.principal)),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\treturn values.length === 0\n\t\t\t\t\t? never\n\t\t\t\t\t: new Nodes.In({ field: current.field, values })\n\t\t\t}\n\t\t\tcase \"Under\":\n\t\t\t\treturn expandUnder(current.prefix, input.principal, input.path)\n\t\t\tcase \"And\":\n\t\t\t\treturn and(Arr.map(current.conditions, bind))\n\t\t\tcase \"Or\":\n\t\t\t\treturn or(Arr.map(current.conditions, bind))\n\t\t\tcase \"Not\":\n\t\t\t\treturn current.condition.pipe(bind, not)\n\t\t\tcase \"Predicate\": {\n\t\t\t\tconst run = Nodes.runOf(current)\n\t\t\t\treturn new Nodes.Predicate({\n\t\t\t\t\tname: current.name,\n\t\t\t\t\trun: (value) => run(value, input.principal),\n\t\t\t\t})\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn current\n\t\t}\n\t}\n\treturn bind(node)\n}\n\n/** Gives the services in `context` to every predicate of a condition. */\nexport const provide = (node: Node, context: Context.Context<never>): Node => {\n\tif (!node.opaque) {\n\t\treturn node\n\t}\n\tswitch (node._tag) {\n\t\tcase \"Predicate\": {\n\t\t\tconst run = Nodes.runOf(node)\n\t\t\treturn new Nodes.Predicate({\n\t\t\t\tname: node.name,\n\t\t\t\trun: (value, principal) =>\n\t\t\t\t\tEffect.provideContext(run(value, principal), context),\n\t\t\t})\n\t\t}\n\t\tcase \"And\":\n\t\t\treturn new Nodes.And(\n\t\t\t\tArr.map(node.conditions, (child) => provide(child, context)),\n\t\t\t)\n\t\tcase \"Or\":\n\t\t\treturn new Nodes.Or(\n\t\t\t\tArr.map(node.conditions, (child) => provide(child, context)),\n\t\t\t)\n\t\tcase \"Not\":\n\t\t\treturn new Nodes.Not(provide(node.condition, context))\n\t\tdefault:\n\t\t\treturn node\n\t}\n}\n\n/** The fields that a condition tests, in the order that they first come. */\nexport const fields = (node: Node): ReadonlyArray<string> => {\n\tconst all = (current: Node): ReadonlyArray<string> => {\n\t\tswitch (current._tag) {\n\t\t\tcase \"Compare\":\n\t\t\tcase \"In\":\n\t\t\tcase \"Exists\":\n\t\t\t\treturn [current.field]\n\t\t\tcase \"And\":\n\t\t\tcase \"Or\":\n\t\t\t\treturn Arr.flatMap(current.conditions, all)\n\t\t\tcase \"Not\":\n\t\t\t\treturn all(current.condition)\n\t\t\tdefault:\n\t\t\t\treturn []\n\t\t}\n\t}\n\t// A native `Set`, as in `dedupe`: a field of a condition that is not bound\n\t// to a domain may be any string.\n\treturn Array.from(new Set(all(node)))\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorizer.d.ts","names":[],"sources":["../../src/internal/authorizer.ts"],"mappings":";;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"authorizer.d.ts","names":[],"sources":["../../src/internal/authorizer.ts"],"mappings":";;;;;;;;;;;;;;;;qBAkCa,kBAAkB;;;;;qBAUlB,0BAA0B;;;;;;qBAK1B,gBAAgB"}
|
|
@@ -5,10 +5,13 @@ import { provideContext } from "./policy.js";
|
|
|
5
5
|
import { decideAny, fitsKind, permittedFields } from "./decide.js";
|
|
6
6
|
import { coverageAny, decideKind } from "./analysis.js";
|
|
7
7
|
import * as Cache from "effect/Cache";
|
|
8
|
+
import * as Chunk from "effect/Chunk";
|
|
8
9
|
import * as Context from "effect/Context";
|
|
9
10
|
import * as Effect from "effect/Effect";
|
|
10
11
|
import * as Equal from "effect/Equal";
|
|
11
12
|
import * as Hash from "effect/Hash";
|
|
13
|
+
import * as HashMap from "effect/HashMap";
|
|
14
|
+
import * as HashSet from "effect/HashSet";
|
|
12
15
|
import * as Layer from "effect/Layer";
|
|
13
16
|
import * as Result from "effect/Result";
|
|
14
17
|
import * as Schema from "effect/Schema";
|
|
@@ -57,29 +60,38 @@ const Service = () => (id, options) => {
|
|
|
57
60
|
var PrincipalKey = class PrincipalKey {
|
|
58
61
|
principal;
|
|
59
62
|
text;
|
|
60
|
-
/**
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
/**
|
|
64
|
+
* The order of the parts of the decoded principal, from {@link orderOf},
|
|
65
|
+
* when the key is the JSON form. The decoded principals must be equal
|
|
66
|
+
* too, with their parts in the same order. `undefined` for a key from
|
|
67
|
+
* `cache.key`.
|
|
68
|
+
*/
|
|
69
|
+
order;
|
|
70
|
+
constructor(principal, text, order) {
|
|
63
71
|
this.principal = principal;
|
|
64
72
|
this.text = text;
|
|
65
|
-
this.
|
|
73
|
+
this.order = order;
|
|
66
74
|
}
|
|
67
75
|
[Equal.symbol](that) {
|
|
68
|
-
return that instanceof PrincipalKey && that.text === this.text && (
|
|
76
|
+
return that instanceof PrincipalKey && that.text === this.text && that.order === this.order && (this.order === void 0 || Equal.equals(that.principal, this.principal));
|
|
69
77
|
}
|
|
70
78
|
[Hash.symbol]() {
|
|
71
79
|
return Hash.string(this.text);
|
|
72
80
|
}
|
|
73
81
|
};
|
|
74
82
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* tells them apart. So two principals with one key are equal, and the cache
|
|
81
|
-
* can not give one the policy of the other.
|
|
83
|
+
* The text of the order of the parts of a value: the names of the fields of
|
|
84
|
+
* each object, and the items of each list, set and map, in the order that a
|
|
85
|
+
* loop sees them. `Equal` does not see this order, but a loader may read it,
|
|
86
|
+
* as `Object.values` does. So two keys need the same order too. A scalar has
|
|
87
|
+
* no parts, and `Equal` compares it.
|
|
82
88
|
*/
|
|
89
|
+
const orderOf = (value) => {
|
|
90
|
+
if (typeof value !== "object" || value === null) return "";
|
|
91
|
+
if (Array.isArray(value)) return `[${value.map(orderOf).join(",")}]`;
|
|
92
|
+
if (value instanceof Map || value instanceof Set || HashMap.isHashMap(value) || HashSet.isHashSet(value) || Chunk.isChunk(value)) return `<${Array.from(value, orderOf).join(",")}>`;
|
|
93
|
+
return `{${Object.keys(value).map((name) => `${JSON.stringify(name)}:${orderOf(value[name])}`).join(",")}}`;
|
|
94
|
+
};
|
|
83
95
|
/**
|
|
84
96
|
* Gives the JSON codec of a Schema, or `undefined` when the Schema has no JSON
|
|
85
97
|
* form, as a struct with a symbol key has none.
|
|
@@ -91,6 +103,17 @@ const jsonCodecOf = (schema) => {
|
|
|
91
103
|
return;
|
|
92
104
|
}
|
|
93
105
|
};
|
|
106
|
+
/**
|
|
107
|
+
* Makes the cache key of a principal from its JSON form. The key keeps the
|
|
108
|
+
* principal that the Schema decodes from that form, so the loader sees only
|
|
109
|
+
* the data that the key shows. A principal gets no key when it does not
|
|
110
|
+
* encode, or when the decoded value is not `Equal` to it. Two classes, or a
|
|
111
|
+
* string and a number, may have one JSON form, as in a union, and `Equal`
|
|
112
|
+
* tells them apart. A Schema may make data when it decodes, as the order of
|
|
113
|
+
* the fields of a record, so the key keeps that order too. So two
|
|
114
|
+
* principals with one key are equal, and the cache can not give one the
|
|
115
|
+
* policy of the other.
|
|
116
|
+
*/
|
|
94
117
|
const jsonKey = (schema) => {
|
|
95
118
|
const codec = jsonCodecOf(schema);
|
|
96
119
|
if (codec === void 0) return () => void 0;
|
|
@@ -102,7 +125,7 @@ const jsonKey = (schema) => {
|
|
|
102
125
|
const text = JSON.stringify(json);
|
|
103
126
|
return Result.match(decode(JSON.parse(text)), {
|
|
104
127
|
onFailure: () => void 0,
|
|
105
|
-
onSuccess: (decoded) => Equal.equals(decoded, principal) ? new PrincipalKey(decoded, text,
|
|
128
|
+
onSuccess: (decoded) => Equal.equals(decoded, principal) ? new PrincipalKey(decoded, text, orderOf(decoded)) : void 0
|
|
106
129
|
});
|
|
107
130
|
}
|
|
108
131
|
});
|
|
@@ -128,7 +151,7 @@ const makeLayer = (service, options) => {
|
|
|
128
151
|
const keyOf = (principal, canonical) => {
|
|
129
152
|
if (custom === void 0) return canonical;
|
|
130
153
|
const text = custom(principal);
|
|
131
|
-
return text === void 0 ? void 0 : new PrincipalKey(canonical?.principal ?? principal, text,
|
|
154
|
+
return text === void 0 ? void 0 : new PrincipalKey(canonical?.principal ?? principal, text, void 0);
|
|
132
155
|
};
|
|
133
156
|
return Layer.effect(service, Effect.gen(function* () {
|
|
134
157
|
const services = yield* Effect.context();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorizer.js","names":["Policies.provideContext","Decide.decideAny","ResourceData.kindOf","DomainData.descriptorOf","Decide.fitsKind","Analysis.decideKind","Decision.reason","Analysis.coverageAny","permittedFields","Decide.permittedFields"],"sources":["../../src/internal/authorizer.ts"],"sourcesContent":["/**\n * The values of authorizers: the errors, the service class and the layer.\n * The types are in `Authorizer.ts`.\n */\n// The service erases the error type and the service type of predicates,\n// principals and deciders to `unknown`. The public types keep them, so the\n// language service check for `unknown` in an Effect is off here.\n// @effect-diagnostics anyUnknownInErrorContext:off\nimport * as Cache from \"effect/Cache\"\nimport * as Context from \"effect/Context\"\nimport type * as Duration from \"effect/Duration\"\nimport * as Effect from \"effect/Effect\"\nimport * as Equal from \"effect/Equal\"\nimport * as Hash from \"effect/Hash\"\nimport * as Layer from \"effect/Layer\"\nimport * as Result from \"effect/Result\"\nimport * as Schema from \"effect/Schema\"\nimport type * as Authorizer from \"../Authorizer.ts\"\nimport type * as Condition from \"../Condition.ts\"\nimport * as Decision from \"../Decision.ts\"\nimport type * as Domain from \"../Domain.ts\"\nimport type * as Policy from \"../Policy.ts\"\nimport * as Analysis from \"./analysis.ts\"\nimport * as Decide from \"./decide.ts\"\nimport * as DomainData from \"./domain.ts\"\nimport * as Policies from \"./policy.ts\"\nimport * as ResourceData from \"./resource.ts\"\n\ntype AnyServiceClass = Authorizer.AnyServiceClass\n\n/** The error when a policy says no. */\nexport class Forbidden extends Schema.TaggedError<Forbidden>(\n\t\"@nmnmcc/ability/Authorizer/Forbidden\",\n)(\"Forbidden\", {\n\taction: Schema.String,\n\tkind: Schema.String,\n\tfield: Schema.optionalKey(Schema.String),\n\treason: Schema.optionalKey(Schema.String),\n}) {}\n\n/** The error when the policy of the principal can not be loaded. */\nexport class PolicyUnavailable extends Schema.TaggedError<PolicyUnavailable>(\n\t\"@nmnmcc/ability/Authorizer/PolicyUnavailable\",\n)(\"PolicyUnavailable\", { cause: Schema.Defect() }) {}\n\n/** The error when a decider must wait for an answer. */\nexport class Pending extends Schema.TaggedError<Pending>(\n\t\"@nmnmcc/ability/Authorizer/Pending\",\n)(\"Pending\", {\n\tticket: Schema.String,\n\treason: Schema.optionalKey(Schema.String),\n}) {}\n\n/** Makes an authorizer service class for a domain. */\nexport const Service =\n\t() =>\n\t(\n\t\tid: string,\n\t\toptions: {\n\t\t\treadonly domain: Domain.Any\n\t\t\treadonly principal: Effect.Effect<any, any, any>\n\t\t},\n\t\t// The class has no name, and its type uses types of Effect that a\n\t\t// declaration file can not name. So this function gives a `Context.Key`.\n\t\t// The public module gives the full type.\n\t): Context.Key<any, any> => {\n\t\tconst Base = Context.Service<any, any>()(id)\n\t\treturn class extends Base {\n\t\t\tstatic readonly domain = options.domain\n\t\t\tstatic readonly principal = options.principal\n\t\t\tstatic readonly decide = (target: AnyTarget) =>\n\t\t\t\tthis.use((service: any) => service.decide(target))\n\t\t\tstatic readonly check = (target: AnyTarget) =>\n\t\t\t\tthis.use((service: any) => service.check(target))\n\t\t\tstatic readonly guard = (target: AnyTarget, options: unknown) =>\n\t\t\t\tthis.use((service: any) => service.guard(target, options))\n\t\t\tstatic readonly filter = (target: AnyTarget) =>\n\t\t\t\tthis.use((service: any) => service.filter(target))\n\t\t\tstatic readonly coverage = (target: AnyTarget) =>\n\t\t\t\tthis.use((service: any) => service.coverage(target))\n\t\t\tstatic readonly permittedFields = (target: AnyTarget) =>\n\t\t\t\tthis.use((service: any) => service.permittedFields(target))\n\t\t}\n\t}\n\ntype AnyTarget = {\n\treadonly action: string\n\treadonly resource?: object\n\treadonly kind?: string\n\treadonly field?: string\n\treadonly within?: unknown\n}\n\n/**\n * A cache key for a principal. Two keys are equal when they have the same\n * text, so two principals with the same key share one policy. A key from the\n * JSON form also needs equal decoded principals: a Schema may make data when\n * it decodes, as `expired` from a time, so one text may give two principals.\n * The key also keeps the principal, so the cache can load its policy. A plain\n * `Data` value can not do all this, so the key gives its own equality with\n * `Equal.Equal`.\n */\nclass PrincipalKey implements Equal.Equal {\n\treadonly principal: object\n\treadonly text: string\n\t/** The key is the JSON form, so the decoded principals must be equal too. */\n\treadonly exact: boolean\n\tconstructor(principal: object, text: string, exact: boolean) {\n\t\tthis.principal = principal\n\t\tthis.text = text\n\t\tthis.exact = exact\n\t}\n\t[Equal.symbol](that: unknown): boolean {\n\t\treturn (\n\t\t\tthat instanceof PrincipalKey &&\n\t\t\tthat.text === this.text &&\n\t\t\t(!this.exact || Equal.equals(that.principal, this.principal))\n\t\t)\n\t}\n\t[Hash.symbol](): number {\n\t\treturn Hash.string(this.text)\n\t}\n}\n\n/**\n * Makes the cache key of a principal from its JSON form. The key keeps the\n * principal that the Schema decodes from that form, so the loader sees only\n * the data that the key shows. A principal gets no key when it does not\n * encode, or when the decoded value is not `Equal` to it. Two classes, or a\n * string and a number, may have one JSON form, as in a union, and `Equal`\n * tells them apart. So two principals with one key are equal, and the cache\n * can not give one the policy of the other.\n */\n/**\n * Gives the JSON codec of a Schema, or `undefined` when the Schema has no JSON\n * form, as a struct with a symbol key has none.\n */\nconst jsonCodecOf = (\n\tschema: Schema.Top,\n): Schema.Codec<unknown, unknown> | undefined => {\n\ttry {\n\t\treturn Schema.toCodecJson(schema) as unknown as Schema.Codec<\n\t\t\tunknown,\n\t\t\tunknown\n\t\t>\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\nconst jsonKey = (\n\tschema: Schema.Top,\n): ((principal: object) => PrincipalKey | undefined) => {\n\tconst codec = jsonCodecOf(schema)\n\t// A principal with no JSON form is never kept.\n\tif (codec === undefined) {\n\t\treturn () => undefined\n\t}\n\tconst encode = Schema.encodeUnknownResult(codec)\n\tconst decode = Schema.decodeUnknownResult(codec)\n\tconst keyOf = (principal: object): PrincipalKey | undefined =>\n\t\tResult.match(encode(principal), {\n\t\t\tonFailure: () => undefined,\n\t\t\tonSuccess: (json) => {\n\t\t\t\t// The key is the text, so the check decodes the text. The encoded\n\t\t\t\t// value may hold more than its text shows, as an object of a\n\t\t\t\t// `Schema.Any` field does: `JSON.stringify` leaves out a map.\n\t\t\t\tconst text = JSON.stringify(json)\n\t\t\t\treturn Result.match(decode(JSON.parse(text)), {\n\t\t\t\t\tonFailure: () => undefined,\n\t\t\t\t\tonSuccess: (decoded) =>\n\t\t\t\t\t\tEqual.equals(decoded, principal)\n\t\t\t\t\t\t\t? new PrincipalKey(decoded as object, text, true)\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t})\n\t\t\t},\n\t\t})\n\treturn (principal) => {\n\t\t// A very deep principal may fill the stack of the encoder or of\n\t\t// `Equal`. Such a principal is not kept.\n\t\ttry {\n\t\t\treturn keyOf(principal)\n\t\t} catch {\n\t\t\treturn undefined\n\t\t}\n\t}\n}\n\nconst forbidden = (\n\ttarget: AnyTarget,\n\tkind: string,\n\treason: string | undefined,\n): Forbidden =>\n\tForbidden.make({\n\t\taction: target.action,\n\t\tkind,\n\t\t...(target.field === undefined ? {} : { field: target.field }),\n\t\t...(reason === undefined ? {} : { reason }),\n\t})\n\n/** Makes the layer of an authorizer. */\nexport const makeLayer = (\n\tservice: AnyServiceClass,\n\toptions: {\n\t\treadonly policy: (\n\t\t\tprincipal: object,\n\t\t) => Effect.Effect<Policy.Any, unknown, unknown>\n\t\treadonly decider?: (\n\t\t\trequest: object,\n\t\t) => Effect.Effect<Decision.Decision, unknown, unknown>\n\t\treadonly cache?:\n\t\t\t| {\n\t\t\t\t\treadonly capacity?: number\n\t\t\t\t\treadonly timeToLive?: Duration.Input\n\t\t\t\t\treadonly key?: (principal: object) => string | undefined\n\t\t\t }\n\t\t\t| false\n\t},\n) => {\n\tconst domain: Domain.Any = service.domain\n\t// The JSON form of a principal, and the copy that the Schema decodes from\n\t// it. The loader always gets the copy, with a cache or not, so a cache can\n\t// not change an answer: a form may lose data that `Equal` does not see,\n\t// as `-0` becomes `0`. A principal with no such form gives `undefined`.\n\tconst canonicalOf = jsonKey(domain.principal)\n\tconst custom = options.cache === false ? undefined : options.cache?.key\n\t// It gives `undefined` when the principal has no key. Such a principal does\n\t// not use the cache.\n\tconst keyOf = (\n\t\tprincipal: object,\n\t\tcanonical: PrincipalKey | undefined,\n\t): PrincipalKey | undefined => {\n\t\tif (custom === undefined) {\n\t\t\treturn canonical\n\t\t}\n\t\tconst text = custom(principal)\n\t\treturn text === undefined\n\t\t\t? undefined\n\t\t\t: new PrincipalKey(canonical?.principal ?? principal, text, false)\n\t}\n\treturn Layer.effect(\n\t\tservice,\n\t\tEffect.gen(function* () {\n\t\t\t// The services of the policy loader and of the predicates come from the\n\t\t\t// layer, so a check does not need them.\n\t\t\tconst services = yield* Effect.context<never>()\n\n\t\t\t// The predicates of the policy get the services of the layer, so the\n\t\t\t// policy and its residual conditions need no services.\n\t\t\t// A policy of another domain may have the same types, but its resources\n\t\t\t// may have other paths. So it fails closed, as a policy that can not be\n\t\t\t// loaded.\n\t\t\tconst load = (principal: object) =>\n\t\t\t\toptions.policy(principal).pipe(\n\t\t\t\t\tEffect.provideContext(services),\n\t\t\t\t\tEffect.filterOrFail(\n\t\t\t\t\t\t(value) => value.domain === domain,\n\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\tnew globalThis.Error(\n\t\t\t\t\t\t\t\t\"The loaded policy is not a policy of this domain\",\n\t\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.mapError((cause) => PolicyUnavailable.make({ cause })),\n\t\t\t\t\tEffect.map((value) =>\n\t\t\t\t\t\tPolicies.provideContext(value, services as never),\n\t\t\t\t\t),\n\t\t\t\t)\n\n\t\t\tconst cache =\n\t\t\t\toptions.cache === false\n\t\t\t\t\t? undefined\n\t\t\t\t\t: yield* Cache.make({\n\t\t\t\t\t\t\tcapacity: options.cache?.capacity ?? 1000,\n\t\t\t\t\t\t\ttimeToLive: options.cache?.timeToLive ?? \"30 seconds\",\n\t\t\t\t\t\t\tlookup: (key: PrincipalKey) => load(key.principal),\n\t\t\t\t\t\t})\n\n\t\t\tconst policyFor = (principal: object) => {\n\t\t\t\tconst canonical = canonicalOf(principal)\n\t\t\t\tconst key =\n\t\t\t\t\tcache === undefined ? undefined : keyOf(principal, canonical)\n\t\t\t\tif (cache === undefined || key === undefined) {\n\t\t\t\t\treturn load(canonical?.principal ?? principal)\n\t\t\t\t}\n\t\t\t\t// A failed lookup is kept in the cache. Remove it, so the next check\n\t\t\t\t// tries again.\n\t\t\t\treturn Cache.get(cache, key).pipe(\n\t\t\t\t\tEffect.tapError(() => Cache.invalidate(cache, key)),\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tconst withServices = <A, E>(\n\t\t\t\teffect: Effect.Effect<A, E, unknown>,\n\t\t\t): Effect.Effect<A, E, unknown> => Effect.provideContext(effect, services)\n\n\t\t\tconst current = Effect.gen(function* () {\n\t\t\t\tconst principal = yield* service.principal\n\t\t\t\tconst value = yield* policyFor(principal)\n\t\t\t\treturn { principal, policy: value }\n\t\t\t})\n\n\t\t\t// The principal and its policy, loaded one time. A check that asks\n\t\t\t// the policy two times, such as `guard`, uses one snapshot for both,\n\t\t\t// so the two answers come from one principal.\n\t\t\ttype Snapshot = Effect.Success<typeof current>\n\n\t\t\tconst decideWith = Effect.fnUntraced(function* (\n\t\t\t\t{ principal, policy: value }: Snapshot,\n\t\t\t\ttarget: AnyTarget,\n\t\t\t) {\n\t\t\t\tconst decision: Decision.Decision = yield* withServices(\n\t\t\t\t\tDecide.decideAny(value, { ...target, principal } as never),\n\t\t\t\t)\n\t\t\t\treturn decision\n\t\t\t})\n\n\t\t\tconst decide = (target: AnyTarget) =>\n\t\t\t\tEffect.flatMap(current, (snapshot) => decideWith(snapshot, target))\n\n\t\t\tconst checkWith = Effect.fnUntraced(function* (\n\t\t\t\t{ principal, policy: value }: Snapshot,\n\t\t\t\ttarget: AnyTarget,\n\t\t\t) {\n\t\t\t\tconst kind =\n\t\t\t\t\ttarget.resource === undefined\n\t\t\t\t\t\t? (target.kind ?? \"\")\n\t\t\t\t\t\t: (ResourceData.kindOf(target.resource) ?? \"\")\n\t\t\t\t// A request that does not fit its kind gets a \"no\" at once. The\n\t\t\t\t// decider must not say yes to an action that the kind does not have.\n\t\t\t\tconst descriptor = DomainData.descriptorOf(domain, kind)\n\t\t\t\tif (descriptor === undefined || !Decide.fitsKind(descriptor, target)) {\n\t\t\t\t\treturn yield* forbidden(target, kind, undefined)\n\t\t\t\t}\n\t\t\t\tconst fromPolicy: Decision.Decision =\n\t\t\t\t\ttarget.resource === undefined\n\t\t\t\t\t\t? Analysis.decideKind(value, { ...target, principal } as never)\n\t\t\t\t\t\t: yield* withServices(\n\t\t\t\t\t\t\t\tDecide.decideAny(value, { ...target, principal } as never),\n\t\t\t\t\t\t\t)\n\t\t\t\t// When no rule gives an answer, the decider gives it.\n\t\t\t\tconst decision: Decision.Decision =\n\t\t\t\t\tfromPolicy._tag === \"NotApplicable\" && options.decider !== undefined\n\t\t\t\t\t\t? yield* options.decider({ ...target, principal })\n\t\t\t\t\t\t: fromPolicy\n\t\t\t\tif (decision._tag !== \"Allow\") {\n\t\t\t\t\treturn yield* forbidden(target, kind, Decision.reason(decision))\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tconst check = (target: AnyTarget) =>\n\t\t\t\tEffect.flatMap(current, (snapshot) => checkWith(snapshot, target))\n\n\t\t\tconst guard = Effect.fnUntraced(function* (\n\t\t\t\ttarget: AnyTarget,\n\t\t\t\tguardOptions: {\n\t\t\t\t\treadonly read?: string\n\t\t\t\t\treadonly hide: () => unknown\n\t\t\t\t},\n\t\t\t) {\n\t\t\t\tconst snapshot = yield* current\n\t\t\t\tconst read = guardOptions.read ?? \"read\"\n\t\t\t\tconst { field: _field, ...rest } = target\n\t\t\t\tconst visible: Decision.Decision = yield* decideWith(snapshot, {\n\t\t\t\t\t...rest,\n\t\t\t\t\taction: read,\n\t\t\t\t})\n\t\t\t\tif (visible._tag !== \"Allow\") {\n\t\t\t\t\treturn yield* Effect.fail(guardOptions.hide())\n\t\t\t\t}\n\t\t\t\tif (target.action !== read || target.field !== undefined) {\n\t\t\t\t\tyield* checkWith(snapshot, target)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\t// The filter is the condition of the coverage: the scope for `All`,\n\t\t\t// `False` for `None`, and else the residual condition in the scope.\n\t\t\t// The residual throws an error when it is too large, so the filter\n\t\t\t// fails.\n\t\t\tconst filter = (\n\t\t\t\ttarget: AnyTarget,\n\t\t\t): Effect.Effect<Condition.Ast, unknown, unknown> =>\n\t\t\t\tEffect.map(\n\t\t\t\t\tcurrent,\n\t\t\t\t\t({ principal, policy: value }): Condition.Ast =>\n\t\t\t\t\t\tAnalysis.coverageAny(value, { ...target, principal } as never)\n\t\t\t\t\t\t\t.condition as Condition.Ast,\n\t\t\t\t)\n\n\t\t\tconst coverage = (\n\t\t\t\ttarget: AnyTarget,\n\t\t\t): Effect.Effect<Analysis.AnyCoverage, unknown, unknown> =>\n\t\t\t\tEffect.map(current, ({ principal, policy: value }) =>\n\t\t\t\t\tAnalysis.coverageAny(value, { ...target, principal } as never),\n\t\t\t\t)\n\n\t\t\tconst permittedFields = Effect.fnUntraced(function* (target: AnyTarget) {\n\t\t\t\tconst { principal, policy: value } = yield* current\n\t\t\t\treturn yield* withServices(\n\t\t\t\t\tDecide.permittedFields(value, { ...target, principal } as never),\n\t\t\t\t)\n\t\t\t})\n\n\t\t\tconst invalidate = (principal?: object) => {\n\t\t\t\tif (cache === undefined) {\n\t\t\t\t\treturn Effect.void\n\t\t\t\t}\n\t\t\t\tif (principal === undefined) {\n\t\t\t\t\treturn Cache.invalidateAll(cache)\n\t\t\t\t}\n\t\t\t\tconst key = keyOf(principal, canonicalOf(principal))\n\t\t\t\treturn key === undefined ? Effect.void : Cache.invalidate(cache, key)\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tdomain,\n\t\t\t\tprincipal: service.principal,\n\t\t\t\tpolicy: Effect.map(current, ({ policy: value }) => value),\n\t\t\t\tdecide,\n\t\t\t\tcheck,\n\t\t\t\tguard,\n\t\t\t\tfilter,\n\t\t\t\tcoverage,\n\t\t\t\tpermittedFields,\n\t\t\t\tinvalidate,\n\t\t\t} as never\n\t\t}),\n\t)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"authorizer.js","names":["Policies.provideContext","Decide.decideAny","ResourceData.kindOf","DomainData.descriptorOf","Decide.fitsKind","Analysis.decideKind","Decision.reason","Analysis.coverageAny","permittedFields","Decide.permittedFields"],"sources":["../../src/internal/authorizer.ts"],"sourcesContent":["/**\n * The values of authorizers: the errors, the service class and the layer.\n * The types are in `Authorizer.ts`.\n */\n// The service erases the error type and the service type of predicates,\n// principals and deciders to `unknown`. The public types keep them, so the\n// language service check for `unknown` in an Effect is off here.\n// @effect-diagnostics anyUnknownInErrorContext:off\nimport * as Cache from \"effect/Cache\"\nimport * as Chunk from \"effect/Chunk\"\nimport * as Context from \"effect/Context\"\nimport type * as Duration from \"effect/Duration\"\nimport * as Effect from \"effect/Effect\"\nimport * as Equal from \"effect/Equal\"\nimport * as Hash from \"effect/Hash\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as HashSet from \"effect/HashSet\"\nimport * as Layer from \"effect/Layer\"\nimport * as Result from \"effect/Result\"\nimport * as Schema from \"effect/Schema\"\nimport type * as Authorizer from \"../Authorizer.ts\"\nimport type * as Condition from \"../Condition.ts\"\nimport * as Decision from \"../Decision.ts\"\nimport type * as Domain from \"../Domain.ts\"\nimport type * as Policy from \"../Policy.ts\"\nimport * as Analysis from \"./analysis.ts\"\nimport * as Decide from \"./decide.ts\"\nimport * as DomainData from \"./domain.ts\"\nimport * as Policies from \"./policy.ts\"\nimport * as ResourceData from \"./resource.ts\"\n\ntype AnyServiceClass = Authorizer.AnyServiceClass\n\n/** The error when a policy says no. */\nexport class Forbidden extends Schema.TaggedError<Forbidden>(\n\t\"@nmnmcc/ability/Authorizer/Forbidden\",\n)(\"Forbidden\", {\n\taction: Schema.String,\n\tkind: Schema.String,\n\tfield: Schema.optionalKey(Schema.String),\n\treason: Schema.optionalKey(Schema.String),\n}) {}\n\n/** The error when the policy of the principal can not be loaded. */\nexport class PolicyUnavailable extends Schema.TaggedError<PolicyUnavailable>(\n\t\"@nmnmcc/ability/Authorizer/PolicyUnavailable\",\n)(\"PolicyUnavailable\", { cause: Schema.Defect() }) {}\n\n/** The error when a decider must wait for an answer. */\nexport class Pending extends Schema.TaggedError<Pending>(\n\t\"@nmnmcc/ability/Authorizer/Pending\",\n)(\"Pending\", {\n\tticket: Schema.String,\n\treason: Schema.optionalKey(Schema.String),\n}) {}\n\n/** Makes an authorizer service class for a domain. */\nexport const Service =\n\t() =>\n\t(\n\t\tid: string,\n\t\toptions: {\n\t\t\treadonly domain: Domain.Any\n\t\t\treadonly principal: Effect.Effect<any, any, any>\n\t\t},\n\t\t// The class has no name, and its type uses types of Effect that a\n\t\t// declaration file can not name. So this function gives a `Context.Key`.\n\t\t// The public module gives the full type.\n\t): Context.Key<any, any> => {\n\t\tconst Base = Context.Service<any, any>()(id)\n\t\treturn class extends Base {\n\t\t\tstatic readonly domain = options.domain\n\t\t\tstatic readonly principal = options.principal\n\t\t\tstatic readonly decide = (target: AnyTarget) =>\n\t\t\t\tthis.use((service: any) => service.decide(target))\n\t\t\tstatic readonly check = (target: AnyTarget) =>\n\t\t\t\tthis.use((service: any) => service.check(target))\n\t\t\tstatic readonly guard = (target: AnyTarget, options: unknown) =>\n\t\t\t\tthis.use((service: any) => service.guard(target, options))\n\t\t\tstatic readonly filter = (target: AnyTarget) =>\n\t\t\t\tthis.use((service: any) => service.filter(target))\n\t\t\tstatic readonly coverage = (target: AnyTarget) =>\n\t\t\t\tthis.use((service: any) => service.coverage(target))\n\t\t\tstatic readonly permittedFields = (target: AnyTarget) =>\n\t\t\t\tthis.use((service: any) => service.permittedFields(target))\n\t\t}\n\t}\n\ntype AnyTarget = {\n\treadonly action: string\n\treadonly resource?: object\n\treadonly kind?: string\n\treadonly field?: string\n\treadonly within?: unknown\n}\n\n/**\n * A cache key for a principal. Two keys are equal when they have the same\n * text, so two principals with the same key share one policy. A key from the\n * JSON form also needs equal decoded principals: a Schema may make data when\n * it decodes, as `expired` from a time, so one text may give two principals.\n * The key also keeps the principal, so the cache can load its policy. A plain\n * `Data` value can not do all this, so the key gives its own equality with\n * `Equal.Equal`.\n */\nclass PrincipalKey implements Equal.Equal {\n\treadonly principal: object\n\treadonly text: string\n\t/**\n\t * The order of the parts of the decoded principal, from {@link orderOf},\n\t * when the key is the JSON form. The decoded principals must be equal\n\t * too, with their parts in the same order. `undefined` for a key from\n\t * `cache.key`.\n\t */\n\treadonly order: string | undefined\n\tconstructor(principal: object, text: string, order: string | undefined) {\n\t\tthis.principal = principal\n\t\tthis.text = text\n\t\tthis.order = order\n\t}\n\t[Equal.symbol](that: unknown): boolean {\n\t\treturn (\n\t\t\tthat instanceof PrincipalKey &&\n\t\t\tthat.text === this.text &&\n\t\t\tthat.order === this.order &&\n\t\t\t(this.order === undefined || Equal.equals(that.principal, this.principal))\n\t\t)\n\t}\n\t[Hash.symbol](): number {\n\t\treturn Hash.string(this.text)\n\t}\n}\n\n/**\n * The text of the order of the parts of a value: the names of the fields of\n * each object, and the items of each list, set and map, in the order that a\n * loop sees them. `Equal` does not see this order, but a loader may read it,\n * as `Object.values` does. So two keys need the same order too. A scalar has\n * no parts, and `Equal` compares it.\n */\nconst orderOf = (value: unknown): string => {\n\tif (typeof value !== \"object\" || value === null) {\n\t\treturn \"\"\n\t}\n\tif (Array.isArray(value)) {\n\t\treturn `[${value.map(orderOf).join(\",\")}]`\n\t}\n\tif (\n\t\tvalue instanceof Map ||\n\t\tvalue instanceof Set ||\n\t\tHashMap.isHashMap(value) ||\n\t\tHashSet.isHashSet(value) ||\n\t\tChunk.isChunk(value)\n\t) {\n\t\treturn `<${Array.from(value as Iterable<unknown>, orderOf).join(\",\")}>`\n\t}\n\treturn `{${Object.keys(value)\n\t\t.map(\n\t\t\t(name) =>\n\t\t\t\t`${JSON.stringify(name)}:${orderOf((value as Record<string, unknown>)[name])}`,\n\t\t)\n\t\t.join(\",\")}}`\n}\n\n/**\n * Gives the JSON codec of a Schema, or `undefined` when the Schema has no JSON\n * form, as a struct with a symbol key has none.\n */\nconst jsonCodecOf = (\n\tschema: Schema.Top,\n): Schema.Codec<unknown, unknown> | undefined => {\n\ttry {\n\t\treturn Schema.toCodecJson(schema) as unknown as Schema.Codec<\n\t\t\tunknown,\n\t\t\tunknown\n\t\t>\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\n/**\n * Makes the cache key of a principal from its JSON form. The key keeps the\n * principal that the Schema decodes from that form, so the loader sees only\n * the data that the key shows. A principal gets no key when it does not\n * encode, or when the decoded value is not `Equal` to it. Two classes, or a\n * string and a number, may have one JSON form, as in a union, and `Equal`\n * tells them apart. A Schema may make data when it decodes, as the order of\n * the fields of a record, so the key keeps that order too. So two\n * principals with one key are equal, and the cache can not give one the\n * policy of the other.\n */\nconst jsonKey = (\n\tschema: Schema.Top,\n): ((principal: object) => PrincipalKey | undefined) => {\n\tconst codec = jsonCodecOf(schema)\n\t// A principal with no JSON form is never kept.\n\tif (codec === undefined) {\n\t\treturn () => undefined\n\t}\n\tconst encode = Schema.encodeUnknownResult(codec)\n\tconst decode = Schema.decodeUnknownResult(codec)\n\tconst keyOf = (principal: object): PrincipalKey | undefined =>\n\t\tResult.match(encode(principal), {\n\t\t\tonFailure: () => undefined,\n\t\t\tonSuccess: (json) => {\n\t\t\t\t// The key is the text, so the check decodes the text. The encoded\n\t\t\t\t// value may hold more than its text shows, as an object of a\n\t\t\t\t// `Schema.Any` field does: `JSON.stringify` leaves out a map.\n\t\t\t\tconst text = JSON.stringify(json)\n\t\t\t\treturn Result.match(decode(JSON.parse(text)), {\n\t\t\t\t\tonFailure: () => undefined,\n\t\t\t\t\tonSuccess: (decoded) =>\n\t\t\t\t\t\tEqual.equals(decoded, principal)\n\t\t\t\t\t\t\t? new PrincipalKey(decoded as object, text, orderOf(decoded))\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t})\n\t\t\t},\n\t\t})\n\treturn (principal) => {\n\t\t// A very deep principal may fill the stack of the encoder or of\n\t\t// `Equal`. Such a principal is not kept.\n\t\ttry {\n\t\t\treturn keyOf(principal)\n\t\t} catch {\n\t\t\treturn undefined\n\t\t}\n\t}\n}\n\nconst forbidden = (\n\ttarget: AnyTarget,\n\tkind: string,\n\treason: string | undefined,\n): Forbidden =>\n\tForbidden.make({\n\t\taction: target.action,\n\t\tkind,\n\t\t...(target.field === undefined ? {} : { field: target.field }),\n\t\t...(reason === undefined ? {} : { reason }),\n\t})\n\n/** Makes the layer of an authorizer. */\nexport const makeLayer = (\n\tservice: AnyServiceClass,\n\toptions: {\n\t\treadonly policy: (\n\t\t\tprincipal: object,\n\t\t) => Effect.Effect<Policy.Any, unknown, unknown>\n\t\treadonly decider?: (\n\t\t\trequest: object,\n\t\t) => Effect.Effect<Decision.Decision, unknown, unknown>\n\t\treadonly cache?:\n\t\t\t| {\n\t\t\t\t\treadonly capacity?: number\n\t\t\t\t\treadonly timeToLive?: Duration.Input\n\t\t\t\t\treadonly key?: (principal: object) => string | undefined\n\t\t\t }\n\t\t\t| false\n\t},\n) => {\n\tconst domain: Domain.Any = service.domain\n\t// The JSON form of a principal, and the copy that the Schema decodes from\n\t// it. The loader always gets the copy, with a cache or not, so a cache can\n\t// not change an answer: a form may lose data that `Equal` does not see,\n\t// as `-0` becomes `0`. A principal with no such form gives `undefined`.\n\tconst canonicalOf = jsonKey(domain.principal)\n\tconst custom = options.cache === false ? undefined : options.cache?.key\n\t// It gives `undefined` when the principal has no key. Such a principal does\n\t// not use the cache.\n\tconst keyOf = (\n\t\tprincipal: object,\n\t\tcanonical: PrincipalKey | undefined,\n\t): PrincipalKey | undefined => {\n\t\tif (custom === undefined) {\n\t\t\treturn canonical\n\t\t}\n\t\tconst text = custom(principal)\n\t\treturn text === undefined\n\t\t\t? undefined\n\t\t\t: new PrincipalKey(canonical?.principal ?? principal, text, undefined)\n\t}\n\treturn Layer.effect(\n\t\tservice,\n\t\tEffect.gen(function* () {\n\t\t\t// The services of the policy loader and of the predicates come from the\n\t\t\t// layer, so a check does not need them.\n\t\t\tconst services = yield* Effect.context<never>()\n\n\t\t\t// The predicates of the policy get the services of the layer, so the\n\t\t\t// policy and its residual conditions need no services.\n\t\t\t// A policy of another domain may have the same types, but its resources\n\t\t\t// may have other paths. So it fails closed, as a policy that can not be\n\t\t\t// loaded.\n\t\t\tconst load = (principal: object) =>\n\t\t\t\toptions.policy(principal).pipe(\n\t\t\t\t\tEffect.provideContext(services),\n\t\t\t\t\tEffect.filterOrFail(\n\t\t\t\t\t\t(value) => value.domain === domain,\n\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\tnew globalThis.Error(\n\t\t\t\t\t\t\t\t\"The loaded policy is not a policy of this domain\",\n\t\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.mapError((cause) => PolicyUnavailable.make({ cause })),\n\t\t\t\t\tEffect.map((value) =>\n\t\t\t\t\t\tPolicies.provideContext(value, services as never),\n\t\t\t\t\t),\n\t\t\t\t)\n\n\t\t\tconst cache =\n\t\t\t\toptions.cache === false\n\t\t\t\t\t? undefined\n\t\t\t\t\t: yield* Cache.make({\n\t\t\t\t\t\t\tcapacity: options.cache?.capacity ?? 1000,\n\t\t\t\t\t\t\ttimeToLive: options.cache?.timeToLive ?? \"30 seconds\",\n\t\t\t\t\t\t\tlookup: (key: PrincipalKey) => load(key.principal),\n\t\t\t\t\t\t})\n\n\t\t\tconst policyFor = (principal: object) => {\n\t\t\t\tconst canonical = canonicalOf(principal)\n\t\t\t\tconst key =\n\t\t\t\t\tcache === undefined ? undefined : keyOf(principal, canonical)\n\t\t\t\tif (cache === undefined || key === undefined) {\n\t\t\t\t\treturn load(canonical?.principal ?? principal)\n\t\t\t\t}\n\t\t\t\t// A failed lookup is kept in the cache. Remove it, so the next check\n\t\t\t\t// tries again.\n\t\t\t\treturn Cache.get(cache, key).pipe(\n\t\t\t\t\tEffect.tapError(() => Cache.invalidate(cache, key)),\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tconst withServices = <A, E>(\n\t\t\t\teffect: Effect.Effect<A, E, unknown>,\n\t\t\t): Effect.Effect<A, E, unknown> => Effect.provideContext(effect, services)\n\n\t\t\tconst current = Effect.gen(function* () {\n\t\t\t\tconst principal = yield* service.principal\n\t\t\t\tconst value = yield* policyFor(principal)\n\t\t\t\treturn { principal, policy: value }\n\t\t\t})\n\n\t\t\t// The principal and its policy, loaded one time. A check that asks\n\t\t\t// the policy two times, such as `guard`, uses one snapshot for both,\n\t\t\t// so the two answers come from one principal.\n\t\t\ttype Snapshot = Effect.Success<typeof current>\n\n\t\t\tconst decideWith = Effect.fnUntraced(function* (\n\t\t\t\t{ principal, policy: value }: Snapshot,\n\t\t\t\ttarget: AnyTarget,\n\t\t\t) {\n\t\t\t\tconst decision: Decision.Decision = yield* withServices(\n\t\t\t\t\tDecide.decideAny(value, { ...target, principal } as never),\n\t\t\t\t)\n\t\t\t\treturn decision\n\t\t\t})\n\n\t\t\tconst decide = (target: AnyTarget) =>\n\t\t\t\tEffect.flatMap(current, (snapshot) => decideWith(snapshot, target))\n\n\t\t\tconst checkWith = Effect.fnUntraced(function* (\n\t\t\t\t{ principal, policy: value }: Snapshot,\n\t\t\t\ttarget: AnyTarget,\n\t\t\t) {\n\t\t\t\tconst kind =\n\t\t\t\t\ttarget.resource === undefined\n\t\t\t\t\t\t? (target.kind ?? \"\")\n\t\t\t\t\t\t: (ResourceData.kindOf(target.resource) ?? \"\")\n\t\t\t\t// A request that does not fit its kind gets a \"no\" at once. The\n\t\t\t\t// decider must not say yes to an action that the kind does not have.\n\t\t\t\tconst descriptor = DomainData.descriptorOf(domain, kind)\n\t\t\t\tif (descriptor === undefined || !Decide.fitsKind(descriptor, target)) {\n\t\t\t\t\treturn yield* forbidden(target, kind, undefined)\n\t\t\t\t}\n\t\t\t\tconst fromPolicy: Decision.Decision =\n\t\t\t\t\ttarget.resource === undefined\n\t\t\t\t\t\t? Analysis.decideKind(value, { ...target, principal } as never)\n\t\t\t\t\t\t: yield* withServices(\n\t\t\t\t\t\t\t\tDecide.decideAny(value, { ...target, principal } as never),\n\t\t\t\t\t\t\t)\n\t\t\t\t// When no rule gives an answer, the decider gives it.\n\t\t\t\tconst decision: Decision.Decision =\n\t\t\t\t\tfromPolicy._tag === \"NotApplicable\" && options.decider !== undefined\n\t\t\t\t\t\t? yield* options.decider({ ...target, principal })\n\t\t\t\t\t\t: fromPolicy\n\t\t\t\tif (decision._tag !== \"Allow\") {\n\t\t\t\t\treturn yield* forbidden(target, kind, Decision.reason(decision))\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tconst check = (target: AnyTarget) =>\n\t\t\t\tEffect.flatMap(current, (snapshot) => checkWith(snapshot, target))\n\n\t\t\tconst guard = Effect.fnUntraced(function* (\n\t\t\t\ttarget: AnyTarget,\n\t\t\t\tguardOptions: {\n\t\t\t\t\treadonly read?: string\n\t\t\t\t\treadonly hide: () => unknown\n\t\t\t\t},\n\t\t\t) {\n\t\t\t\tconst snapshot = yield* current\n\t\t\t\tconst read = guardOptions.read ?? \"read\"\n\t\t\t\tconst { field: _field, ...rest } = target\n\t\t\t\tconst visible: Decision.Decision = yield* decideWith(snapshot, {\n\t\t\t\t\t...rest,\n\t\t\t\t\taction: read,\n\t\t\t\t})\n\t\t\t\tif (visible._tag !== \"Allow\") {\n\t\t\t\t\treturn yield* Effect.fail(guardOptions.hide())\n\t\t\t\t}\n\t\t\t\tif (target.action !== read || target.field !== undefined) {\n\t\t\t\t\tyield* checkWith(snapshot, target)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\t// The filter is the condition of the coverage: the scope for `All`,\n\t\t\t// `False` for `None`, and else the residual condition in the scope.\n\t\t\t// The residual throws an error when it is too large, so the filter\n\t\t\t// fails.\n\t\t\tconst filter = (\n\t\t\t\ttarget: AnyTarget,\n\t\t\t): Effect.Effect<Condition.Ast, unknown, unknown> =>\n\t\t\t\tEffect.map(\n\t\t\t\t\tcurrent,\n\t\t\t\t\t({ principal, policy: value }): Condition.Ast =>\n\t\t\t\t\t\tAnalysis.coverageAny(value, { ...target, principal } as never)\n\t\t\t\t\t\t\t.condition as Condition.Ast,\n\t\t\t\t)\n\n\t\t\tconst coverage = (\n\t\t\t\ttarget: AnyTarget,\n\t\t\t): Effect.Effect<Analysis.AnyCoverage, unknown, unknown> =>\n\t\t\t\tEffect.map(current, ({ principal, policy: value }) =>\n\t\t\t\t\tAnalysis.coverageAny(value, { ...target, principal } as never),\n\t\t\t\t)\n\n\t\t\tconst permittedFields = Effect.fnUntraced(function* (target: AnyTarget) {\n\t\t\t\tconst { principal, policy: value } = yield* current\n\t\t\t\treturn yield* withServices(\n\t\t\t\t\tDecide.permittedFields(value, { ...target, principal } as never),\n\t\t\t\t)\n\t\t\t})\n\n\t\t\tconst invalidate = (principal?: object) => {\n\t\t\t\tif (cache === undefined) {\n\t\t\t\t\treturn Effect.void\n\t\t\t\t}\n\t\t\t\tif (principal === undefined) {\n\t\t\t\t\treturn Cache.invalidateAll(cache)\n\t\t\t\t}\n\t\t\t\tconst key = keyOf(principal, canonicalOf(principal))\n\t\t\t\treturn key === undefined ? Effect.void : Cache.invalidate(cache, key)\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tdomain,\n\t\t\t\tprincipal: service.principal,\n\t\t\t\tpolicy: Effect.map(current, ({ policy: value }) => value),\n\t\t\t\tdecide,\n\t\t\t\tcheck,\n\t\t\t\tguard,\n\t\t\t\tfilter,\n\t\t\t\tcoverage,\n\t\t\t\tpermittedFields,\n\t\t\t\tinvalidate,\n\t\t\t} as never\n\t\t}),\n\t)\n}\n"],"mappings":"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"}
|
package/dist/internal/bind.js
CHANGED
|
@@ -4,11 +4,10 @@ import { accepts, anyKinds, lookup } from "./catalog.js";
|
|
|
4
4
|
import { fitsCatalog, parse } from "./patterns.js";
|
|
5
5
|
import { at, pathOf } from "./place.js";
|
|
6
6
|
import { withSuggestion } from "./suggest.js";
|
|
7
|
+
import * as HashMap from "effect/HashMap";
|
|
7
8
|
import * as Result from "effect/Result";
|
|
8
9
|
import * as Arr from "effect/Array";
|
|
9
10
|
import * as Option from "effect/Option";
|
|
10
|
-
import * as HashMap from "effect/HashMap";
|
|
11
|
-
import * as MutableHashMap from "effect/MutableHashMap";
|
|
12
11
|
// src/internal/bind.ts
|
|
13
12
|
/**
|
|
14
13
|
* The binding of rules to a domain. It is the one check of a rule, for a rule
|
|
@@ -18,7 +17,7 @@ import * as MutableHashMap from "effect/MutableHashMap";
|
|
|
18
17
|
* the name that it may mean.
|
|
19
18
|
*/
|
|
20
19
|
const makeMemo = () => ({
|
|
21
|
-
suggestions:
|
|
20
|
+
suggestions: /* @__PURE__ */ new Map(),
|
|
22
21
|
fits: /* @__PURE__ */ new Map(),
|
|
23
22
|
issues: {
|
|
24
23
|
count: 0,
|
|
@@ -38,11 +37,11 @@ const maxSuggestions = 32;
|
|
|
38
37
|
/** Adds a suggestion to a message, with the memo of the binding. */
|
|
39
38
|
const suggestIn = (memo, message, name, scope, names) => {
|
|
40
39
|
const key = `${scope}\u0000${name}`;
|
|
41
|
-
const known =
|
|
42
|
-
if (
|
|
43
|
-
if (
|
|
40
|
+
const known = memo.suggestions.get(key);
|
|
41
|
+
if (known !== void 0) return known;
|
|
42
|
+
if (memo.suggestions.size >= maxSuggestions) return message;
|
|
44
43
|
const made = withSuggestion(message, name, names());
|
|
45
|
-
|
|
44
|
+
memo.suggestions.set(key, made);
|
|
46
45
|
return made;
|
|
47
46
|
};
|
|
48
47
|
/**
|
|
@@ -296,7 +295,7 @@ const ruleIssues = (domain, rule, at, memo = makeMemo()) => {
|
|
|
296
295
|
applicable,
|
|
297
296
|
paths: all ? Arr.filter(known, (descriptor) => hasAction(descriptor, rule.actions)) : applicable
|
|
298
297
|
};
|
|
299
|
-
const actions =
|
|
298
|
+
const actions = Array.from(new Set(Arr.flatMap(known, (descriptor) => descriptor.actions)));
|
|
300
299
|
return [
|
|
301
300
|
...Arr.map(Arr.filter(names, (kind) => !HashMap.has(domain.resources, kind)), (kind) => ({
|
|
302
301
|
path: [...at, "resources"],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bind.js","names":["Catalog.accepts","Catalog.lookup","Patterns.parse","Patterns.fitsCatalog","Limits.searchSteps","Catalog.anyKinds","Nodes.isNode","Place.pathOf","Place.at"],"sources":["../../src/internal/bind.ts"],"sourcesContent":["/**\n * The binding of rules to a domain. It is the one check of a rule, for a rule\n * from the builder, from `Policy.rule`, and from JSON alike. It reads the\n * catalogs of the resources and of the principal, and it gives one issue for\n * each problem, with the path of the problem and, for a name with a mistake,\n * the name that it may mean.\n */\nimport * as Arr from \"effect/Array\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as MutableHashMap from \"effect/MutableHashMap\"\nimport * as Option from \"effect/Option\"\nimport * as Result from \"effect/Result\"\nimport type * as Domain from \"../Domain.ts\"\nimport type * as Resource from \"../Resource.ts\"\nimport * as Catalog from \"./catalog.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport * as Patterns from \"./patterns.ts\"\nimport * as Place from \"./place.ts\"\nimport { withSuggestion } from \"./suggest.ts\"\n\n/** One problem of a rule or a policy, with its path. */\nexport interface Issue {\n\treadonly path: ReadonlyArray<PropertyKey>\n\treadonly issue: string\n}\n\n/** The data of a rule that the binding reads. */\nexport interface RuleData {\n\treadonly actions: ReadonlyArray<string>\n\treadonly resources: ReadonlyArray<string>\n\treadonly when?: unknown\n\treadonly fields?: ReadonlyArray<string>\n}\n\n/**\n * The memo of one binding, for all its rules. A policy may name one wrong\n * field or one pattern in very many places, so the work on each is done one\n * time. It goes away when the binding ends.\n */\ninterface Memo {\n\t/** The suggestion for each name, by the place where it was looked for. */\n\treadonly suggestions: MutableHashMap.MutableHashMap<string, string>\n\t/**\n\t * If a pattern matches a field of a kind, by the kind and the pattern. The\n\t * patterns come from a policy, so the map is native: the hash of a string\n\t * in Effect has no random seed, and a policy could hold very many\n\t * patterns with one hash. The engine hashes a string with a random seed.\n\t */\n\treadonly fits: Map<string, boolean>\n\t/** The count of the issues of conditions so far. It belongs to one binding. */\n\treadonly issues: { count: number; noted: boolean }\n\t/** The steps of the checks of patterns so far. It belongs to one binding. */\n\treadonly work: { steps: number; noted: boolean }\n}\n\nconst makeMemo = (): Memo => ({\n\tsuggestions: MutableHashMap.empty(),\n\tfits: new Map(),\n\tissues: { count: 0, noted: false },\n\twork: { steps: 0, noted: false },\n})\n\n/**\n * The most names that one binding makes a suggestion for. A suggestion\n * compares the name with every name of a catalog, so a policy with very many\n * wrong names would take a long time.\n */\nconst maxSuggestions = 32\n\n/** Adds a suggestion to a message, with the memo of the binding. */\nconst suggestIn = (\n\tmemo: Memo,\n\tmessage: string,\n\tname: string,\n\tscope: string,\n\tnames: () => ReadonlyArray<string>,\n): string => {\n\tconst key = `${scope}\\u0000${name}`\n\tconst known = MutableHashMap.get(memo.suggestions, key)\n\tif (Option.isSome(known)) {\n\t\treturn known.value\n\t}\n\tif (MutableHashMap.size(memo.suggestions) >= maxSuggestions) {\n\t\treturn message\n\t}\n\tconst made = withSuggestion(message, name, names())\n\tMutableHashMap.set(memo.suggestions, key, made)\n\treturn made\n}\n\n/** The kinds that a rule may run on, from the view of its checks. */\ninterface Context {\n\treadonly domain: Domain.Any\n\treadonly memo: Memo\n\t/** The rule is for every resource (`\"*\"`). */\n\treadonly all: boolean\n\t/** The resources that the rule names and that the domain has. */\n\treadonly named: ReadonlyArray<Resource.Descriptor>\n\t/** The named resources that have one of the actions of the rule. */\n\treadonly applicable: ReadonlyArray<Resource.Descriptor>\n\t/**\n\t * The resources whose paths a path test may read: every resource with one\n\t * of the actions, for a rule for every resource.\n\t */\n\treadonly paths: ReadonlyArray<Resource.Descriptor>\n}\n\ntype Operand = Nodes.Operand\n\n/**\n * Tests if a field may hold a value of the type of a fixed value. A test with\n * `null` is a test that the field is missing, so `null` always fits. A number\n * fits any field of numbers: a test of an integer field with `1.5` or\n * `Infinity` has a meaning, as `lt` shows.\n */\nconst fitsLiteral = (kinds: Catalog.Kinds, value: unknown): boolean =>\n\tvalue === null ||\n\t(typeof value === \"number\" && kinds.number !== \"none\") ||\n\tCatalog.accepts(kinds, value)\n\n/**\n * Tests if two fields may hold one scalar. A principal field that is `null`\n * or that holds an object is a missing value, and a test with a missing value\n * is false, so only scalars count.\n */\nconst meet = (left: Catalog.Kinds, right: Catalog.Kinds): boolean =>\n\t(left.string && right.string) ||\n\t(left.boolean && right.boolean) ||\n\t(left.number !== \"none\" && right.number !== \"none\") ||\n\tArr.some(left.literals, (value) => Catalog.accepts(right, value)) ||\n\tArr.some(right.literals, (value) => Catalog.accepts(left, value))\n\nconst hasStrings = (kinds: Catalog.Kinds): boolean =>\n\tkinds.string || Arr.some(kinds.literals, (value) => typeof value === \"string\")\n\nconst hasNumbers = (kinds: Catalog.Kinds): boolean =>\n\tkinds.number !== \"none\" ||\n\tArr.some(kinds.literals, (value) => typeof value === \"number\")\n\n/** Tests if a field may hold a value with an order: a number or a string. */\nconst isOrdered = (kinds: Catalog.Kinds): boolean =>\n\thasStrings(kinds) || hasNumbers(kinds)\n\n/** Tests if two fields may hold values of one ordered type. */\nconst orderedMeet = (left: Catalog.Kinds, right: Catalog.Kinds): boolean =>\n\t(hasStrings(left) && hasStrings(right)) ||\n\t(hasNumbers(left) && hasNumbers(right))\n\n/** The texts of the fields of a catalog that have only names, for suggestions. */\nconst namesOf = (catalog: Catalog.Catalog): ReadonlyArray<string> =>\n\tArr.map(\n\t\tArr.filter(catalog.fields, (field) =>\n\t\t\tArr.every(field.path, (segment) => typeof segment === \"string\"),\n\t\t),\n\t\t(field) => field.text,\n\t)\n\n/** The issue of a field that is not a field of every resource of the rule. */\nconst fieldIssue = (\n\tcontext: Context,\n\tfield: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst message = `The field \"${field}\" is not a field of every resource of the rule`\n\tif (context.all || context.named.length === 0) {\n\t\treturn [{ path: at, issue: message }]\n\t}\n\tconst without = Arr.filter(\n\t\tcontext.named,\n\t\t(descriptor) => Catalog.lookup(descriptor.catalog, field) === undefined,\n\t)\n\treturn without.length === 0\n\t\t? []\n\t\t: [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: suggestIn(context.memo, message, field, without[0]!.kind, () =>\n\t\t\t\t\t\tnamesOf(without[0]!.catalog),\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t]\n}\n\n/**\n * The issues of a field that a condition reads: a field that is not a field\n * of every resource of the rule, or a field that is not plain data in one of\n * them. A read of a value that is not plain data, as a getter of a class, is\n * a defect in a decision. A residual may leave the read out, so the residual\n * would say \"yes\" where the decision fails. So the binding does not take\n * such a read: see `Catalog.Field.plain`.\n */\nconst readIssues = (\n\tcontext: Context,\n\tfield: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst exists = fieldIssue(context, field, at)\n\tif (exists.length > 0) {\n\t\treturn exists\n\t}\n\treturn Arr.some(\n\t\tcontext.named,\n\t\t(descriptor) => !(Catalog.lookup(descriptor.catalog, field)?.plain ?? true),\n\t)\n\t\t? [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: `A condition may not read the field \"${field}\". A value above it may be an object that does not have it as a data field, as a class with a getter, a date or a list. Use a predicate for it.`,\n\t\t\t\t},\n\t\t\t]\n\t\t: []\n}\n\n/**\n * The issues of a field pattern: a pattern that does not parse, or that\n * matches no field of the resources of the rule, as a pattern with a mistake\n * in a name does.\n */\nconst patternIssues = (\n\tmemo: Memo,\n\tknown: ReadonlyArray<Resource.Descriptor>,\n\tpattern: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> =>\n\tResult.match(Patterns.parse(pattern), {\n\t\tonFailure: (issue) => [{ path: at, issue }],\n\t\tonSuccess: (parsed) => {\n\t\t\t// A rule with no known resource has an issue for that already. The\n\t\t\t// pattern is checked on every kind, not only up to the first that it\n\t\t\t// fits: a decision on a kind does this check, so the limit of the\n\t\t\t// work below is a limit of the work of a decision too.\n\t\t\tconst fits =\n\t\t\t\tknown.length === 0 ||\n\t\t\t\tArr.reduce(known, false, (before, descriptor) => {\n\t\t\t\t\tconst key = `${descriptor.kind}\\u0000${pattern}`\n\t\t\t\t\treturn (\n\t\t\t\t\t\tOption.getOrElse(Option.fromUndefinedOr(memo.fits.get(key)), () => {\n\t\t\t\t\t\t\t// Each check of a pattern reads the fields of a catalog. The\n\t\t\t\t\t\t\t// work of one binding has a limit, so very many patterns stop\n\t\t\t\t\t\t\t// with one issue, and the policy does not load.\n\t\t\t\t\t\t\tmemo.work.steps +=\n\t\t\t\t\t\t\t\tdescriptor.catalog.fields.length * Math.max(parsed.length, 1)\n\t\t\t\t\t\t\tif (memo.work.steps > Limits.searchSteps) {\n\t\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst found = Patterns.fitsCatalog(parsed, descriptor.catalog)\n\t\t\t\t\t\t\tmemo.fits.set(key, found)\n\t\t\t\t\t\t\treturn found\n\t\t\t\t\t\t}) || before\n\t\t\t\t\t)\n\t\t\t\t})\n\t\t\tif (memo.work.steps > Limits.searchSteps && !memo.work.noted) {\n\t\t\t\tmemo.work.noted = true\n\t\t\t\treturn [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\tissue: `The field patterns of the policy take more than ${String(Limits.searchSteps)} steps to check. The most is ${String(Limits.searchSteps)}.`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t}\n\t\t\treturn fits\n\t\t\t\t? []\n\t\t\t\t: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field pattern \"${pattern}\" matches no field of the resources of the rule`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t},\n\t})\n\n/** The fields of the resources that a test of the rule may read. */\nconst fieldsAt = (\n\tcontext: Context,\n\tfield: string,\n): ReadonlyArray<Catalog.Field> =>\n\tArr.flatMap(context.applicable, (descriptor) => {\n\t\tconst found = Catalog.lookup(descriptor.catalog, field)\n\t\treturn found === undefined ? [] : [found]\n\t})\n\n/** The field of the principal that an operand names, or the issue when there is none. */\nconst principalField = (\n\tcontext: Context,\n\tref: string,\n\tat: ReadonlyArray<PropertyKey>,\n): Catalog.Field | Issue => {\n\tconst found = Catalog.lookup(context.domain.principalCatalog, ref)\n\t// A read of the principal that is not plain data is a defect, as a read\n\t// of a resource is: see `readIssues`.\n\tif (found !== undefined && !found.plain) {\n\t\treturn {\n\t\t\tpath: at,\n\t\t\tissue: `A condition may not read the field \"${ref}\" of the principal. A value above it may be an object that does not have it as a data field, as a class with a getter, a date or a list. Use a predicate for it.`,\n\t\t}\n\t}\n\treturn found !== undefined\n\t\t? found\n\t\t: {\n\t\t\t\tpath: at,\n\t\t\t\tissue: suggestIn(\n\t\t\t\t\tcontext.memo,\n\t\t\t\t\t`The principal has no field \"${ref}\"`,\n\t\t\t\t\tref,\n\t\t\t\t\t\"~principal\",\n\t\t\t\t\t() => namesOf(context.domain.principalCatalog),\n\t\t\t\t),\n\t\t\t}\n}\n\nconst isIssue = (value: Catalog.Field | Issue): value is Issue =>\n\t\"issue\" in value\n\n/** The issues of one operand of a test of a field. */\nconst operandIssues = (\n\tcontext: Context,\n\tfield: string,\n\tordered: boolean,\n\toperand: Operand,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst fields = fieldsAt(context, field)\n\tif (operand._tag === \"Literal\") {\n\t\tconst value = operand.value\n\t\tif (ordered && typeof value !== \"string\" && typeof value !== \"number\") {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t},\n\t\t\t]\n\t\t}\n\t\treturn [\n\t\t\t...(Arr.some(fields, (found) => !fitsLiteral(found.kinds, value))\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field \"${field}\" can not hold the value ${JSON.stringify(value)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []),\n\t\t\t...(ordered && Arr.some(fields, (found) => !isOrdered(found.kinds))\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []),\n\t\t]\n\t}\n\tconst principal = principalField(context, operand.path, at)\n\tif (isIssue(principal)) {\n\t\treturn [principal]\n\t}\n\treturn [\n\t\t...(Arr.some(fields, (found) => !meet(found.kinds, principal.kinds))\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\tissue: `The field \"${field}\" can not hold a value of the principal field \"${operand.path}\"`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []),\n\t\t...(ordered &&\n\t\tArr.some(fields, (found) => !orderedMeet(found.kinds, principal.kinds))\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []),\n\t]\n}\n\n/**\n * Tests if the path of a resource may start with a prefix: it is as long as\n * the prefix, and each fixed string of the prefix is the string of the path\n * at its place, when the path has a string there.\n */\nconst mayStartWith = (\n\tdescriptor: Resource.Descriptor,\n\tstrings: ReadonlyArray<string | null>,\n): boolean =>\n\t(descriptor.path?.length ?? 0) >= strings.length &&\n\tArr.every(strings, (string, index) => {\n\t\tconst segment = descriptor.path?.[index]\n\t\treturn string === null || typeof segment !== \"string\" || segment === string\n\t})\n\n/** The kinds of the part of the path of a resource at a place. */\nconst pathPartKinds = (\n\tdescriptor: Resource.Descriptor,\n\tindex: number,\n): Catalog.Kinds | string | undefined => {\n\tconst segment = descriptor.path?.[index]\n\treturn segment === undefined || typeof segment === \"string\"\n\t\t? segment\n\t\t: (Catalog.lookup(descriptor.catalog, segment.field)?.kinds ??\n\t\t\t\tCatalog.anyKinds)\n}\n\n/** The issues of the prefix of a path test. */\nconst underIssues = (\n\tcontext: Context,\n\tprefix: ReadonlyArray<Operand>,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst strings = Arr.map(prefix, (operand) =>\n\t\toperand._tag === \"Literal\" && typeof operand.value === \"string\"\n\t\t\t? operand.value\n\t\t\t: null,\n\t)\n\t// The test can match only a path that starts with the fixed strings of\n\t// the prefix, so the other resources are left out.\n\tconst candidates = Arr.filter(context.paths, (descriptor) =>\n\t\tmayStartWith(descriptor, strings),\n\t)\n\treturn Arr.flatMap(prefix, (operand, index): ReadonlyArray<Issue> => {\n\t\tconst place = [...at, \"prefix\", index]\n\t\tif (operand._tag === \"Literal\") {\n\t\t\treturn Arr.some(candidates, (descriptor) => {\n\t\t\t\tconst kinds = pathPartKinds(descriptor, index)\n\t\t\t\treturn typeof kinds === \"object\" && !fitsLiteral(kinds, operand.value)\n\t\t\t})\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: place,\n\t\t\t\t\t\t\tissue: `The path part ${String(index)} can not hold the value ${JSON.stringify(operand.value)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []\n\t\t}\n\t\tconst principal = principalField(context, operand.path, place)\n\t\tif (isIssue(principal)) {\n\t\t\treturn [principal]\n\t\t}\n\t\treturn Arr.some(candidates, (descriptor) => {\n\t\t\tconst kinds = pathPartKinds(descriptor, index)\n\t\t\treturn kinds === undefined\n\t\t\t\t? true\n\t\t\t\t: typeof kinds === \"string\"\n\t\t\t\t\t? !Catalog.accepts(principal.kinds, kinds)\n\t\t\t\t\t: !meet(kinds, principal.kinds)\n\t\t})\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: place,\n\t\t\t\t\t\tissue: `The principal field \"${operand.path}\" can not hold a part of the path of the resource`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []\n\t})\n}\n\nconst notWhole = (at: ReadonlyArray<PropertyKey>): Issue => ({\n\tpath: at,\n\tissue: \"The condition is not a whole condition tree\",\n})\n\n/**\n * The most issues that the walk of the conditions of one binding gives. A\n * policy may have very many wrong tests, and each issue keeps its path.\n */\nconst maxIssues = 100\n\n/** A path that the walk makes only when it needs it, for an issue. */\ntype LazyPath = () => ReadonlyArray<PropertyKey>\n\n/**\n * Puts the path of a node before the paths of its issues. A walk keeps no\n * path for a node with no issue, so a deep and wide condition costs little.\n */\nconst under = (\n\tmemo: Memo,\n\tat: LazyPath,\n\tissues: ReadonlyArray<Issue>,\n): ReadonlyArray<Issue> => {\n\tif (issues.length === 0) {\n\t\treturn issues\n\t}\n\tconst room = maxIssues - memo.issues.count\n\tmemo.issues.count += issues.length\n\tconst kept = Arr.map(Arr.take(issues, Math.max(room, 0)), (issue) => ({\n\t\tpath: [...at(), ...issue.path],\n\t\tissue: issue.issue,\n\t}))\n\tconst first = memo.issues.count > maxIssues && !memo.issues.noted\n\tmemo.issues.noted ||= first\n\treturn first\n\t\t? [\n\t\t\t\t...kept,\n\t\t\t\t{\n\t\t\t\t\tpath: [],\n\t\t\t\t\tissue: `The policy has more problems. The first ${String(maxIssues)} are here.`,\n\t\t\t\t},\n\t\t\t]\n\t\t: kept\n}\n\n/**\n * The issues of a condition. The constructors of the nodes checked the shape\n * of each node, so this checks the names and the types against the domain. A\n * node that the condition uses in many places is checked one time. The nodes\n * it has seen are in a native `Set`, because it must find a node by the node\n * itself: `Equal` compares nodes by structure.\n */\nconst conditionIssues = (\n\tcontext: Context,\n\tnode: Nodes.Node,\n\tat: LazyPath,\n\tseen: Set<Nodes.Node>,\n): ReadonlyArray<Issue> => {\n\tif (seen.has(node)) {\n\t\treturn []\n\t}\n\tseen.add(node)\n\tconst here = (issues: ReadonlyArray<Issue>) => under(context.memo, at, issues)\n\tswitch (node._tag) {\n\t\tcase \"Compare\": {\n\t\t\tconst exists = readIssues(context, node.field, [\"field\"])\n\t\t\treturn here(\n\t\t\t\texists.length > 0\n\t\t\t\t\t? exists\n\t\t\t\t\t: operandIssues(\n\t\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t\tnode.field,\n\t\t\t\t\t\t\tnode.op !== \"eq\" && node.op !== \"ne\",\n\t\t\t\t\t\t\tnode.value,\n\t\t\t\t\t\t\t[\"value\"],\n\t\t\t\t\t\t),\n\t\t\t)\n\t\t}\n\t\tcase \"In\": {\n\t\t\tconst exists = readIssues(context, node.field, [\"field\"])\n\t\t\treturn here(\n\t\t\t\texists.length > 0\n\t\t\t\t\t? exists\n\t\t\t\t\t: Arr.flatMap(node.values, (operand, index) =>\n\t\t\t\t\t\t\toperandIssues(context, node.field, false, operand, [\n\t\t\t\t\t\t\t\t\"values\",\n\t\t\t\t\t\t\t\tindex,\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t),\n\t\t\t)\n\t\t}\n\t\tcase \"Exists\":\n\t\t\treturn here(readIssues(context, node.field, [\"field\"]))\n\t\tcase \"Under\":\n\t\t\treturn here(underIssues(context, node.prefix, []))\n\t\tcase \"And\":\n\t\tcase \"Or\":\n\t\t\treturn Arr.flatMap(node.conditions, (child, index) =>\n\t\t\t\tconditionIssues(\n\t\t\t\t\tcontext,\n\t\t\t\t\tchild,\n\t\t\t\t\t() => [...at(), \"conditions\", index],\n\t\t\t\t\tseen,\n\t\t\t\t),\n\t\t\t)\n\t\tcase \"Not\":\n\t\t\treturn conditionIssues(\n\t\t\t\tcontext,\n\t\t\t\tnode.condition,\n\t\t\t\t() => [...at(), \"condition\"],\n\t\t\t\tseen,\n\t\t\t)\n\t\tdefault:\n\t\t\treturn []\n\t}\n}\n\nconst hasAction = (\n\tdescriptor: Resource.Descriptor,\n\tactions: ReadonlyArray<string>,\n): boolean =>\n\tactions.includes(\"*\") ||\n\tArr.some(actions, (action) => descriptor.actions.includes(action))\n\n/**\n * Checks one rule against a domain. It gives one issue for each problem. `at`\n * is the path of the rule, for the paths of the issues.\n */\nexport const ruleIssues = (\n\tdomain: Domain.Any,\n\trule: RuleData,\n\tat: ReadonlyArray<PropertyKey>,\n\tmemo: Memo = makeMemo(),\n): ReadonlyArray<Issue> => {\n\tconst all = rule.resources.includes(\"*\")\n\t// Each kind is checked one time, also when the list names it again. A\n\t// native `Set` finds the copies in one pass: `Arr.dedupe` compares each\n\t// name with each name before it, and a stored rule may name very many.\n\tconst names = Array.from(\n\t\tnew Set(Arr.filter(rule.resources, (kind) => kind !== \"*\")),\n\t)\n\tconst kinds = Array.from(HashMap.keys(domain.resources))\n\tconst named = Arr.flatMap(names, (kind) =>\n\t\tOption.toArray(HashMap.get(domain.resources, kind)),\n\t)\n\tconst known = all ? Array.from(HashMap.values(domain.resources)) : named\n\t// The rule never runs on a kind without one of its actions, so the types\n\t// of its tests are checked only on the kinds with one.\n\tconst applicable = Arr.filter(named, (descriptor) =>\n\t\thasAction(descriptor, rule.actions),\n\t)\n\tconst context: Context = {\n\t\tdomain,\n\t\tmemo,\n\t\tall,\n\t\tnamed,\n\t\tapplicable,\n\t\tpaths: all\n\t\t\t? Arr.filter(known, (descriptor) => hasAction(descriptor, rule.actions))\n\t\t\t: applicable,\n\t}\n\tconst actions = Arr.dedupe(\n\t\tArr.flatMap(known, (descriptor) => descriptor.actions),\n\t)\n\treturn [\n\t\t...Arr.map(\n\t\t\tArr.filter(names, (kind) => !HashMap.has(domain.resources, kind)),\n\t\t\t(kind): Issue => ({\n\t\t\t\tpath: [...at, \"resources\"],\n\t\t\t\tissue: suggestIn(\n\t\t\t\t\tmemo,\n\t\t\t\t\t`There is no resource with the kind \"${kind}\"`,\n\t\t\t\t\tkind,\n\t\t\t\t\t\"~kinds\",\n\t\t\t\t\t() => kinds,\n\t\t\t\t),\n\t\t\t}),\n\t\t),\n\t\t...Arr.map(\n\t\t\tArr.filter(\n\t\t\t\trule.actions,\n\t\t\t\t(action) => action !== \"*\" && !actions.includes(action),\n\t\t\t),\n\t\t\t(action): Issue => ({\n\t\t\t\tpath: [...at, \"actions\"],\n\t\t\t\tissue: suggestIn(\n\t\t\t\t\tmemo,\n\t\t\t\t\t`The action \"${action}\" is not an action of these resources`,\n\t\t\t\t\taction,\n\t\t\t\t\t`~actions\\u0000${names.join(\",\")}`,\n\t\t\t\t\t() => actions,\n\t\t\t\t),\n\t\t\t}),\n\t\t),\n\t\t...Arr.flatMap(rule.fields ?? [], (field, index) =>\n\t\t\tfield.includes(\"*\")\n\t\t\t\t? patternIssues(memo, known, field, [...at, \"fields\", index])\n\t\t\t\t: fieldIssue(context, field, [...at, \"fields\", index]),\n\t\t),\n\t\t...(rule.when === undefined\n\t\t\t? []\n\t\t\t: Nodes.isNode(rule.when)\n\t\t\t\t? conditionIssues(context, rule.when, () => [...at, \"when\"], new Set())\n\t\t\t\t: [notWhole([...at, \"when\"])]),\n\t]\n}\n\n/** The JSON form of a policy tree, or a policy tree, as the binding reads it. */\ntype TreeNode =\n\t| { readonly _tag: \"Rules\"; readonly rules: ReadonlyArray<RuleData> }\n\t| {\n\t\t\treadonly _tag: \"All\" | \"Any\" | \"FirstOf\"\n\t\t\treadonly policies: ReadonlyArray<TreeNode>\n\t }\n\t| {\n\t\t\treadonly _tag: \"Ceiling\"\n\t\t\treadonly cap: TreeNode\n\t\t\treadonly policy: TreeNode\n\t }\n\n/** Checks each rule of a policy tree against a domain. */\nexport const treeIssues = (\n\tdomain: Domain.Any,\n\troot: TreeNode,\n): ReadonlyArray<Issue> => {\n\t// The rules come out of the tree first, and each one is checked after.\n\t// A check of a condition goes down its levels, so a check inside the walk\n\t// of the tree would go down the levels of both.\n\t// The place of each rule shares the path of its parent, and the full path\n\t// is made only when its rule is checked. A deep tree with very many rules\n\t// would need very much memory for a full copy of the path of each rule.\n\tconst rulesOf = (\n\t\tnode: TreeNode,\n\t\tplace: Place.Place | undefined,\n\t\tlevel: number,\n\t): ReadonlyArray<Issue | readonly [RuleData, Place.Place]> => {\n\t\tif (level > Limits.policyDepth) {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tpath: Place.pathOf(place),\n\t\t\t\t\tissue: `A policy tree has more than ${String(Limits.policyDepth)} levels. The most is ${String(Limits.policyDepth)}.`,\n\t\t\t\t},\n\t\t\t]\n\t\t}\n\t\tswitch (node._tag) {\n\t\t\tcase \"Rules\":\n\t\t\t\treturn Arr.map(\n\t\t\t\t\tnode.rules,\n\t\t\t\t\t(rule, index) => [rule, Place.at(place, \"rules\", index)] as const,\n\t\t\t\t)\n\t\t\tcase \"All\":\n\t\t\tcase \"Any\":\n\t\t\tcase \"FirstOf\":\n\t\t\t\treturn Arr.flatMap(node.policies, (child, index) =>\n\t\t\t\t\trulesOf(child, Place.at(place, \"policies\", index), level + 1),\n\t\t\t\t)\n\t\t\tcase \"Ceiling\":\n\t\t\t\treturn [\n\t\t\t\t\t...rulesOf(node.cap, Place.at(place, \"cap\"), level + 1),\n\t\t\t\t\t...rulesOf(node.policy, Place.at(place, \"policy\"), level + 1),\n\t\t\t\t]\n\t\t}\n\t}\n\tconst memo = makeMemo()\n\treturn Arr.flatMap(rulesOf(root, undefined, 1), (found) =>\n\t\tArray.isArray(found)\n\t\t\t? ruleIssues(domain, found[0], Place.pathOf(found[1]), memo)\n\t\t\t: [found as Issue],\n\t)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bind.js","names":["Catalog.accepts","Catalog.lookup","Patterns.parse","Patterns.fitsCatalog","Limits.searchSteps","Catalog.anyKinds","Nodes.isNode","Place.pathOf","Place.at"],"sources":["../../src/internal/bind.ts"],"sourcesContent":["/**\n * The binding of rules to a domain. It is the one check of a rule, for a rule\n * from the builder, from `Policy.rule`, and from JSON alike. It reads the\n * catalogs of the resources and of the principal, and it gives one issue for\n * each problem, with the path of the problem and, for a name with a mistake,\n * the name that it may mean.\n */\nimport * as Arr from \"effect/Array\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as Option from \"effect/Option\"\nimport * as Result from \"effect/Result\"\nimport type * as Domain from \"../Domain.ts\"\nimport type * as Resource from \"../Resource.ts\"\nimport * as Catalog from \"./catalog.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport * as Patterns from \"./patterns.ts\"\nimport * as Place from \"./place.ts\"\nimport { withSuggestion } from \"./suggest.ts\"\n\n/** One problem of a rule or a policy, with its path. */\nexport interface Issue {\n\treadonly path: ReadonlyArray<PropertyKey>\n\treadonly issue: string\n}\n\n/** The data of a rule that the binding reads. */\nexport interface RuleData {\n\treadonly actions: ReadonlyArray<string>\n\treadonly resources: ReadonlyArray<string>\n\treadonly when?: unknown\n\treadonly fields?: ReadonlyArray<string>\n}\n\n/**\n * The memo of one binding, for all its rules. A policy may name one wrong\n * field or one pattern in very many places, so the work on each is done one\n * time. It goes away when the binding ends.\n */\ninterface Memo {\n\t/**\n\t * The suggestion for each name, by the place where it was looked for. The\n\t * names come from a policy, so the map is native, as `fits` is.\n\t */\n\treadonly suggestions: Map<string, string>\n\t/**\n\t * If a pattern matches a field of a kind, by the kind and the pattern. The\n\t * patterns come from a policy, so the map is native: the hash of a string\n\t * in Effect has no random seed, and a policy could hold very many\n\t * patterns with one hash. The engine hashes a string with a random seed.\n\t */\n\treadonly fits: Map<string, boolean>\n\t/** The count of the issues of conditions so far. It belongs to one binding. */\n\treadonly issues: { count: number; noted: boolean }\n\t/** The steps of the checks of patterns so far. It belongs to one binding. */\n\treadonly work: { steps: number; noted: boolean }\n}\n\nconst makeMemo = (): Memo => ({\n\tsuggestions: new Map(),\n\tfits: new Map(),\n\tissues: { count: 0, noted: false },\n\twork: { steps: 0, noted: false },\n})\n\n/**\n * The most names that one binding makes a suggestion for. A suggestion\n * compares the name with every name of a catalog, so a policy with very many\n * wrong names would take a long time.\n */\nconst maxSuggestions = 32\n\n/** Adds a suggestion to a message, with the memo of the binding. */\nconst suggestIn = (\n\tmemo: Memo,\n\tmessage: string,\n\tname: string,\n\tscope: string,\n\tnames: () => ReadonlyArray<string>,\n): string => {\n\tconst key = `${scope}\\u0000${name}`\n\tconst known = memo.suggestions.get(key)\n\tif (known !== undefined) {\n\t\treturn known\n\t}\n\tif (memo.suggestions.size >= maxSuggestions) {\n\t\treturn message\n\t}\n\tconst made = withSuggestion(message, name, names())\n\tmemo.suggestions.set(key, made)\n\treturn made\n}\n\n/** The kinds that a rule may run on, from the view of its checks. */\ninterface Context {\n\treadonly domain: Domain.Any\n\treadonly memo: Memo\n\t/** The rule is for every resource (`\"*\"`). */\n\treadonly all: boolean\n\t/** The resources that the rule names and that the domain has. */\n\treadonly named: ReadonlyArray<Resource.Descriptor>\n\t/** The named resources that have one of the actions of the rule. */\n\treadonly applicable: ReadonlyArray<Resource.Descriptor>\n\t/**\n\t * The resources whose paths a path test may read: every resource with one\n\t * of the actions, for a rule for every resource.\n\t */\n\treadonly paths: ReadonlyArray<Resource.Descriptor>\n}\n\ntype Operand = Nodes.Operand\n\n/**\n * Tests if a field may hold a value of the type of a fixed value. A test with\n * `null` is a test that the field is missing, so `null` always fits. A number\n * fits any field of numbers: a test of an integer field with `1.5` or\n * `Infinity` has a meaning, as `lt` shows.\n */\nconst fitsLiteral = (kinds: Catalog.Kinds, value: unknown): boolean =>\n\tvalue === null ||\n\t(typeof value === \"number\" && kinds.number !== \"none\") ||\n\tCatalog.accepts(kinds, value)\n\n/**\n * Tests if two fields may hold one scalar. A principal field that is `null`\n * or that holds an object is a missing value, and a test with a missing value\n * is false, so only scalars count.\n */\nconst meet = (left: Catalog.Kinds, right: Catalog.Kinds): boolean =>\n\t(left.string && right.string) ||\n\t(left.boolean && right.boolean) ||\n\t(left.number !== \"none\" && right.number !== \"none\") ||\n\tArr.some(left.literals, (value) => Catalog.accepts(right, value)) ||\n\tArr.some(right.literals, (value) => Catalog.accepts(left, value))\n\nconst hasStrings = (kinds: Catalog.Kinds): boolean =>\n\tkinds.string || Arr.some(kinds.literals, (value) => typeof value === \"string\")\n\nconst hasNumbers = (kinds: Catalog.Kinds): boolean =>\n\tkinds.number !== \"none\" ||\n\tArr.some(kinds.literals, (value) => typeof value === \"number\")\n\n/** Tests if a field may hold a value with an order: a number or a string. */\nconst isOrdered = (kinds: Catalog.Kinds): boolean =>\n\thasStrings(kinds) || hasNumbers(kinds)\n\n/** Tests if two fields may hold values of one ordered type. */\nconst orderedMeet = (left: Catalog.Kinds, right: Catalog.Kinds): boolean =>\n\t(hasStrings(left) && hasStrings(right)) ||\n\t(hasNumbers(left) && hasNumbers(right))\n\n/** The texts of the fields of a catalog that have only names, for suggestions. */\nconst namesOf = (catalog: Catalog.Catalog): ReadonlyArray<string> =>\n\tArr.map(\n\t\tArr.filter(catalog.fields, (field) =>\n\t\t\tArr.every(field.path, (segment) => typeof segment === \"string\"),\n\t\t),\n\t\t(field) => field.text,\n\t)\n\n/** The issue of a field that is not a field of every resource of the rule. */\nconst fieldIssue = (\n\tcontext: Context,\n\tfield: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst message = `The field \"${field}\" is not a field of every resource of the rule`\n\tif (context.all || context.named.length === 0) {\n\t\treturn [{ path: at, issue: message }]\n\t}\n\tconst without = Arr.filter(\n\t\tcontext.named,\n\t\t(descriptor) => Catalog.lookup(descriptor.catalog, field) === undefined,\n\t)\n\treturn without.length === 0\n\t\t? []\n\t\t: [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: suggestIn(context.memo, message, field, without[0]!.kind, () =>\n\t\t\t\t\t\tnamesOf(without[0]!.catalog),\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t]\n}\n\n/**\n * The issues of a field that a condition reads: a field that is not a field\n * of every resource of the rule, or a field that is not plain data in one of\n * them. A read of a value that is not plain data, as a getter of a class, is\n * a defect in a decision. A residual may leave the read out, so the residual\n * would say \"yes\" where the decision fails. So the binding does not take\n * such a read: see `Catalog.Field.plain`.\n */\nconst readIssues = (\n\tcontext: Context,\n\tfield: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst exists = fieldIssue(context, field, at)\n\tif (exists.length > 0) {\n\t\treturn exists\n\t}\n\treturn Arr.some(\n\t\tcontext.named,\n\t\t(descriptor) => !(Catalog.lookup(descriptor.catalog, field)?.plain ?? true),\n\t)\n\t\t? [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: `A condition may not read the field \"${field}\". A value above it may be an object that does not have it as a data field, as a class with a getter, a date or a list. Use a predicate for it.`,\n\t\t\t\t},\n\t\t\t]\n\t\t: []\n}\n\n/**\n * The issues of a field pattern: a pattern that does not parse, or that\n * matches no field of the resources of the rule, as a pattern with a mistake\n * in a name does.\n */\nconst patternIssues = (\n\tmemo: Memo,\n\tknown: ReadonlyArray<Resource.Descriptor>,\n\tpattern: string,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> =>\n\tResult.match(Patterns.parse(pattern), {\n\t\tonFailure: (issue) => [{ path: at, issue }],\n\t\tonSuccess: (parsed) => {\n\t\t\t// A rule with no known resource has an issue for that already. The\n\t\t\t// pattern is checked on every kind, not only up to the first that it\n\t\t\t// fits: a decision on a kind does this check, so the limit of the\n\t\t\t// work below is a limit of the work of a decision too.\n\t\t\tconst fits =\n\t\t\t\tknown.length === 0 ||\n\t\t\t\tArr.reduce(known, false, (before, descriptor) => {\n\t\t\t\t\tconst key = `${descriptor.kind}\\u0000${pattern}`\n\t\t\t\t\treturn (\n\t\t\t\t\t\tOption.getOrElse(Option.fromUndefinedOr(memo.fits.get(key)), () => {\n\t\t\t\t\t\t\t// Each check of a pattern reads the fields of a catalog. The\n\t\t\t\t\t\t\t// work of one binding has a limit, so very many patterns stop\n\t\t\t\t\t\t\t// with one issue, and the policy does not load.\n\t\t\t\t\t\t\tmemo.work.steps +=\n\t\t\t\t\t\t\t\tdescriptor.catalog.fields.length * Math.max(parsed.length, 1)\n\t\t\t\t\t\t\tif (memo.work.steps > Limits.searchSteps) {\n\t\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst found = Patterns.fitsCatalog(parsed, descriptor.catalog)\n\t\t\t\t\t\t\tmemo.fits.set(key, found)\n\t\t\t\t\t\t\treturn found\n\t\t\t\t\t\t}) || before\n\t\t\t\t\t)\n\t\t\t\t})\n\t\t\tif (memo.work.steps > Limits.searchSteps && !memo.work.noted) {\n\t\t\t\tmemo.work.noted = true\n\t\t\t\treturn [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\tissue: `The field patterns of the policy take more than ${String(Limits.searchSteps)} steps to check. The most is ${String(Limits.searchSteps)}.`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t}\n\t\t\treturn fits\n\t\t\t\t? []\n\t\t\t\t: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field pattern \"${pattern}\" matches no field of the resources of the rule`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t},\n\t})\n\n/** The fields of the resources that a test of the rule may read. */\nconst fieldsAt = (\n\tcontext: Context,\n\tfield: string,\n): ReadonlyArray<Catalog.Field> =>\n\tArr.flatMap(context.applicable, (descriptor) => {\n\t\tconst found = Catalog.lookup(descriptor.catalog, field)\n\t\treturn found === undefined ? [] : [found]\n\t})\n\n/** The field of the principal that an operand names, or the issue when there is none. */\nconst principalField = (\n\tcontext: Context,\n\tref: string,\n\tat: ReadonlyArray<PropertyKey>,\n): Catalog.Field | Issue => {\n\tconst found = Catalog.lookup(context.domain.principalCatalog, ref)\n\t// A read of the principal that is not plain data is a defect, as a read\n\t// of a resource is: see `readIssues`.\n\tif (found !== undefined && !found.plain) {\n\t\treturn {\n\t\t\tpath: at,\n\t\t\tissue: `A condition may not read the field \"${ref}\" of the principal. A value above it may be an object that does not have it as a data field, as a class with a getter, a date or a list. Use a predicate for it.`,\n\t\t}\n\t}\n\treturn found !== undefined\n\t\t? found\n\t\t: {\n\t\t\t\tpath: at,\n\t\t\t\tissue: suggestIn(\n\t\t\t\t\tcontext.memo,\n\t\t\t\t\t`The principal has no field \"${ref}\"`,\n\t\t\t\t\tref,\n\t\t\t\t\t\"~principal\",\n\t\t\t\t\t() => namesOf(context.domain.principalCatalog),\n\t\t\t\t),\n\t\t\t}\n}\n\nconst isIssue = (value: Catalog.Field | Issue): value is Issue =>\n\t\"issue\" in value\n\n/** The issues of one operand of a test of a field. */\nconst operandIssues = (\n\tcontext: Context,\n\tfield: string,\n\tordered: boolean,\n\toperand: Operand,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst fields = fieldsAt(context, field)\n\tif (operand._tag === \"Literal\") {\n\t\tconst value = operand.value\n\t\tif (ordered && typeof value !== \"string\" && typeof value !== \"number\") {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tpath: at,\n\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t},\n\t\t\t]\n\t\t}\n\t\treturn [\n\t\t\t...(Arr.some(fields, (found) => !fitsLiteral(found.kinds, value))\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field \"${field}\" can not hold the value ${JSON.stringify(value)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []),\n\t\t\t...(ordered && Arr.some(fields, (found) => !isOrdered(found.kinds))\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []),\n\t\t]\n\t}\n\tconst principal = principalField(context, operand.path, at)\n\tif (isIssue(principal)) {\n\t\treturn [principal]\n\t}\n\treturn [\n\t\t...(Arr.some(fields, (found) => !meet(found.kinds, principal.kinds))\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\tissue: `The field \"${field}\" can not hold a value of the principal field \"${operand.path}\"`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []),\n\t\t...(ordered &&\n\t\tArr.some(fields, (found) => !orderedMeet(found.kinds, principal.kinds))\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\tissue: `The field \"${field}\" has an ordered test with a value that has no order`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []),\n\t]\n}\n\n/**\n * Tests if the path of a resource may start with a prefix: it is as long as\n * the prefix, and each fixed string of the prefix is the string of the path\n * at its place, when the path has a string there.\n */\nconst mayStartWith = (\n\tdescriptor: Resource.Descriptor,\n\tstrings: ReadonlyArray<string | null>,\n): boolean =>\n\t(descriptor.path?.length ?? 0) >= strings.length &&\n\tArr.every(strings, (string, index) => {\n\t\tconst segment = descriptor.path?.[index]\n\t\treturn string === null || typeof segment !== \"string\" || segment === string\n\t})\n\n/** The kinds of the part of the path of a resource at a place. */\nconst pathPartKinds = (\n\tdescriptor: Resource.Descriptor,\n\tindex: number,\n): Catalog.Kinds | string | undefined => {\n\tconst segment = descriptor.path?.[index]\n\treturn segment === undefined || typeof segment === \"string\"\n\t\t? segment\n\t\t: (Catalog.lookup(descriptor.catalog, segment.field)?.kinds ??\n\t\t\t\tCatalog.anyKinds)\n}\n\n/** The issues of the prefix of a path test. */\nconst underIssues = (\n\tcontext: Context,\n\tprefix: ReadonlyArray<Operand>,\n\tat: ReadonlyArray<PropertyKey>,\n): ReadonlyArray<Issue> => {\n\tconst strings = Arr.map(prefix, (operand) =>\n\t\toperand._tag === \"Literal\" && typeof operand.value === \"string\"\n\t\t\t? operand.value\n\t\t\t: null,\n\t)\n\t// The test can match only a path that starts with the fixed strings of\n\t// the prefix, so the other resources are left out.\n\tconst candidates = Arr.filter(context.paths, (descriptor) =>\n\t\tmayStartWith(descriptor, strings),\n\t)\n\treturn Arr.flatMap(prefix, (operand, index): ReadonlyArray<Issue> => {\n\t\tconst place = [...at, \"prefix\", index]\n\t\tif (operand._tag === \"Literal\") {\n\t\t\treturn Arr.some(candidates, (descriptor) => {\n\t\t\t\tconst kinds = pathPartKinds(descriptor, index)\n\t\t\t\treturn typeof kinds === \"object\" && !fitsLiteral(kinds, operand.value)\n\t\t\t})\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpath: place,\n\t\t\t\t\t\t\tissue: `The path part ${String(index)} can not hold the value ${JSON.stringify(operand.value)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []\n\t\t}\n\t\tconst principal = principalField(context, operand.path, place)\n\t\tif (isIssue(principal)) {\n\t\t\treturn [principal]\n\t\t}\n\t\treturn Arr.some(candidates, (descriptor) => {\n\t\t\tconst kinds = pathPartKinds(descriptor, index)\n\t\t\treturn kinds === undefined\n\t\t\t\t? true\n\t\t\t\t: typeof kinds === \"string\"\n\t\t\t\t\t? !Catalog.accepts(principal.kinds, kinds)\n\t\t\t\t\t: !meet(kinds, principal.kinds)\n\t\t})\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: place,\n\t\t\t\t\t\tissue: `The principal field \"${operand.path}\" can not hold a part of the path of the resource`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []\n\t})\n}\n\nconst notWhole = (at: ReadonlyArray<PropertyKey>): Issue => ({\n\tpath: at,\n\tissue: \"The condition is not a whole condition tree\",\n})\n\n/**\n * The most issues that the walk of the conditions of one binding gives. A\n * policy may have very many wrong tests, and each issue keeps its path.\n */\nconst maxIssues = 100\n\n/** A path that the walk makes only when it needs it, for an issue. */\ntype LazyPath = () => ReadonlyArray<PropertyKey>\n\n/**\n * Puts the path of a node before the paths of its issues. A walk keeps no\n * path for a node with no issue, so a deep and wide condition costs little.\n */\nconst under = (\n\tmemo: Memo,\n\tat: LazyPath,\n\tissues: ReadonlyArray<Issue>,\n): ReadonlyArray<Issue> => {\n\tif (issues.length === 0) {\n\t\treturn issues\n\t}\n\tconst room = maxIssues - memo.issues.count\n\tmemo.issues.count += issues.length\n\tconst kept = Arr.map(Arr.take(issues, Math.max(room, 0)), (issue) => ({\n\t\tpath: [...at(), ...issue.path],\n\t\tissue: issue.issue,\n\t}))\n\tconst first = memo.issues.count > maxIssues && !memo.issues.noted\n\tmemo.issues.noted ||= first\n\treturn first\n\t\t? [\n\t\t\t\t...kept,\n\t\t\t\t{\n\t\t\t\t\tpath: [],\n\t\t\t\t\tissue: `The policy has more problems. The first ${String(maxIssues)} are here.`,\n\t\t\t\t},\n\t\t\t]\n\t\t: kept\n}\n\n/**\n * The issues of a condition. The constructors of the nodes checked the shape\n * of each node, so this checks the names and the types against the domain. A\n * node that the condition uses in many places is checked one time. The nodes\n * it has seen are in a native `Set`, because it must find a node by the node\n * itself: `Equal` compares nodes by structure.\n */\nconst conditionIssues = (\n\tcontext: Context,\n\tnode: Nodes.Node,\n\tat: LazyPath,\n\tseen: Set<Nodes.Node>,\n): ReadonlyArray<Issue> => {\n\tif (seen.has(node)) {\n\t\treturn []\n\t}\n\tseen.add(node)\n\tconst here = (issues: ReadonlyArray<Issue>) => under(context.memo, at, issues)\n\tswitch (node._tag) {\n\t\tcase \"Compare\": {\n\t\t\tconst exists = readIssues(context, node.field, [\"field\"])\n\t\t\treturn here(\n\t\t\t\texists.length > 0\n\t\t\t\t\t? exists\n\t\t\t\t\t: operandIssues(\n\t\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t\tnode.field,\n\t\t\t\t\t\t\tnode.op !== \"eq\" && node.op !== \"ne\",\n\t\t\t\t\t\t\tnode.value,\n\t\t\t\t\t\t\t[\"value\"],\n\t\t\t\t\t\t),\n\t\t\t)\n\t\t}\n\t\tcase \"In\": {\n\t\t\tconst exists = readIssues(context, node.field, [\"field\"])\n\t\t\treturn here(\n\t\t\t\texists.length > 0\n\t\t\t\t\t? exists\n\t\t\t\t\t: Arr.flatMap(node.values, (operand, index) =>\n\t\t\t\t\t\t\toperandIssues(context, node.field, false, operand, [\n\t\t\t\t\t\t\t\t\"values\",\n\t\t\t\t\t\t\t\tindex,\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t),\n\t\t\t)\n\t\t}\n\t\tcase \"Exists\":\n\t\t\treturn here(readIssues(context, node.field, [\"field\"]))\n\t\tcase \"Under\":\n\t\t\treturn here(underIssues(context, node.prefix, []))\n\t\tcase \"And\":\n\t\tcase \"Or\":\n\t\t\treturn Arr.flatMap(node.conditions, (child, index) =>\n\t\t\t\tconditionIssues(\n\t\t\t\t\tcontext,\n\t\t\t\t\tchild,\n\t\t\t\t\t() => [...at(), \"conditions\", index],\n\t\t\t\t\tseen,\n\t\t\t\t),\n\t\t\t)\n\t\tcase \"Not\":\n\t\t\treturn conditionIssues(\n\t\t\t\tcontext,\n\t\t\t\tnode.condition,\n\t\t\t\t() => [...at(), \"condition\"],\n\t\t\t\tseen,\n\t\t\t)\n\t\tdefault:\n\t\t\treturn []\n\t}\n}\n\nconst hasAction = (\n\tdescriptor: Resource.Descriptor,\n\tactions: ReadonlyArray<string>,\n): boolean =>\n\tactions.includes(\"*\") ||\n\tArr.some(actions, (action) => descriptor.actions.includes(action))\n\n/**\n * Checks one rule against a domain. It gives one issue for each problem. `at`\n * is the path of the rule, for the paths of the issues.\n */\nexport const ruleIssues = (\n\tdomain: Domain.Any,\n\trule: RuleData,\n\tat: ReadonlyArray<PropertyKey>,\n\tmemo: Memo = makeMemo(),\n): ReadonlyArray<Issue> => {\n\tconst all = rule.resources.includes(\"*\")\n\t// Each kind is checked one time, also when the list names it again. The\n\t// names come from a policy, so a native `Set` finds the copies:\n\t// `Arr.dedupe` puts the names in groups by the hash of Effect, which has\n\t// no random seed, and very many names with one hash make one long group.\n\tconst names = Array.from(\n\t\tnew Set(Arr.filter(rule.resources, (kind) => kind !== \"*\")),\n\t)\n\tconst kinds = Array.from(HashMap.keys(domain.resources))\n\tconst named = Arr.flatMap(names, (kind) =>\n\t\tOption.toArray(HashMap.get(domain.resources, kind)),\n\t)\n\tconst known = all ? Array.from(HashMap.values(domain.resources)) : named\n\t// The rule never runs on a kind without one of its actions, so the types\n\t// of its tests are checked only on the kinds with one.\n\tconst applicable = Arr.filter(named, (descriptor) =>\n\t\thasAction(descriptor, rule.actions),\n\t)\n\tconst context: Context = {\n\t\tdomain,\n\t\tmemo,\n\t\tall,\n\t\tnamed,\n\t\tapplicable,\n\t\tpaths: all\n\t\t\t? Arr.filter(known, (descriptor) => hasAction(descriptor, rule.actions))\n\t\t\t: applicable,\n\t}\n\t// A native `Set` finds copies about ten times as fast as `Arr.dedupe`.\n\tconst actions = Array.from(\n\t\tnew Set(Arr.flatMap(known, (descriptor) => descriptor.actions)),\n\t)\n\treturn [\n\t\t...Arr.map(\n\t\t\tArr.filter(names, (kind) => !HashMap.has(domain.resources, kind)),\n\t\t\t(kind): Issue => ({\n\t\t\t\tpath: [...at, \"resources\"],\n\t\t\t\tissue: suggestIn(\n\t\t\t\t\tmemo,\n\t\t\t\t\t`There is no resource with the kind \"${kind}\"`,\n\t\t\t\t\tkind,\n\t\t\t\t\t\"~kinds\",\n\t\t\t\t\t() => kinds,\n\t\t\t\t),\n\t\t\t}),\n\t\t),\n\t\t...Arr.map(\n\t\t\tArr.filter(\n\t\t\t\trule.actions,\n\t\t\t\t(action) => action !== \"*\" && !actions.includes(action),\n\t\t\t),\n\t\t\t(action): Issue => ({\n\t\t\t\tpath: [...at, \"actions\"],\n\t\t\t\tissue: suggestIn(\n\t\t\t\t\tmemo,\n\t\t\t\t\t`The action \"${action}\" is not an action of these resources`,\n\t\t\t\t\taction,\n\t\t\t\t\t`~actions\\u0000${names.join(\",\")}`,\n\t\t\t\t\t() => actions,\n\t\t\t\t),\n\t\t\t}),\n\t\t),\n\t\t...Arr.flatMap(rule.fields ?? [], (field, index) =>\n\t\t\tfield.includes(\"*\")\n\t\t\t\t? patternIssues(memo, known, field, [...at, \"fields\", index])\n\t\t\t\t: fieldIssue(context, field, [...at, \"fields\", index]),\n\t\t),\n\t\t...(rule.when === undefined\n\t\t\t? []\n\t\t\t: Nodes.isNode(rule.when)\n\t\t\t\t? conditionIssues(context, rule.when, () => [...at, \"when\"], new Set())\n\t\t\t\t: [notWhole([...at, \"when\"])]),\n\t]\n}\n\n/** The JSON form of a policy tree, or a policy tree, as the binding reads it. */\ntype TreeNode =\n\t| { readonly _tag: \"Rules\"; readonly rules: ReadonlyArray<RuleData> }\n\t| {\n\t\t\treadonly _tag: \"All\" | \"Any\" | \"FirstOf\"\n\t\t\treadonly policies: ReadonlyArray<TreeNode>\n\t }\n\t| {\n\t\t\treadonly _tag: \"Ceiling\"\n\t\t\treadonly cap: TreeNode\n\t\t\treadonly policy: TreeNode\n\t }\n\n/** Checks each rule of a policy tree against a domain. */\nexport const treeIssues = (\n\tdomain: Domain.Any,\n\troot: TreeNode,\n): ReadonlyArray<Issue> => {\n\t// The rules come out of the tree first, and each one is checked after.\n\t// A check of a condition goes down its levels, so a check inside the walk\n\t// of the tree would go down the levels of both.\n\t// The place of each rule shares the path of its parent, and the full path\n\t// is made only when its rule is checked. A deep tree with very many rules\n\t// would need very much memory for a full copy of the path of each rule.\n\tconst rulesOf = (\n\t\tnode: TreeNode,\n\t\tplace: Place.Place | undefined,\n\t\tlevel: number,\n\t): ReadonlyArray<Issue | readonly [RuleData, Place.Place]> => {\n\t\tif (level > Limits.policyDepth) {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tpath: Place.pathOf(place),\n\t\t\t\t\tissue: `A policy tree has more than ${String(Limits.policyDepth)} levels. The most is ${String(Limits.policyDepth)}.`,\n\t\t\t\t},\n\t\t\t]\n\t\t}\n\t\tswitch (node._tag) {\n\t\t\tcase \"Rules\":\n\t\t\t\treturn Arr.map(\n\t\t\t\t\tnode.rules,\n\t\t\t\t\t(rule, index) => [rule, Place.at(place, \"rules\", index)] as const,\n\t\t\t\t)\n\t\t\tcase \"All\":\n\t\t\tcase \"Any\":\n\t\t\tcase \"FirstOf\":\n\t\t\t\treturn Arr.flatMap(node.policies, (child, index) =>\n\t\t\t\t\trulesOf(child, Place.at(place, \"policies\", index), level + 1),\n\t\t\t\t)\n\t\t\tcase \"Ceiling\":\n\t\t\t\treturn [\n\t\t\t\t\t...rulesOf(node.cap, Place.at(place, \"cap\"), level + 1),\n\t\t\t\t\t...rulesOf(node.policy, Place.at(place, \"policy\"), level + 1),\n\t\t\t\t]\n\t\t}\n\t}\n\tconst memo = makeMemo()\n\treturn Arr.flatMap(rulesOf(root, undefined, 1), (found) =>\n\t\tArray.isArray(found)\n\t\t\t? ruleIssues(domain, found[0], Place.pathOf(found[1]), memo)\n\t\t\t: [found as Issue],\n\t)\n}\n"],"mappings":"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"}
|
|
@@ -51,6 +51,12 @@ export interface Branch {
|
|
|
51
51
|
readonly field: Field | undefined;
|
|
52
52
|
/** The places below, by name, in the order of the Schema. */
|
|
53
53
|
readonly children: ReadonlyArray<readonly [string, Branch]>;
|
|
54
|
+
/**
|
|
55
|
+
* The places below, by name. A lookup gets each name from `split`, as a
|
|
56
|
+
* new string, and a `HashMap` finds a new string about 20% faster than a
|
|
57
|
+
* native `Map`. The names come from the Schema, so a name from a policy
|
|
58
|
+
* can not make a long list of names with one hash here.
|
|
59
|
+
*/
|
|
54
60
|
readonly names: HashMap.HashMap<string, Branch>;
|
|
55
61
|
/** The place for the keys of a record, when the value may be a record. */
|
|
56
62
|
readonly key: Key | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.d.ts","names":[],"sources":["../../src/internal/catalog.ts"],"mappings":";;;iBAsBiB;;WAEP;WACA;;WAEA;;WAEA;;WAEA;;WAEA,UAAU;;;
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","names":[],"sources":["../../src/internal/catalog.ts"],"mappings":";;;iBAsBiB;;WAEP;WACA;;WAEA;;WAEA;;WAEA;;WAEA,UAAU;;;iBA8EH;WACP;;WAEA,YAAY,cAAc,UAAU;;;YAIlC,mBAAmB;;iBAGd;WACP,MAAM,cAAc;;WAEpB;WACA,OAAO;;;;;WAKP;;WAEA;;;;;;;;;WASA;;;iBAIO;;WAEP,OAAO;;WAEP,UAAU,gCAAgC;;;;;;;WAO1C,OAAO,QAAQ,gBAAgB;;WAE/B,KAAK;WACL,WAAW;;;iBAIJ;;WAEP,QAAQ,cAAc;WACtB,MAAM"}
|