@nmnmcc/ability 0.3.0 → 1.0.1

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.
Files changed (66) hide show
  1. package/README.md +199 -31
  2. package/dist/Authorizer.d.ts +64 -39
  3. package/dist/Authorizer.d.ts.map +1 -1
  4. package/dist/Authorizer.js +291 -6
  5. package/dist/Authorizer.js.map +1 -1
  6. package/dist/Condition.d.ts +444 -65
  7. package/dist/Condition.d.ts.map +1 -1
  8. package/dist/Condition.js +1007 -25
  9. package/dist/Condition.js.map +1 -1
  10. package/dist/Domain.d.ts +27 -17
  11. package/dist/Domain.d.ts.map +1 -1
  12. package/dist/Domain.js +52 -10
  13. package/dist/Domain.js.map +1 -1
  14. package/dist/HttpApi.d.ts +84 -40
  15. package/dist/HttpApi.d.ts.map +1 -1
  16. package/dist/HttpApi.js +103 -13
  17. package/dist/HttpApi.js.map +1 -1
  18. package/dist/Policy.d.ts +314 -56
  19. package/dist/Policy.d.ts.map +1 -1
  20. package/dist/Policy.js +1215 -38
  21. package/dist/Policy.js.map +1 -1
  22. package/dist/Principal.js +2 -2
  23. package/dist/Principal.js.map +1 -1
  24. package/dist/Resource.d.ts +171 -20
  25. package/dist/Resource.d.ts.map +1 -1
  26. package/dist/Resource.js +788 -15
  27. package/dist/Resource.js.map +1 -1
  28. package/dist/Rule.d.ts +115 -29
  29. package/dist/Rule.d.ts.map +1 -1
  30. package/dist/Rule.js +219 -12
  31. package/dist/Rule.js.map +1 -1
  32. package/dist/index.js +4 -4
  33. package/package.json +2 -2
  34. package/src/Authorizer.ts +625 -61
  35. package/src/Condition.ts +2403 -112
  36. package/src/Domain.ts +80 -23
  37. package/src/HttpApi.ts +225 -25
  38. package/src/Policy.ts +2836 -96
  39. package/src/Principal.ts +2 -2
  40. package/src/Resource.ts +1544 -29
  41. package/src/Rule.ts +463 -45
  42. package/dist/internal/authorizer.d.ts +0 -28
  43. package/dist/internal/authorizer.d.ts.map +0 -1
  44. package/dist/internal/authorizer.js +0 -153
  45. package/dist/internal/authorizer.js.map +0 -1
  46. package/dist/internal/condition.d.ts +0 -11
  47. package/dist/internal/condition.d.ts.map +0 -1
  48. package/dist/internal/condition.js +0 -393
  49. package/dist/internal/condition.js.map +0 -1
  50. package/dist/internal/domain.js +0 -34
  51. package/dist/internal/domain.js.map +0 -1
  52. package/dist/internal/httpApi.js +0 -41
  53. package/dist/internal/httpApi.js.map +0 -1
  54. package/dist/internal/policy.js +0 -524
  55. package/dist/internal/policy.js.map +0 -1
  56. package/dist/internal/resource.js +0 -66
  57. package/dist/internal/resource.js.map +0 -1
  58. package/dist/internal/rule.js +0 -131
  59. package/dist/internal/rule.js.map +0 -1
  60. package/src/internal/authorizer.ts +0 -271
  61. package/src/internal/condition.ts +0 -607
  62. package/src/internal/domain.ts +0 -56
  63. package/src/internal/httpApi.ts +0 -84
  64. package/src/internal/policy.ts +0 -815
  65. package/src/internal/resource.ts +0 -98
  66. package/src/internal/rule.ts +0 -203
@@ -1 +0,0 @@
1
- {"version":3,"file":"policy.js","names":["rule.toData","rule.builder","rule.matchesTarget","condition.evaluateOf","Decision.Allow","Decision.Deny","Decision.NotApplicable","condition","condition.and","condition.or","condition.not","condition.partialOf","condition.schema","condition.fields","rule.make"],"sources":["../../src/internal/policy.ts"],"sourcesContent":["// A policy keeps the error type and the service type of its predicates only in\n// the public types. Inside this file they are erased to `unknown`, so the\n// language service check for `unknown` in an Effect is off here.\n// @effect-diagnostics anyUnknownInErrorContext:off\nimport * as Effect from \"effect/Effect\"\nimport * as Equal from \"effect/Equal\"\nimport * as Formatter from \"effect/Formatter\"\nimport { dual } from \"effect/Function\"\nimport * as Hash from \"effect/Hash\"\nimport * as Inspectable from \"effect/Inspectable\"\nimport * as Pipeable from \"effect/Pipeable\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport * as SchemaTransformation from \"effect/SchemaTransformation\"\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 type * as Rule from \"../Rule.ts\"\nimport * as condition from \"./condition.ts\"\nimport { getDescriptor, kindOf } from \"./domain.ts\"\nimport * as rule from \"./rule.ts\"\n\n/** @internal */\nexport const TypeId = \"~@nmnmcc/ability/Policy\" as const\n\ntype Node = Policy.Node\ntype Ast = Condition.Ast\n\ninterface PolicyData {\n\treadonly domain: Domain.Any\n\treadonly node: Node\n}\n\n/** @internal */\nexport const encodeNode = (node: Node): Policy.EncodedNode => {\n\tswitch (node._tag) {\n\t\tcase \"Rules\":\n\t\t\treturn {\n\t\t\t\t_tag: \"Rules\",\n\t\t\t\talgorithm: node.algorithm,\n\t\t\t\trules: node.rules.map(\n\t\t\t\t\t(value) => rule.toData(value) as Policy.EncodedRule,\n\t\t\t\t),\n\t\t\t}\n\t\tcase \"All\":\n\t\tcase \"Any\":\n\t\tcase \"FirstOf\":\n\t\t\treturn { _tag: node._tag, policies: node.policies.map(encodeNode) }\n\t\tcase \"Ceiling\":\n\t\t\treturn {\n\t\t\t\t_tag: \"Ceiling\",\n\t\t\t\tcap: encodeNode(node.cap),\n\t\t\t\tpolicy: encodeNode(node.policy),\n\t\t\t}\n\t}\n}\n\nconst Proto = {\n\t...Pipeable.Prototype,\n\t[TypeId]: TypeId,\n\t[Equal.symbol](this: PolicyData, that: unknown): boolean {\n\t\treturn (\n\t\t\tisPolicy(that) &&\n\t\t\tthis.domain === (that as unknown as PolicyData).domain &&\n\t\t\tEqual.equals(this.node, (that as unknown as PolicyData).node)\n\t\t)\n\t},\n\t[Hash.symbol](this: PolicyData): number {\n\t\treturn Hash.hash(this.node)\n\t},\n\ttoJSON(this: PolicyData) {\n\t\treturn encodeNode(this.node)\n\t},\n\ttoString(this: PolicyData) {\n\t\treturn Formatter.format(encodeNode(this.node), { space: 2 })\n\t},\n\t[Inspectable.NodeInspectSymbol](this: PolicyData) {\n\t\treturn encodeNode(this.node)\n\t},\n}\n\n/** @internal */\nexport const isPolicy = (u: unknown): u is Policy.Policy<Domain.Any> =>\n\tPredicate.hasProperty(u, TypeId)\n\n/** @internal */\nexport const fromNode = (domain: Domain.Any, node: Node): any => {\n\tconst self = Object.create(Proto) as { domain: Domain.Any; node: Node }\n\tself.domain = domain\n\tself.node = node\n\treturn Object.freeze(self)\n}\n\nconst nodeOf = (policy: unknown): Node => (policy as PolicyData).node\nconst domainOf = (policy: unknown): Domain.Any => (policy as PolicyData).domain\n\n/** @internal */\nexport const rules = (\n\tdomain: Domain.Any,\n\tvalues: Iterable<Rule.Any>,\n\toptions?: { readonly algorithm?: Policy.Algorithm },\n): any =>\n\tfromNode(\n\t\tdomain,\n\t\tObject.freeze({\n\t\t\t_tag: \"Rules\",\n\t\t\talgorithm: options?.algorithm ?? \"denyOverrides\",\n\t\t\trules: Object.freeze(Array.from(values)),\n\t\t}),\n\t)\n\n/** @internal */\nexport const empty = (domain: Domain.Any): any => rules(domain, [])\n\n/** @internal */\nexport const define =\n\t(domain: Domain.Any, options?: { readonly algorithm?: Policy.Algorithm }) =>\n\t(body: (builder: unknown) => Generator<Rule.Any, unknown, unknown>): any => {\n\t\tconst collected: Array<Rule.Any> = []\n\t\tconst iterator = body(rule.builder(domain))\n\t\tlet state = iterator.next()\n\t\twhile (state.done !== true) {\n\t\t\tcollected.push(state.value)\n\t\t\tstate = iterator.next(state.value)\n\t\t}\n\t\treturn rules(domain, collected, options)\n\t}\n\nconst combine =\n\t(tag: \"All\" | \"Any\" | \"FirstOf\") =>\n\t(policies: ReadonlyArray<unknown>): any => {\n\t\tconst domain = domainOf(policies[0])\n\t\tfor (const policy of policies) {\n\t\t\tif (domainOf(policy) !== domain) {\n\t\t\t\tthrow new Error(\"Policies from two domains can not be put together\")\n\t\t\t}\n\t\t}\n\t\treturn fromNode(\n\t\t\tdomain,\n\t\t\tObject.freeze({\n\t\t\t\t_tag: tag,\n\t\t\t\tpolicies: Object.freeze(policies.map(nodeOf)),\n\t\t\t}),\n\t\t)\n\t}\n\n/** @internal */\nexport const all = combine(\"All\")\n/** @internal */\nexport const any = combine(\"Any\")\n/** @internal */\nexport const firstOf = combine(\"FirstOf\")\n\n/** @internal */\nexport const ceiling = dual(2, (policy: unknown, cap: unknown): any => {\n\tif (domainOf(policy) !== domainOf(cap)) {\n\t\tthrow new Error(\"Policies from two domains can not be put together\")\n\t}\n\treturn fromNode(\n\t\tdomainOf(policy),\n\t\tObject.freeze({\n\t\t\t_tag: \"Ceiling\",\n\t\t\tcap: nodeOf(cap),\n\t\t\tpolicy: nodeOf(policy),\n\t\t}),\n\t)\n})\n\n// Decisions\n\ninterface Context {\n\treadonly principal: object\n\treadonly action: string\n\treadonly kind: string\n\treadonly value: object\n\treadonly field: string | undefined\n\treadonly input: Condition.Input\n\treadonly trace: Array<Policy.Step> | undefined\n}\n\nconst ruleApplies = (\n\tvalue: Rule.Any,\n\tcontext: Context,\n\tpath: ReadonlyArray<number>,\n): Effect.Effect<boolean, unknown, unknown> => {\n\tif (!rule.matchesTarget(value, context.action, context.kind, context.field)) {\n\t\treturn Effect.succeed(false)\n\t}\n\tconst test =\n\t\tvalue.when === undefined\n\t\t\t? Effect.succeed(true)\n\t\t\t: condition.evaluateOf(value.when, context.input)\n\treturn context.trace === undefined\n\t\t? test\n\t\t: Effect.tap(test, (applies) =>\n\t\t\t\tEffect.sync(() => {\n\t\t\t\t\tcontext.trace!.push({ path, rule: value, applies })\n\t\t\t\t}),\n\t\t\t)\n}\n\nconst fromRule = (value: Rule.Any): Decision.Decision =>\n\tvalue._tag === \"Allow\"\n\t\t? Decision.Allow({ rule: value })\n\t\t: value.reason === undefined\n\t\t\t? Decision.Deny({ rule: value })\n\t\t\t: Decision.Deny({ rule: value, reason: value.reason })\n\nconst firstApplying = Effect.fnUntraced(function* (\n\tcandidates: ReadonlyArray<readonly [Rule.Any, number]>,\n\tcontext: Context,\n\tpath: ReadonlyArray<number>,\n): Effect.fn.Return<Rule.Any | undefined, unknown, unknown> {\n\tfor (const [value, index] of candidates) {\n\t\tif (yield* ruleApplies(value, context, [...path, index])) {\n\t\t\treturn value\n\t\t}\n\t}\n\treturn undefined\n})\n\nconst decideRules = Effect.fnUntraced(function* (\n\tnode: Policy.RulesNode,\n\tcontext: Context,\n\tpath: ReadonlyArray<number>,\n): Effect.fn.Return<Decision.Decision, unknown, unknown> {\n\tconst indexed = node.rules.map((value, index) => [value, index] as const)\n\tconst denies = indexed.filter(([value]) => value._tag === \"Deny\")\n\tconst allows = indexed.filter(([value]) => value._tag === \"Allow\")\n\tlet found: Rule.Any | undefined\n\tswitch (node.algorithm) {\n\t\tcase \"denyOverrides\":\n\t\t\tfound =\n\t\t\t\t(yield* firstApplying(denies, context, path)) ??\n\t\t\t\t(yield* firstApplying(allows, context, path))\n\t\t\tbreak\n\t\tcase \"permitOverrides\":\n\t\t\tfound =\n\t\t\t\t(yield* firstApplying(allows, context, path)) ??\n\t\t\t\t(yield* firstApplying(denies, context, path))\n\t\t\tbreak\n\t\tcase \"firstApplicable\":\n\t\t\tfound = yield* firstApplying(indexed, context, path)\n\t\t\tbreak\n\t\tcase \"lastApplicable\":\n\t\t\tfound = yield* firstApplying(indexed.slice().reverse(), context, path)\n\t\t\tbreak\n\t}\n\treturn found === undefined ? Decision.NotApplicable : fromRule(found)\n})\n\nconst decideNode = (\n\tnode: Node,\n\tcontext: Context,\n\tpath: ReadonlyArray<number>,\n): Effect.Effect<Decision.Decision, unknown, unknown> => {\n\tswitch (node._tag) {\n\t\tcase \"Rules\":\n\t\t\treturn decideRules(node, context, path)\n\t\tcase \"All\":\n\t\t\treturn Effect.gen(function* () {\n\t\t\t\tlet firstAllow: Decision.Decision | undefined\n\t\t\t\tlet allAllow = node.policies.length > 0\n\t\t\t\tfor (const [index, child] of node.policies.entries()) {\n\t\t\t\t\tconst result = yield* decideNode(child, context, [...path, index])\n\t\t\t\t\tif (result._tag === \"Deny\") {\n\t\t\t\t\t\treturn result\n\t\t\t\t\t}\n\t\t\t\t\tif (result._tag === \"Allow\") {\n\t\t\t\t\t\tfirstAllow ??= result\n\t\t\t\t\t} else {\n\t\t\t\t\t\tallAllow = false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn allAllow && firstAllow !== undefined\n\t\t\t\t\t? firstAllow\n\t\t\t\t\t: Decision.NotApplicable\n\t\t\t})\n\t\tcase \"Any\":\n\t\t\treturn Effect.gen(function* () {\n\t\t\t\tlet firstDeny: Decision.Decision | undefined\n\t\t\t\tfor (const [index, child] of node.policies.entries()) {\n\t\t\t\t\tconst result = yield* decideNode(child, context, [...path, index])\n\t\t\t\t\tif (result._tag === \"Allow\") {\n\t\t\t\t\t\treturn result\n\t\t\t\t\t}\n\t\t\t\t\tif (result._tag === \"Deny\") {\n\t\t\t\t\t\tfirstDeny ??= result\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn firstDeny ?? Decision.NotApplicable\n\t\t\t})\n\t\tcase \"FirstOf\":\n\t\t\treturn Effect.gen(function* () {\n\t\t\t\tfor (const [index, child] of node.policies.entries()) {\n\t\t\t\t\tconst result = yield* decideNode(child, context, [...path, index])\n\t\t\t\t\tif (result._tag !== \"NotApplicable\") {\n\t\t\t\t\t\treturn result\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn Decision.NotApplicable\n\t\t\t})\n\t\tcase \"Ceiling\":\n\t\t\treturn Effect.gen(function* () {\n\t\t\t\tconst capped = yield* decideNode(node.cap, context, [...path, 0])\n\t\t\t\tif (capped._tag === \"Deny\") {\n\t\t\t\t\treturn capped\n\t\t\t\t}\n\t\t\t\treturn yield* decideNode(node.policy, context, [...path, 1])\n\t\t\t})\n\t}\n}\n\nconst makeContext = (\n\tdomain: Domain.Any,\n\trequest: Policy.AnyRequest,\n\ttrace: Array<Policy.Step> | undefined,\n): Context | undefined => {\n\tconst kind = kindOf(request.resource)\n\tconst descriptor =\n\t\tkind === undefined ? undefined : getDescriptor(domain, kind)\n\tif (kind === undefined || descriptor === undefined) {\n\t\treturn undefined\n\t}\n\treturn {\n\t\tprincipal: request.principal,\n\t\taction: request.action,\n\t\tkind,\n\t\tvalue: request.resource,\n\t\tfield: request.field,\n\t\tinput: {\n\t\t\tvalue: request.resource,\n\t\t\tprincipal: request.principal,\n\t\t\tpath: descriptor.path,\n\t\t},\n\t\ttrace,\n\t}\n}\n\n/** @internal */\nexport const decideOf = (\n\tpolicy: unknown,\n\trequest: Policy.AnyRequest,\n): Effect.Effect<Decision.Decision, unknown, unknown> => {\n\tconst context = makeContext(domainOf(policy), request, undefined)\n\treturn context === undefined\n\t\t? Effect.succeed(Decision.NotApplicable)\n\t\t: decideNode(nodeOf(policy), context, [])\n}\n\n/** @internal */\nexport const decide = dual(2, decideOf)\n\n/** @internal */\nexport const decideSync = dual(\n\t2,\n\t(policy: unknown, request: Policy.AnyRequest): Decision.Decision =>\n\t\tEffect.runSync(\n\t\t\t// The public type of `decideSync` takes only policies with no\n\t\t\t// predicates, so this Effect has no error and needs no services.\n\t\t\t// @effect-diagnostics-next-line unsafeEffectTypeAssertion:off\n\t\t\tdecideOf(policy, request) as Effect.Effect<Decision.Decision>,\n\t\t),\n)\n\n/** @internal */\nexport const explainOf = (\n\tpolicy: unknown,\n\trequest: Policy.AnyRequest,\n): Effect.Effect<Policy.Explanation, unknown, unknown> => {\n\tconst trace: Array<Policy.Step> = []\n\tconst context = makeContext(domainOf(policy), request, trace)\n\tif (context === undefined) {\n\t\treturn Effect.succeed({ decision: Decision.NotApplicable, steps: [] })\n\t}\n\treturn Effect.map(decideNode(nodeOf(policy), context, []), (decision) => ({\n\t\tdecision,\n\t\tsteps: Object.freeze(trace),\n\t}))\n}\n\n/** @internal */\nexport const explain = dual(2, explainOf)\n\n/** @internal */\nexport const permittedFieldsOf = Effect.fnUntraced(function* (\n\tpolicy: unknown,\n\trequest: Omit<Policy.AnyRequest, \"field\">,\n): Effect.fn.Return<ReadonlyArray<string>, unknown, unknown> {\n\tconst domain = domainOf(policy)\n\tconst kind = kindOf(request.resource)\n\tconst descriptor =\n\t\tkind === undefined ? undefined : getDescriptor(domain, kind)\n\tif (descriptor === undefined) {\n\t\treturn []\n\t}\n\tconst output: Array<string> = []\n\tfor (const field of descriptor.fieldPaths) {\n\t\tconst result: Decision.Decision = yield* decideOf(policy, {\n\t\t\t...request,\n\t\t\tfield,\n\t\t})\n\t\tif (result._tag === \"Allow\") {\n\t\t\toutput.push(field)\n\t\t}\n\t}\n\treturn Object.freeze(output)\n})\n\n/** @internal */\nexport const permittedFields = dual(2, permittedFieldsOf)\n\n// Residual conditions\n\ninterface Pair {\n\treadonly allow: Ast\n\treadonly deny: Ast\n}\n\nconst { always, never, simplify } = condition\nconst and = (...values: ReadonlyArray<Ast>): Ast =>\n\tsimplify(condition.and(...values))\nconst or = (...values: ReadonlyArray<Ast>): Ast =>\n\tsimplify(condition.or(...values))\nconst not = (value: Ast): Ast => simplify(condition.not(value))\n\ninterface ResidualContext {\n\treadonly principal: object\n\treadonly action: string\n\treadonly kind: string\n\treadonly field: string | undefined\n\treadonly input: {\n\t\treadonly principal: object\n\t\treadonly path: Condition.Input[\"path\"]\n\t}\n}\n\nconst ruleCondition = (value: Rule.Any, context: ResidualContext): Ast =>\n\t!rule.matchesTarget(value, context.action, context.kind, context.field)\n\t\t? never\n\t\t: value.when === undefined\n\t\t\t? always\n\t\t\t: condition.partialOf(value.when, context.input)\n\n/**\n * Gives the conditions for \"the first rule that applies is an allow rule\" and\n * \"the first rule that applies is a deny rule\".\n */\nconst firstApplicableResidual = (\n\tvalues: ReadonlyArray<Rule.Any>,\n\tcontext: ResidualContext,\n): Pair => {\n\tlet allow: Ast = never\n\tlet deny: Ast = never\n\tfor (const value of values.slice().reverse()) {\n\t\tconst applies = ruleCondition(value, context)\n\t\tif (value._tag === \"Allow\") {\n\t\t\tallow = or(applies, allow)\n\t\t\tdeny = and(not(applies), deny)\n\t\t} else {\n\t\t\tdeny = or(applies, deny)\n\t\t\tallow = and(not(applies), allow)\n\t\t}\n\t}\n\treturn { allow, deny }\n}\n\nconst residualNode = (node: Node, context: ResidualContext): Pair => {\n\tswitch (node._tag) {\n\t\tcase \"Rules\": {\n\t\t\tswitch (node.algorithm) {\n\t\t\t\tcase \"denyOverrides\": {\n\t\t\t\t\tconst deny = or(\n\t\t\t\t\t\t...node.rules\n\t\t\t\t\t\t\t.filter((value) => value._tag === \"Deny\")\n\t\t\t\t\t\t\t.map((value) => ruleCondition(value, context)),\n\t\t\t\t\t)\n\t\t\t\t\tconst allow = and(\n\t\t\t\t\t\tor(\n\t\t\t\t\t\t\t...node.rules\n\t\t\t\t\t\t\t\t.filter((value) => value._tag === \"Allow\")\n\t\t\t\t\t\t\t\t.map((value) => ruleCondition(value, context)),\n\t\t\t\t\t\t),\n\t\t\t\t\t\tnot(deny),\n\t\t\t\t\t)\n\t\t\t\t\treturn { allow, deny }\n\t\t\t\t}\n\t\t\t\tcase \"permitOverrides\": {\n\t\t\t\t\tconst allow = or(\n\t\t\t\t\t\t...node.rules\n\t\t\t\t\t\t\t.filter((value) => value._tag === \"Allow\")\n\t\t\t\t\t\t\t.map((value) => ruleCondition(value, context)),\n\t\t\t\t\t)\n\t\t\t\t\tconst deny = and(\n\t\t\t\t\t\tor(\n\t\t\t\t\t\t\t...node.rules\n\t\t\t\t\t\t\t\t.filter((value) => value._tag === \"Deny\")\n\t\t\t\t\t\t\t\t.map((value) => ruleCondition(value, context)),\n\t\t\t\t\t\t),\n\t\t\t\t\t\tnot(allow),\n\t\t\t\t\t)\n\t\t\t\t\treturn { allow, deny }\n\t\t\t\t}\n\t\t\t\tcase \"firstApplicable\":\n\t\t\t\t\treturn firstApplicableResidual(node.rules, context)\n\t\t\t\tcase \"lastApplicable\":\n\t\t\t\t\treturn firstApplicableResidual(node.rules.slice().reverse(), context)\n\t\t\t}\n\t\t}\n\t\t// falls through: every algorithm returns above\n\t\tcase \"All\": {\n\t\t\tif (node.policies.length === 0) {\n\t\t\t\treturn { allow: never, deny: never }\n\t\t\t}\n\t\t\tconst pairs = node.policies.map((child) => residualNode(child, context))\n\t\t\tconst deny = or(...pairs.map((pair) => pair.deny))\n\t\t\tconst allow = and(...pairs.map((pair) => pair.allow), not(deny))\n\t\t\treturn { allow, deny }\n\t\t}\n\t\tcase \"Any\": {\n\t\t\tconst pairs = node.policies.map((child) => residualNode(child, context))\n\t\t\tconst allow = or(...pairs.map((pair) => pair.allow))\n\t\t\tconst deny = and(or(...pairs.map((pair) => pair.deny)), not(allow))\n\t\t\treturn { allow, deny }\n\t\t}\n\t\tcase \"FirstOf\": {\n\t\t\tlet allow: Ast = never\n\t\t\tlet deny: Ast = never\n\t\t\tfor (const child of node.policies.slice().reverse()) {\n\t\t\t\tconst pair = residualNode(child, context)\n\t\t\t\tconst nextAllow = or(pair.allow, and(not(pair.deny), allow))\n\t\t\t\tdeny = or(pair.deny, and(not(pair.allow), deny))\n\t\t\t\tallow = nextAllow\n\t\t\t}\n\t\t\treturn { allow, deny }\n\t\t}\n\t\tcase \"Ceiling\": {\n\t\t\tconst cap = residualNode(node.cap, context)\n\t\t\tconst inner = residualNode(node.policy, context)\n\t\t\treturn {\n\t\t\t\tallow: and(not(cap.deny), inner.allow),\n\t\t\t\tdeny: or(cap.deny, inner.deny),\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** @internal */\nexport const residualPair = (\n\tpolicy: unknown,\n\trequest: Policy.KindRequest<Domain.Any>,\n): Pair => {\n\tconst domain = domainOf(policy)\n\tconst descriptor = getDescriptor(domain, request.kind)\n\tif (descriptor === undefined) {\n\t\treturn { allow: never, deny: never }\n\t}\n\treturn residualNode(nodeOf(policy), {\n\t\tprincipal: request.principal,\n\t\taction: request.action,\n\t\tkind: request.kind,\n\t\tfield: request.field,\n\t\tinput: { principal: request.principal, path: descriptor.path },\n\t})\n}\n\n/** @internal */\nexport const residualOf = (\n\tpolicy: unknown,\n\trequest: Policy.KindRequest<Domain.Any>,\n): Ast => residualPair(policy, request).allow\n\n/** @internal */\nexport const residual = dual(2, residualOf)\n\n// Schema\n\nconst Algorithm = Schema.Literals([\n\t\"denyOverrides\",\n\t\"permitOverrides\",\n\t\"firstApplicable\",\n\t\"lastApplicable\",\n])\n\nconst encodedRuleSchema = (meta: Schema.Top | undefined) =>\n\tSchema.Struct({\n\t\t_tag: Schema.Literals([\"Allow\", \"Deny\"]),\n\t\tactions: Schema.Array(Schema.String),\n\t\tresources: Schema.Array(Schema.String),\n\t\twhen: Schema.optionalKey(condition.schema),\n\t\tfields: Schema.String.pipe(Schema.Array, Schema.optionalKey),\n\t\treason: Schema.optionalKey(Schema.String),\n\t\tid: Schema.optionalKey(Schema.String),\n\t\tmeta: Schema.optionalKey(\n\t\t\t(meta ?? Schema.Never) as Schema.Codec<unknown, unknown>,\n\t\t),\n\t})\n\nconst encodedNodeSchema = (\n\tmeta: Schema.Top | undefined,\n): Schema.Codec<Policy.EncodedNode> => {\n\tconst self: Schema.Codec<Policy.EncodedNode> = Schema.Union([\n\t\tSchema.TaggedStruct(\"Rules\", {\n\t\t\talgorithm: Algorithm,\n\t\t\trules: Schema.Array(encodedRuleSchema(meta)),\n\t\t}),\n\t\tSchema.TaggedStruct(\"All\", {\n\t\t\tpolicies: Schema.Array(Schema.suspend(() => self)),\n\t\t}),\n\t\tSchema.TaggedStruct(\"Any\", {\n\t\t\tpolicies: Schema.Array(Schema.suspend(() => self)),\n\t\t}),\n\t\tSchema.TaggedStruct(\"FirstOf\", {\n\t\t\tpolicies: Schema.Array(Schema.suspend(() => self)),\n\t\t}),\n\t\tSchema.TaggedStruct(\"Ceiling\", {\n\t\t\tcap: Schema.suspend(() => self),\n\t\t\tpolicy: Schema.suspend(() => self),\n\t\t}),\n\t]) as never\n\treturn self\n}\n\nconst principalFieldPaths = (domain: Domain.Any): ReadonlySet<string> => {\n\tconst output = new Set<string>()\n\tconst visit = (schema: unknown, prefix: string): void => {\n\t\tif (\n\t\t\t!Predicate.hasProperty(schema, \"fields\") ||\n\t\t\t!Predicate.isObject(schema.fields)\n\t\t) {\n\t\t\treturn\n\t\t}\n\t\tconst fields = schema.fields as Record<string, unknown>\n\t\tfor (const key of Object.keys(fields)) {\n\t\t\tconst path = prefix === \"\" ? key : `${prefix}.${key}`\n\t\t\toutput.add(path)\n\t\t\tvisit(fields[key], path)\n\t\t}\n\t}\n\tvisit(domain.principal, \"\")\n\treturn output\n}\n\nconst principalRefs = (ast: Condition.Encoded | Ast): ReadonlyArray<string> => {\n\tconst output: Array<string> = []\n\tconst visitOperand = (operand: Condition.Operand) => {\n\t\tif (operand._tag === \"Principal\") output.push(operand.path)\n\t}\n\tconst visit = (node: Condition.Encoded | Ast): void => {\n\t\tswitch (node._tag) {\n\t\t\tcase \"Compare\":\n\t\t\t\tvisitOperand(node.value)\n\t\t\t\treturn\n\t\t\tcase \"In\":\n\t\t\t\tnode.values.forEach(visitOperand)\n\t\t\t\treturn\n\t\t\tcase \"Under\":\n\t\t\t\tnode.prefix.forEach(visitOperand)\n\t\t\t\treturn\n\t\t\tcase \"And\":\n\t\t\tcase \"Or\":\n\t\t\t\t;(node.conditions as ReadonlyArray<Ast>).forEach(visit)\n\t\t\t\treturn\n\t\t\tcase \"Not\":\n\t\t\t\tvisit(node.condition)\n\t\t\t\treturn\n\t\t\tdefault:\n\t\t\t\treturn\n\t\t}\n\t}\n\tvisit(ast)\n\treturn output\n}\n\n/**\n * Checks the names in an encoded policy against the domain. It gives one\n * message for each problem.\n */\nconst validate = (\n\tdomain: Domain.Any,\n\troot: Policy.EncodedNode,\n): ReadonlyArray<Schema.FilterIssue> => {\n\tconst issues: Array<Schema.FilterIssue> = []\n\tconst principalPaths = principalFieldPaths(domain)\n\tconst visit = (\n\t\tnode: Policy.EncodedNode,\n\t\tpath: ReadonlyArray<PropertyKey>,\n\t) => {\n\t\tswitch (node._tag) {\n\t\t\tcase \"Rules\":\n\t\t\t\tnode.rules.forEach((value, index) => {\n\t\t\t\t\tconst at = [...path, \"rules\", index]\n\t\t\t\t\tconst kinds = value.resources.includes(\"*\")\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: value.resources\n\t\t\t\t\tconst descriptors = []\n\t\t\t\t\tfor (const kind of value.resources) {\n\t\t\t\t\t\tif (kind === \"*\") continue\n\t\t\t\t\t\tconst descriptor = getDescriptor(domain, kind)\n\t\t\t\t\t\tif (descriptor === undefined) {\n\t\t\t\t\t\t\tissues.push({\n\t\t\t\t\t\t\t\tpath: [...at, \"resources\"],\n\t\t\t\t\t\t\t\tissue: `There is no resource with the kind \"${kind}\"`,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tdescriptors.push(descriptor)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tconst known =\n\t\t\t\t\t\tkinds === undefined\n\t\t\t\t\t\t\t? Array.from(domain.resources.values())\n\t\t\t\t\t\t\t: descriptors\n\t\t\t\t\tfor (const action of value.actions) {\n\t\t\t\t\t\tif (action === \"*\") continue\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t!known.some((descriptor) => descriptor.actions.includes(action))\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tissues.push({\n\t\t\t\t\t\t\t\tpath: [...at, \"actions\"],\n\t\t\t\t\t\t\t\tissue: `The action \"${action}\" is not an action of these resources`,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tconst usedFields = [\n\t\t\t\t\t\t...(value.fields ?? []).filter((field) => !field.includes(\"*\")),\n\t\t\t\t\t\t...(value.when === undefined\n\t\t\t\t\t\t\t? []\n\t\t\t\t\t\t\t: condition.fields(value.when as Ast)),\n\t\t\t\t\t]\n\t\t\t\t\tfor (const field of usedFields) {\n\t\t\t\t\t\tconst everywhere =\n\t\t\t\t\t\t\tkinds !== undefined &&\n\t\t\t\t\t\t\tdescriptors.length > 0 &&\n\t\t\t\t\t\t\tdescriptors.every((descriptor) =>\n\t\t\t\t\t\t\t\tdescriptor.fieldPaths.includes(field),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\tif (!everywhere) {\n\t\t\t\t\t\t\tissues.push({\n\t\t\t\t\t\t\t\tpath: at,\n\t\t\t\t\t\t\t\tissue: `The field \"${field}\" is not a field of every resource of the rule`,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (value.when !== undefined) {\n\t\t\t\t\t\tfor (const ref of principalRefs(value.when)) {\n\t\t\t\t\t\t\tif (!principalPaths.has(ref)) {\n\t\t\t\t\t\t\t\tissues.push({\n\t\t\t\t\t\t\t\t\tpath: [...at, \"when\"],\n\t\t\t\t\t\t\t\t\tissue: `The principal has no field \"${ref}\"`,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\tcase \"All\":\n\t\t\tcase \"Any\":\n\t\t\tcase \"FirstOf\":\n\t\t\t\tnode.policies.forEach((child, index) =>\n\t\t\t\t\tvisit(child, [...path, \"policies\", index]),\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\tcase \"Ceiling\":\n\t\t\t\tvisit(node.cap, [...path, \"cap\"])\n\t\t\t\tvisit(node.policy, [...path, \"policy\"])\n\t\t\t\treturn\n\t\t}\n\t}\n\tvisit(root, [])\n\treturn issues\n}\n\nconst decodeNode = (node: Policy.EncodedNode): Node => {\n\tswitch (node._tag) {\n\t\tcase \"Rules\":\n\t\t\treturn Object.freeze({\n\t\t\t\t_tag: \"Rules\",\n\t\t\t\talgorithm: node.algorithm,\n\t\t\t\trules: Object.freeze(\n\t\t\t\t\tnode.rules.map((value) => rule.make(value as rule.RuleData)),\n\t\t\t\t),\n\t\t\t})\n\t\tcase \"All\":\n\t\tcase \"Any\":\n\t\tcase \"FirstOf\":\n\t\t\treturn Object.freeze({\n\t\t\t\t_tag: node._tag,\n\t\t\t\tpolicies: Object.freeze(node.policies.map(decodeNode)),\n\t\t\t})\n\t\tcase \"Ceiling\":\n\t\t\treturn Object.freeze({\n\t\t\t\t_tag: \"Ceiling\",\n\t\t\t\tcap: decodeNode(node.cap),\n\t\t\t\tpolicy: decodeNode(node.policy),\n\t\t\t})\n\t}\n}\n\n/** @internal */\nexport const schema = (domain: Domain.Any): any =>\n\tencodedNodeSchema(domain.meta).pipe(\n\t\tSchema.check(Schema.makeFilter((node) => validate(domain, node))),\n\t\tSchema.decodeTo(\n\t\t\tSchema.declare(\n\t\t\t\t(u): u is Policy.Policy<Domain.Any> =>\n\t\t\t\t\tisPolicy(u) && domainOf(u) === domain,\n\t\t\t),\n\t\t\tSchemaTransformation.transform({\n\t\t\t\tdecode: (node: Policy.EncodedNode) =>\n\t\t\t\t\tfromNode(domain, decodeNode(node)),\n\t\t\t\tencode: (policy: Policy.Policy<Domain.Any>) => encodeNode(policy.node),\n\t\t\t}),\n\t\t),\n\t)\n"],"mappings":"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"}
@@ -1,66 +0,0 @@
1
- import * as Schema from "effect/Schema";
2
- import * as Predicate from "effect/Predicate";
3
- // src/internal/resource.ts
4
- /** @internal */
5
- const TypeId = "~@nmnmcc/ability/Resource";
6
- const fieldRefs = (fields) => {
7
- const refs = {};
8
- for (const key of Object.keys(fields)) refs[key] = Object.freeze({
9
- _tag: "Field",
10
- field: key
11
- });
12
- return Object.freeze(refs);
13
- };
14
- const hasFields = (schema) => Predicate.hasProperty(schema, "fields") && Predicate.isObject(schema.fields);
15
- /**
16
- * Gives the paths of all data fields. A nested struct gives one path for each
17
- * of its own fields, with a dot between the names.
18
- */
19
- const collectFieldPaths = (fields, prefix, output) => {
20
- for (const key of Object.keys(fields)) {
21
- const schema = fields[key];
22
- const path = prefix === "" ? key : `${prefix}.${key}`;
23
- if (hasFields(schema)) collectFieldPaths(schema.fields, path, output);
24
- else output.push(path);
25
- }
26
- };
27
- const makeDescriptor = (kind, options, schema) => {
28
- const fieldPaths = [];
29
- collectFieldPaths(options.fields, "", fieldPaths);
30
- return Object.freeze({
31
- kind,
32
- actions: Object.freeze([...options.actions]),
33
- path: options.path === void 0 ? void 0 : Object.freeze([...options.path(fieldRefs(options.fields))]),
34
- fieldPaths: Object.freeze(fieldPaths),
35
- schema
36
- });
37
- };
38
- /** @internal */
39
- const Class = (identifier) => (kind, options) => {
40
- const Base = Schema.TaggedClass(identifier)(kind, options.fields);
41
- const descriptor = makeDescriptor(kind, options, Base);
42
- return class extends Base {
43
- static [TypeId] = descriptor;
44
- static kind = kind;
45
- static actions = descriptor.actions;
46
- };
47
- };
48
- /** @internal */
49
- const make = (kind, options) => {
50
- const schema = Schema.TaggedStruct(kind, options.fields);
51
- const descriptor = makeDescriptor(kind, options, schema);
52
- return Object.freeze({
53
- [TypeId]: descriptor,
54
- kind,
55
- actions: descriptor.actions,
56
- schema,
57
- make: (input) => schema.make(input)
58
- });
59
- };
60
- /** @internal */
61
- const isResource = (u) => Predicate.hasProperty(u, TypeId);
62
- /** @internal */
63
- const descriptor = (resource) => resource[TypeId];
64
- export { Class, TypeId, descriptor, isResource, make };
65
-
66
- //# sourceMappingURL=resource.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"resource.js","names":[],"sources":["../../src/internal/resource.ts"],"sourcesContent":["import * as Predicate from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport type * as Resource from \"../Resource.ts\"\n\n/** @internal */\nexport const TypeId = \"~@nmnmcc/ability/Resource\" as const\n\nconst fieldRefs = (fields: Schema.Struct.Fields) => {\n\tconst refs: Record<string, Resource.FieldRef<string>> = {}\n\tfor (const key of Object.keys(fields)) {\n\t\trefs[key] = Object.freeze({ _tag: \"Field\", field: key })\n\t}\n\treturn Object.freeze(refs)\n}\n\nconst hasFields = (\n\tschema: unknown,\n): schema is { readonly fields: Schema.Struct.Fields } =>\n\tPredicate.hasProperty(schema, \"fields\") && Predicate.isObject(schema.fields)\n\n/**\n * Gives the paths of all data fields. A nested struct gives one path for each\n * of its own fields, with a dot between the names.\n */\nconst collectFieldPaths = (\n\tfields: Schema.Struct.Fields,\n\tprefix: string,\n\toutput: Array<string>,\n): void => {\n\tfor (const key of Object.keys(fields)) {\n\t\tconst schema = fields[key]\n\t\tconst path = prefix === \"\" ? key : `${prefix}.${key}`\n\t\tif (hasFields(schema)) {\n\t\t\tcollectFieldPaths(schema.fields, path, output)\n\t\t} else {\n\t\t\toutput.push(path)\n\t\t}\n\t}\n}\n\nconst makeDescriptor = (\n\tkind: string,\n\toptions: Resource.Options<ReadonlyArray<string>, Schema.Struct.Fields>,\n\tschema: Schema.Top,\n): Resource.Descriptor => {\n\tconst fieldPaths: Array<string> = []\n\tcollectFieldPaths(options.fields, \"\", fieldPaths)\n\treturn Object.freeze({\n\t\tkind,\n\t\tactions: Object.freeze([...options.actions]),\n\t\tpath:\n\t\t\toptions.path === undefined\n\t\t\t\t? undefined\n\t\t\t\t: Object.freeze([...options.path(fieldRefs(options.fields))]),\n\t\tfieldPaths: Object.freeze(fieldPaths),\n\t\tschema,\n\t})\n}\n\n/** @internal */\nexport const Class =\n\t(identifier?: string) =>\n\t(\n\t\tkind: string,\n\t\toptions: Resource.Options<ReadonlyArray<string>, Schema.Struct.Fields>,\n\t) => {\n\t\tconst Base = Schema.TaggedClass<any>(identifier)(kind, options.fields)\n\t\tconst descriptor = makeDescriptor(kind, options, Base)\n\t\treturn class extends Base {\n\t\t\tstatic readonly [TypeId] = descriptor\n\t\t\tstatic readonly kind = kind\n\t\t\tstatic readonly actions = descriptor.actions\n\t\t}\n\t}\n\n/** @internal */\nexport const make = (\n\tkind: string,\n\toptions: Resource.Options<ReadonlyArray<string>, Schema.Struct.Fields>,\n) => {\n\tconst schema = Schema.TaggedStruct(kind, options.fields)\n\tconst descriptor = makeDescriptor(kind, options, schema)\n\treturn Object.freeze({\n\t\t[TypeId]: descriptor,\n\t\tkind,\n\t\tactions: descriptor.actions,\n\t\tschema,\n\t\tmake: (input: object) => schema.make(input as never),\n\t})\n}\n\n/** @internal */\nexport const isResource = (u: unknown): u is Resource.Any =>\n\tPredicate.hasProperty(u, TypeId)\n\n/** @internal */\nexport const descriptor = (resource: Resource.Any): Resource.Descriptor =>\n\tresource[TypeId]\n"],"mappings":";;;;AAKA,MAAa,SAAS;AAEtB,MAAM,aAAa,WAAiC;CACnD,MAAM,OAAkD,CAAC;CACzD,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,GACnC,KAAK,OAAO,OAAO,OAAO;EAAE,MAAM;EAAS,OAAO;CAAI,CAAC;CAExD,OAAO,OAAO,OAAO,IAAI;AAC1B;AAEA,MAAM,aACL,WAEA,UAAU,YAAY,QAAQ,QAAQ,KAAK,UAAU,SAAS,OAAO,MAAM;;;;;AAM5E,MAAM,qBACL,QACA,QACA,WACU;CACV,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,GAAG;EACtC,MAAM,SAAS,OAAO;EACtB,MAAM,OAAO,WAAW,KAAK,MAAM,GAAG,OAAO,GAAG;EAChD,IAAI,UAAU,MAAM,GACnB,kBAAkB,OAAO,QAAQ,MAAM,MAAM;OAE7C,OAAO,KAAK,IAAI;CAElB;AACD;AAEA,MAAM,kBACL,MACA,SACA,WACyB;CACzB,MAAM,aAA4B,CAAC;CACnC,kBAAkB,QAAQ,QAAQ,IAAI,UAAU;CAChD,OAAO,OAAO,OAAO;EACpB;EACA,SAAS,OAAO,OAAO,CAAC,GAAG,QAAQ,OAAO,CAAC;EAC3C,MACC,QAAQ,SAAS,KAAA,IACd,KAAA,IACA,OAAO,OAAO,CAAC,GAAG,QAAQ,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC,CAAC;EAC9D,YAAY,OAAO,OAAO,UAAU;EACpC;CACD,CAAC;AACF;;AAGA,MAAa,SACX,gBAEA,MACA,YACI;CACJ,MAAM,OAAO,OAAO,YAAiB,UAAU,CAAC,CAAC,MAAM,QAAQ,MAAM;CACrE,MAAM,aAAa,eAAe,MAAM,SAAS,IAAI;CACrD,OAAO,cAAc,KAAK;EACzB,QAAiB,UAAU;EAC3B,OAAgB,OAAO;EACvB,OAAgB,UAAU,WAAW;CACtC;AACD;;AAGD,MAAa,QACZ,MACA,YACI;CACJ,MAAM,SAAS,OAAO,aAAa,MAAM,QAAQ,MAAM;CACvD,MAAM,aAAa,eAAe,MAAM,SAAS,MAAM;CACvD,OAAO,OAAO,OAAO;GACnB,SAAS;EACV;EACA,SAAS,WAAW;EACpB;EACA,OAAO,UAAkB,OAAO,KAAK,KAAc;CACpD,CAAC;AACF;;AAGA,MAAa,cAAc,MAC1B,UAAU,YAAY,GAAG,MAAM;;AAGhC,MAAa,cAAc,aAC1B,SAAS"}
@@ -1,131 +0,0 @@
1
- import { descriptor, isResource } from "./resource.js";
2
- import { builder as builder$1 } from "./condition.js";
3
- import * as Equal from "effect/Equal";
4
- import * as Hash from "effect/Hash";
5
- import * as Predicate from "effect/Predicate";
6
- import * as Formatter from "effect/Formatter";
7
- import * as Inspectable from "effect/Inspectable";
8
- import * as Pipeable from "effect/Pipeable";
9
- // src/internal/rule.ts
10
- /** @internal */
11
- const TypeId = "~@nmnmcc/ability/Rule";
12
- /** @internal */
13
- const toData = (rule) => {
14
- const data = {
15
- _tag: rule._tag,
16
- actions: rule.actions,
17
- resources: rule.resources
18
- };
19
- if (rule.when !== void 0) data.when = rule.when;
20
- if (rule.fields !== void 0) data.fields = rule.fields;
21
- if (rule.reason !== void 0) data.reason = rule.reason;
22
- if (rule.id !== void 0) data.id = rule.id;
23
- if (rule.meta !== void 0) data.meta = rule.meta;
24
- return data;
25
- };
26
- function* ruleIterator(rule) {
27
- yield rule;
28
- return rule;
29
- }
30
- const Proto = {
31
- ...Pipeable.Prototype,
32
- [TypeId]: TypeId,
33
- [Symbol.iterator]() {
34
- return ruleIterator(this);
35
- },
36
- [Equal.symbol](that) {
37
- return isRule(that) && Equal.equals(toData(this), toData(that));
38
- },
39
- [Hash.symbol]() {
40
- return Hash.structure(toData(this));
41
- },
42
- toJSON() {
43
- return toData(this);
44
- },
45
- toString() {
46
- return Formatter.format(toData(this), { space: 2 });
47
- },
48
- [Inspectable.NodeInspectSymbol]() {
49
- return toData(this);
50
- }
51
- };
52
- /**
53
- * Makes a rule from plain data. It does not check the data.
54
- *
55
- * @internal
56
- */
57
- const make = (data) => {
58
- const rule = Object.create(Proto);
59
- Object.assign(rule, toData(data));
60
- rule.actions = Object.freeze([...data.actions]);
61
- rule.resources = Object.freeze([...data.resources]);
62
- if (data.fields !== void 0) rule.fields = Object.freeze([...data.fields]);
63
- return Object.freeze(rule);
64
- };
65
- /** @internal */
66
- const isRule = (u) => Predicate.hasProperty(u, TypeId);
67
- const toArray = (value) => Array.isArray(value) ? value : [value];
68
- const kindName = (target) => isResource(target) ? descriptor(target).kind : target;
69
- const makeFromOptions = (tag) => (action, resource, options) => {
70
- const data = {
71
- _tag: tag,
72
- actions: toArray(action),
73
- resources: toArray(resource).map(kindName)
74
- };
75
- if (options?.when !== void 0) data.when = options.when(builder$1());
76
- if (options?.fields !== void 0) data.fields = options.fields;
77
- if (options?.reason !== void 0) data.reason = options.reason;
78
- if (options?.id !== void 0) data.id = options.id;
79
- if (options?.meta !== void 0) data.meta = options.meta;
80
- return make(data);
81
- };
82
- const sharedBuilder = Object.freeze({
83
- allow: makeFromOptions("Allow"),
84
- deny: makeFromOptions("Deny")
85
- });
86
- /** @internal */
87
- const builder = (_domain) => sharedBuilder;
88
- const patternCache = /* @__PURE__ */ new Map();
89
- const compilePattern = (pattern) => {
90
- const cached = patternCache.get(pattern);
91
- if (cached !== void 0) return cached;
92
- let source = "";
93
- let index = 0;
94
- while (index < pattern.length) {
95
- const char = pattern[index];
96
- if (char === "*") {
97
- if (pattern[index + 1] === "*") {
98
- source += ".*";
99
- index = index + 2;
100
- continue;
101
- }
102
- source += "[^.]*";
103
- } else source += char.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
104
- index = index + 1;
105
- }
106
- const regex = new RegExp(`^${source}$`);
107
- patternCache.set(pattern, regex);
108
- return regex;
109
- };
110
- /** @internal */
111
- const matchesFieldPattern = (pattern, field) => pattern.includes("*") ? compilePattern(pattern).test(field) : pattern === field;
112
- /**
113
- * Tests the parts of a rule that do not need the resource value: the action,
114
- * the kind and the field.
115
- *
116
- * A rule with fields covers a request with no field only when it is an allow
117
- * rule. So "may update some fields" is a yes. A deny rule for some fields does
118
- * not stop a request for the whole value.
119
- *
120
- * @internal
121
- */
122
- const matchesTarget = (rule, action, kind, field) => {
123
- if (!rule.actions.includes("*") && !rule.actions.includes(action)) return false;
124
- if (!rule.resources.includes("*") && !rule.resources.includes(kind)) return false;
125
- if (rule.fields === void 0) return true;
126
- if (field === void 0) return rule._tag === "Allow";
127
- return rule.fields.some((pattern) => matchesFieldPattern(pattern, field));
128
- };
129
- export { TypeId, builder, isRule, make, matchesFieldPattern, matchesTarget, toData };
130
-
131
- //# sourceMappingURL=rule.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rule.js","names":["conditionBuilder"],"sources":["../../src/internal/rule.ts"],"sourcesContent":["import * as Equal from \"effect/Equal\"\nimport * as Formatter from \"effect/Formatter\"\nimport * as Hash from \"effect/Hash\"\nimport * as Inspectable from \"effect/Inspectable\"\nimport * as Pipeable from \"effect/Pipeable\"\nimport * as Predicate from \"effect/Predicate\"\nimport type * as Condition from \"../Condition.ts\"\nimport type * as Domain from \"../Domain.ts\"\nimport type * as Rule from \"../Rule.ts\"\nimport { builder as conditionBuilder } from \"./condition.ts\"\nimport { isResource, descriptor } from \"./resource.ts\"\n\n/** @internal */\nexport const TypeId = \"~@nmnmcc/ability/Rule\" as const\n\n/**\n * The plain data of a rule.\n *\n * @internal\n */\nexport interface RuleData {\n\treadonly _tag: \"Allow\" | \"Deny\"\n\treadonly actions: ReadonlyArray<string>\n\treadonly resources: ReadonlyArray<string>\n\treadonly when?: Condition.Ast\n\treadonly fields?: ReadonlyArray<string>\n\treadonly reason?: string\n\treadonly id?: string\n\treadonly meta?: unknown\n}\n\n/** @internal */\nexport const toData = (rule: RuleData): RuleData => {\n\tconst data: {\n\t\t-readonly [K in keyof RuleData]: RuleData[K]\n\t} = {\n\t\t_tag: rule._tag,\n\t\tactions: rule.actions,\n\t\tresources: rule.resources,\n\t}\n\tif (rule.when !== undefined) data.when = rule.when\n\tif (rule.fields !== undefined) data.fields = rule.fields\n\tif (rule.reason !== undefined) data.reason = rule.reason\n\tif (rule.id !== undefined) data.id = rule.id\n\tif (rule.meta !== undefined) data.meta = rule.meta\n\treturn data\n}\n\nfunction* ruleIterator<A>(rule: A): Generator<A, A, unknown> {\n\tyield rule\n\treturn rule\n}\n\nconst Proto = {\n\t...Pipeable.Prototype,\n\t[TypeId]: TypeId,\n\t[Symbol.iterator](this: Rule.Any) {\n\t\treturn ruleIterator(this)\n\t},\n\t[Equal.symbol](this: RuleData, that: unknown): boolean {\n\t\treturn isRule(that) && Equal.equals(toData(this), toData(that))\n\t},\n\t[Hash.symbol](this: RuleData): number {\n\t\treturn Hash.structure(toData(this))\n\t},\n\ttoJSON(this: RuleData) {\n\t\treturn toData(this)\n\t},\n\ttoString(this: RuleData) {\n\t\treturn Formatter.format(toData(this), { space: 2 })\n\t},\n\t[Inspectable.NodeInspectSymbol](this: RuleData) {\n\t\treturn toData(this)\n\t},\n}\n\n/**\n * Makes a rule from plain data. It does not check the data.\n *\n * @internal\n */\nexport const make = (data: RuleData): Rule.Any => {\n\tconst rule = Object.create(Proto) as {\n\t\t-readonly [K in keyof RuleData]: RuleData[K]\n\t}\n\tObject.assign(rule, toData(data))\n\trule.actions = Object.freeze([...data.actions])\n\trule.resources = Object.freeze([...data.resources])\n\tif (data.fields !== undefined) rule.fields = Object.freeze([...data.fields])\n\treturn Object.freeze(rule) as Rule.Any\n}\n\n/** @internal */\nexport const isRule = (u: unknown): u is Rule.Any =>\n\tPredicate.hasProperty(u, TypeId)\n\nconst toArray = <A>(value: A | ReadonlyArray<A>): ReadonlyArray<A> =>\n\tArray.isArray(value) ? value : [value as A]\n\nconst kindName = (target: unknown): string =>\n\tisResource(target) ? descriptor(target).kind : (target as string)\n\nconst makeFromOptions =\n\t(tag: \"Allow\" | \"Deny\") =>\n\t(\n\t\taction: string | ReadonlyArray<string>,\n\t\tresource: unknown,\n\t\toptions?: {\n\t\t\treadonly when?: (builder: unknown) => Condition.Ast\n\t\t\treadonly fields?: ReadonlyArray<string>\n\t\t\treadonly reason?: string\n\t\t\treadonly id?: string\n\t\t\treadonly meta?: unknown\n\t\t},\n\t): Rule.Any => {\n\t\tconst data: {\n\t\t\t-readonly [K in keyof RuleData]: RuleData[K]\n\t\t} = {\n\t\t\t_tag: tag,\n\t\t\tactions: toArray(action),\n\t\t\tresources: toArray(resource).map(kindName),\n\t\t}\n\t\tif (options?.when !== undefined)\n\t\t\tdata.when = options.when(conditionBuilder())\n\t\tif (options?.fields !== undefined) data.fields = options.fields\n\t\tif (options?.reason !== undefined) data.reason = options.reason\n\t\tif (options?.id !== undefined) data.id = options.id\n\t\tif (options?.meta !== undefined) data.meta = options.meta\n\t\treturn make(data)\n\t}\n\nconst sharedBuilder = Object.freeze({\n\tallow: makeFromOptions(\"Allow\"),\n\tdeny: makeFromOptions(\"Deny\"),\n})\n\n/** @internal */\nexport const builder = (_domain: Domain.Any): any => sharedBuilder\n\nconst patternCache = new Map<string, RegExp>()\n\nconst compilePattern = (pattern: string): RegExp => {\n\tconst cached = patternCache.get(pattern)\n\tif (cached !== undefined) {\n\t\treturn cached\n\t}\n\tlet source = \"\"\n\tlet index = 0\n\twhile (index < pattern.length) {\n\t\tconst char = pattern[index]!\n\t\tif (char === \"*\") {\n\t\t\tif (pattern[index + 1] === \"*\") {\n\t\t\t\tsource += \".*\"\n\t\t\t\tindex = index + 2\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tsource += \"[^.]*\"\n\t\t} else {\n\t\t\tsource += char.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\t\t}\n\t\tindex = index + 1\n\t}\n\tconst regex = new RegExp(`^${source}$`)\n\tpatternCache.set(pattern, regex)\n\treturn regex\n}\n\n/** @internal */\nexport const matchesFieldPattern = (pattern: string, field: string): boolean =>\n\tpattern.includes(\"*\")\n\t\t? compilePattern(pattern).test(field)\n\t\t: pattern === field\n\n/**\n * Tests the parts of a rule that do not need the resource value: the action,\n * the kind and the field.\n *\n * A rule with fields covers a request with no field only when it is an allow\n * rule. So \"may update some fields\" is a yes. A deny rule for some fields does\n * not stop a request for the whole value.\n *\n * @internal\n */\nexport const matchesTarget = (\n\trule: RuleData,\n\taction: string,\n\tkind: string,\n\tfield: string | undefined,\n): boolean => {\n\tif (!rule.actions.includes(\"*\") && !rule.actions.includes(action)) {\n\t\treturn false\n\t}\n\tif (!rule.resources.includes(\"*\") && !rule.resources.includes(kind)) {\n\t\treturn false\n\t}\n\tif (rule.fields === undefined) {\n\t\treturn true\n\t}\n\tif (field === undefined) {\n\t\treturn rule._tag === \"Allow\"\n\t}\n\treturn rule.fields.some((pattern) => matchesFieldPattern(pattern, field))\n}\n"],"mappings":"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"}
@@ -1,271 +0,0 @@
1
- // The service erases the error type and the service type of predicates,
2
- // principals and deciders to `unknown`. The public types in
3
- // `src/Authorizer.ts` keep them, so the language service check for `unknown`
4
- // in an Effect is off here.
5
- // @effect-diagnostics anyUnknownInErrorContext:off
6
- import * as Cache from "effect/Cache"
7
- import * as Context from "effect/Context"
8
- import * as Effect from "effect/Effect"
9
- import * as Equal from "effect/Equal"
10
- import * as Hash from "effect/Hash"
11
- import * as Layer from "effect/Layer"
12
- import * as Schema from "effect/Schema"
13
- import type * as Authorizer from "../Authorizer.ts"
14
- import type * as Condition from "../Condition.ts"
15
- import * as Decision from "../Decision.ts"
16
- import type * as Domain from "../Domain.ts"
17
- import type * as Policy from "../Policy.ts"
18
- import { kindOf } from "./domain.ts"
19
- import * as policy from "./policy.ts"
20
-
21
- /** @internal */
22
- export class Forbidden extends Schema.TaggedError<Forbidden>(
23
- "@nmnmcc/ability/Authorizer/Forbidden",
24
- )("Forbidden", {
25
- action: Schema.String,
26
- kind: Schema.String,
27
- field: Schema.optionalKey(Schema.String),
28
- reason: Schema.optionalKey(Schema.String),
29
- }) {}
30
-
31
- /** @internal */
32
- export class PolicyUnavailable extends Schema.TaggedError<PolicyUnavailable>(
33
- "@nmnmcc/ability/Authorizer/PolicyUnavailable",
34
- )("PolicyUnavailable", { cause: Schema.Defect() }) {}
35
-
36
- /** @internal */
37
- export class Pending extends Schema.TaggedError<Pending>(
38
- "@nmnmcc/ability/Authorizer/Pending",
39
- )("Pending", {
40
- ticket: Schema.String,
41
- reason: Schema.optionalKey(Schema.String),
42
- }) {}
43
-
44
- type AnyTarget = {
45
- readonly action: string
46
- readonly resource?: object
47
- readonly kind?: string
48
- readonly field?: string
49
- }
50
-
51
- /** @internal */
52
- export const Service =
53
- () =>
54
- (id: string, domain: Domain.Any): Authorizer.AnyServiceClass => {
55
- const Base = Context.Service<any, any>()(id)
56
- return class extends Base {
57
- static readonly domain = domain
58
- static readonly decide = (target: AnyTarget) =>
59
- this.use((service: any) => service.decide(target))
60
- static readonly check = (target: AnyTarget) =>
61
- this.use((service: any) => service.check(target))
62
- static readonly guard = (target: AnyTarget, options: unknown) =>
63
- this.use((service: any) => service.guard(target, options))
64
- static readonly filter = (target: AnyTarget) =>
65
- this.use((service: any) => service.filter(target))
66
- static readonly permittedFields = (target: AnyTarget) =>
67
- this.use((service: any) => service.permittedFields(target))
68
- } as never
69
- }
70
-
71
- /**
72
- * A cache key for a principal. Two principals with the same encoded value are
73
- * the same key.
74
- */
75
- class PrincipalKey implements Equal.Equal {
76
- readonly principal: object
77
- readonly json: string
78
- constructor(principal: object, json: string) {
79
- this.principal = principal
80
- this.json = json
81
- }
82
- [Equal.symbol](that: unknown): boolean {
83
- return that instanceof PrincipalKey && that.json === this.json
84
- }
85
- [Hash.symbol](): number {
86
- return Hash.string(this.json)
87
- }
88
- }
89
-
90
- const forbidden = (
91
- target: AnyTarget,
92
- kind: string,
93
- reason: string | undefined,
94
- ): Forbidden =>
95
- Forbidden.make({
96
- action: target.action,
97
- kind,
98
- ...(target.field === undefined ? {} : { field: target.field }),
99
- ...(reason === undefined ? {} : { reason }),
100
- })
101
-
102
- /** @internal */
103
- export const layer = (
104
- service: Authorizer.AnyServiceClass,
105
- options: {
106
- readonly principal: Effect.Effect<object, unknown, unknown>
107
- readonly policy: (
108
- principal: object,
109
- ) => Effect.Effect<Policy.Any, unknown, unknown>
110
- readonly decider?: (
111
- request: object,
112
- ) => Effect.Effect<Decision.Decision, unknown, unknown>
113
- readonly cache?:
114
- { readonly capacity?: number; readonly timeToLive?: any } | false
115
- },
116
- ) => {
117
- const domain: Domain.Any = service.domain
118
- const encode = Schema.encodeUnknownSync(
119
- domain.principal as unknown as Schema.Codec<unknown, unknown>,
120
- )
121
- const keyOf = (principal: object) =>
122
- new PrincipalKey(principal, JSON.stringify(encode(principal)))
123
- return Layer.effect(
124
- service,
125
- Effect.gen(function* () {
126
- // The services of the policy loader and of the predicates come from the
127
- // layer, so a check does not need them.
128
- const services = yield* Effect.context<never>()
129
-
130
- const load = (principal: object) =>
131
- options.policy(principal).pipe(
132
- Effect.provideContext(services),
133
- Effect.mapError((cause) => PolicyUnavailable.make({ cause })),
134
- )
135
-
136
- const cache =
137
- options.cache === false
138
- ? undefined
139
- : yield* Cache.make({
140
- capacity: options.cache?.capacity ?? 1000,
141
- timeToLive: options.cache?.timeToLive ?? "30 seconds",
142
- lookup: (key: PrincipalKey) => load(key.principal),
143
- })
144
-
145
- const policyFor = (principal: object) => {
146
- if (cache === undefined) {
147
- return load(principal)
148
- }
149
- const key = keyOf(principal)
150
- // A failed lookup is kept in the cache. Remove it, so the next check
151
- // tries again.
152
- return Cache.get(cache, key).pipe(
153
- Effect.tapError(() => Cache.invalidate(cache, key)),
154
- )
155
- }
156
-
157
- const withServices = <A, E>(
158
- effect: Effect.Effect<A, E, unknown>,
159
- ): Effect.Effect<A, E, unknown> => Effect.provideContext(effect, services)
160
-
161
- const current = Effect.gen(function* () {
162
- const principal = yield* options.principal
163
- const value = yield* policyFor(principal)
164
- return { principal, policy: value }
165
- })
166
-
167
- const decide = Effect.fnUntraced(function* (target: AnyTarget) {
168
- const { principal, policy: value } = yield* current
169
- const decision: Decision.Decision = yield* withServices(
170
- policy.decideOf(value, { ...target, principal } as never),
171
- )
172
- return decision
173
- })
174
-
175
- const kindDecision = (
176
- value: Policy.Any,
177
- principal: object,
178
- target: AnyTarget,
179
- ): Decision.Decision => {
180
- const pair = policy.residualPair(value, {
181
- ...target,
182
- principal,
183
- } as never)
184
- if (pair.allow._tag === "True") {
185
- return Decision.Allow({})
186
- }
187
- if (pair.deny._tag === "True") {
188
- return Decision.Deny({})
189
- }
190
- return Decision.NotApplicable
191
- }
192
-
193
- const check = Effect.fnUntraced(function* (target: AnyTarget) {
194
- const { principal, policy: value } = yield* current
195
- const kind =
196
- target.resource === undefined
197
- ? (target.kind ?? "")
198
- : (kindOf(target.resource) ?? "")
199
- let decision: Decision.Decision =
200
- target.resource === undefined
201
- ? kindDecision(value, principal, target)
202
- : yield* withServices(
203
- policy.decideOf(value, { ...target, principal } as never),
204
- )
205
- if (
206
- decision._tag === "NotApplicable" &&
207
- options.decider !== undefined
208
- ) {
209
- decision = yield* options.decider({ ...target, principal })
210
- }
211
- if (decision._tag !== "Allow") {
212
- return yield* forbidden(target, kind, Decision.reason(decision))
213
- }
214
- })
215
-
216
- const guard = Effect.fnUntraced(function* (
217
- target: AnyTarget,
218
- guardOptions: {
219
- readonly read?: string
220
- readonly hide: () => unknown
221
- },
222
- ) {
223
- const read = guardOptions.read ?? "read"
224
- const { field: _field, ...rest } = target
225
- const visible: Decision.Decision = yield* decide({
226
- ...rest,
227
- action: read,
228
- })
229
- if (visible._tag !== "Allow") {
230
- return yield* Effect.fail(guardOptions.hide())
231
- }
232
- if (target.action !== read || target.field !== undefined) {
233
- yield* check(target)
234
- }
235
- })
236
-
237
- const filter = (
238
- target: AnyTarget,
239
- ): Effect.Effect<Condition.Ast, unknown, unknown> =>
240
- Effect.map(current, ({ principal, policy: value }) =>
241
- policy.residualOf(value, { ...target, principal } as never),
242
- )
243
-
244
- const permittedFields = Effect.fnUntraced(function* (target: AnyTarget) {
245
- const { principal, policy: value } = yield* current
246
- return yield* withServices(
247
- policy.permittedFieldsOf(value, { ...target, principal } as never),
248
- )
249
- })
250
-
251
- const invalidate = (principal?: object) =>
252
- cache === undefined
253
- ? Effect.void
254
- : principal === undefined
255
- ? Cache.invalidateAll(cache)
256
- : Cache.invalidate(cache, keyOf(principal))
257
-
258
- return {
259
- domain,
260
- principal: options.principal,
261
- policy: Effect.map(current, ({ policy: value }) => value),
262
- decide,
263
- check,
264
- guard,
265
- filter,
266
- permittedFields,
267
- invalidate,
268
- } as never
269
- }),
270
- )
271
- }