@nmnmcc/ability 1.0.1 → 2.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.
- package/README.md +112 -69
- package/dist/Authorizer.d.ts +43 -47
- package/dist/Authorizer.d.ts.map +1 -1
- package/dist/Authorizer.js +5 -290
- package/dist/Authorizer.js.map +1 -1
- package/dist/Condition.d.ts +164 -332
- package/dist/Condition.d.ts.map +1 -1
- package/dist/Condition.js +75 -890
- package/dist/Condition.js.map +1 -1
- package/dist/Decision.d.ts +13 -13
- package/dist/Decision.d.ts.map +1 -1
- package/dist/Decision.js.map +1 -1
- package/dist/Domain.d.ts +10 -21
- package/dist/Domain.d.ts.map +1 -1
- package/dist/Domain.js +4 -62
- package/dist/Domain.js.map +1 -1
- package/dist/HttpApi.d.ts +31 -7
- package/dist/HttpApi.d.ts.map +1 -1
- package/dist/HttpApi.js +25 -8
- package/dist/HttpApi.js.map +1 -1
- package/dist/Limits.d.ts +114 -0
- package/dist/Limits.d.ts.map +1 -0
- package/dist/Limits.js +132 -0
- package/dist/Limits.js.map +1 -0
- package/dist/Policy.d.ts +276 -269
- package/dist/Policy.d.ts.map +1 -1
- package/dist/Policy.js +73 -1177
- package/dist/Policy.js.map +1 -1
- package/dist/Resource.d.ts +4 -177
- package/dist/Resource.d.ts.map +1 -1
- package/dist/Resource.js +4 -803
- package/dist/Resource.js.map +1 -1
- package/dist/index.d.ts +3 -4
- package/dist/index.js +4 -5
- package/dist/internal/algebra.js +267 -0
- package/dist/internal/algebra.js.map +1 -0
- package/dist/internal/analysis.js +245 -0
- package/dist/internal/analysis.js.map +1 -0
- package/dist/internal/authorizer.d.ts +30 -0
- package/dist/internal/authorizer.d.ts.map +1 -0
- package/dist/internal/authorizer.js +221 -0
- package/dist/internal/authorizer.js.map +1 -0
- package/dist/internal/bind.js +311 -0
- package/dist/internal/bind.js.map +1 -0
- package/dist/internal/catalog.d.ts +56 -0
- package/dist/internal/catalog.d.ts.map +1 -0
- package/dist/internal/catalog.js +292 -0
- package/dist/internal/catalog.js.map +1 -0
- package/dist/internal/decide.js +166 -0
- package/dist/internal/decide.js.map +1 -0
- package/dist/internal/domain.js +32 -0
- package/dist/internal/domain.js.map +1 -0
- package/dist/internal/json.js +134 -0
- package/dist/internal/json.js.map +1 -0
- package/dist/internal/limits.d.ts +14 -0
- package/dist/internal/limits.d.ts.map +1 -0
- package/dist/internal/limits.js +43 -0
- package/dist/internal/limits.js.map +1 -0
- package/dist/internal/nodes.d.ts +121 -0
- package/dist/internal/nodes.d.ts.map +1 -0
- package/dist/internal/nodes.js +277 -0
- package/dist/internal/nodes.js.map +1 -0
- package/dist/internal/patterns.js +110 -0
- package/dist/internal/patterns.js.map +1 -0
- package/dist/internal/policy-json.js +265 -0
- package/dist/internal/policy-json.js.map +1 -0
- package/dist/internal/policy-nodes.d.ts +61 -0
- package/dist/internal/policy-nodes.d.ts.map +1 -0
- package/dist/internal/policy-nodes.js +146 -0
- package/dist/internal/policy-nodes.js.map +1 -0
- package/dist/internal/policy.d.ts +42 -0
- package/dist/internal/policy.d.ts.map +1 -0
- package/dist/internal/policy.js +124 -0
- package/dist/internal/policy.js.map +1 -0
- package/dist/internal/read.js +39 -0
- package/dist/internal/read.js.map +1 -0
- package/dist/internal/resource.js +115 -0
- package/dist/internal/resource.js.map +1 -0
- package/dist/internal/rule.d.ts +26 -0
- package/dist/internal/rule.d.ts.map +1 -0
- package/dist/internal/rule.js +103 -0
- package/dist/internal/rule.js.map +1 -0
- package/dist/internal/search.d.ts +22 -0
- package/dist/internal/search.d.ts.map +1 -0
- package/dist/internal/search.js +274 -0
- package/dist/internal/search.js.map +1 -0
- package/dist/internal/suggest.js +46 -0
- package/dist/internal/suggest.js.map +1 -0
- package/package.json +3 -4
- package/src/Authorizer.ts +67 -583
- package/src/Condition.ts +339 -2095
- package/src/Decision.ts +6 -5
- package/src/Domain.ts +12 -69
- package/src/HttpApi.ts +72 -10
- package/src/Limits.ts +132 -0
- package/src/Policy.ts +391 -2772
- package/src/Resource.ts +7 -1548
- package/src/index.ts +3 -9
- package/src/internal/algebra.ts +482 -0
- package/src/internal/analysis.ts +478 -0
- package/src/internal/authorizer.ts +418 -0
- package/src/internal/bind.ts +648 -0
- package/src/internal/catalog.ts +534 -0
- package/src/internal/decide.ts +402 -0
- package/src/internal/domain.ts +66 -0
- package/src/internal/json.ts +260 -0
- package/src/internal/limits.ts +78 -0
- package/src/internal/nodes.ts +404 -0
- package/src/internal/patterns.ts +211 -0
- package/src/internal/policy-json.ts +487 -0
- package/src/internal/policy-nodes.ts +207 -0
- package/src/internal/policy.ts +264 -0
- package/src/internal/read.ts +55 -0
- package/src/internal/resource.ts +180 -0
- package/src/internal/rule.ts +196 -0
- package/src/internal/search.ts +541 -0
- package/src/internal/suggest.ts +104 -0
- package/dist/Principal.d.ts +0 -35
- package/dist/Principal.d.ts.map +0 -1
- package/dist/Principal.js +0 -37
- package/dist/Principal.js.map +0 -1
- package/dist/Rule.d.ts +0 -275
- package/dist/Rule.d.ts.map +0 -1
- package/dist/Rule.js +0 -257
- package/dist/Rule.js.map +0 -1
- package/src/Principal.ts +0 -41
- package/src/Rule.ts +0 -683
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { Allow, Deny, NotApplicable } from "../Decision.js";
|
|
2
|
+
import { exceeded, residualNodes } from "./limits.js";
|
|
3
|
+
import { toNode } from "./nodes.js";
|
|
4
|
+
import { and, not, or } from "./algebra.js";
|
|
5
|
+
import { kindsIn, satisfiable } from "./search.js";
|
|
6
|
+
import { kindOf } from "./resource.js";
|
|
7
|
+
import { always as always$1, builder, never as never$1, partial } from "../Condition.js";
|
|
8
|
+
import { ruleIssues } from "./bind.js";
|
|
9
|
+
import { descriptorOf } from "./domain.js";
|
|
10
|
+
import { matchesTarget } from "./rule.js";
|
|
11
|
+
import { rulesOf } from "./policy-nodes.js";
|
|
12
|
+
import { nodeOf } from "./policy.js";
|
|
13
|
+
import { decideNode, fitsKind } from "./decide.js";
|
|
14
|
+
import * as Effect from "effect/Effect";
|
|
15
|
+
import * as Data from "effect/Data";
|
|
16
|
+
import * as Arr from "effect/Array";
|
|
17
|
+
// src/internal/analysis.ts
|
|
18
|
+
/**
|
|
19
|
+
* The residual conditions and the coverage of a policy. The types are in
|
|
20
|
+
* `Policy.ts`.
|
|
21
|
+
*/
|
|
22
|
+
const always = always$1;
|
|
23
|
+
const never = never$1;
|
|
24
|
+
const limit = (node) => {
|
|
25
|
+
if (node.size > 1e4) throw exceeded("A residual condition", "residualNodes", "nodes", node.size, residualNodes);
|
|
26
|
+
return node;
|
|
27
|
+
};
|
|
28
|
+
const build = {
|
|
29
|
+
and: (values) => limit(and(values)),
|
|
30
|
+
or: (values) => limit(or(values)),
|
|
31
|
+
not: (value) => value.pipe(not, limit)
|
|
32
|
+
};
|
|
33
|
+
const makeCondition = (value, context) => !matchesTarget(value, context) ? never : value.when === void 0 ? always : partial(value.when, context.input);
|
|
34
|
+
const ruleCondition = (value, context) => context.conditions.get(value) ?? makeCondition(value, context);
|
|
35
|
+
/**
|
|
36
|
+
* Gives the conditions for "the first rule that applies is an allow rule" and
|
|
37
|
+
* "the first rule that applies is a deny rule".
|
|
38
|
+
*/
|
|
39
|
+
const firstApplicableResidual = (values, context) => {
|
|
40
|
+
const { and, not, or } = build;
|
|
41
|
+
const runs = Arr.isReadonlyArrayNonEmpty(values) ? Arr.groupWith(values, (a, b) => a._tag === b._tag) : [];
|
|
42
|
+
return Arr.reduceRight(runs, {
|
|
43
|
+
allow: never,
|
|
44
|
+
deny: never
|
|
45
|
+
}, (rest, run) => {
|
|
46
|
+
const applies = or(Arr.map(run, (value) => ruleCondition(value, context)));
|
|
47
|
+
return run[0]._tag === "Allow" ? {
|
|
48
|
+
allow: or([applies, rest.allow]),
|
|
49
|
+
deny: and([not(applies), rest.deny])
|
|
50
|
+
} : {
|
|
51
|
+
allow: and([not(applies), rest.allow]),
|
|
52
|
+
deny: or([applies, rest.deny])
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Gives the residual pair of a node. A shared child is worked again for each
|
|
58
|
+
* place. The limits of the nodes and of a residual keep this work small.
|
|
59
|
+
*/
|
|
60
|
+
const residualNode = (node, context) => {
|
|
61
|
+
const { and, not, or } = build;
|
|
62
|
+
switch (node._tag) {
|
|
63
|
+
case "Rules": switch (node.algorithm) {
|
|
64
|
+
case "denyOverrides": {
|
|
65
|
+
const deny = or(node.rules.filter((value) => value._tag === "Deny").map((value) => ruleCondition(value, context)));
|
|
66
|
+
return {
|
|
67
|
+
allow: and([not(deny), or(node.rules.filter((value) => value._tag === "Allow").map((value) => ruleCondition(value, context)))]),
|
|
68
|
+
deny
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
case "permitOverrides": {
|
|
72
|
+
const allow = or(node.rules.filter((value) => value._tag === "Allow").map((value) => ruleCondition(value, context)));
|
|
73
|
+
return {
|
|
74
|
+
allow,
|
|
75
|
+
deny: and([not(allow), or(node.rules.filter((value) => value._tag === "Deny").map((value) => ruleCondition(value, context)))])
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
case "firstApplicable": return firstApplicableResidual(node.rules, context);
|
|
79
|
+
case "lastApplicable": return firstApplicableResidual(node.rules.slice().reverse(), context);
|
|
80
|
+
}
|
|
81
|
+
case "All": {
|
|
82
|
+
if (node.policies.length === 0) return {
|
|
83
|
+
allow: never,
|
|
84
|
+
deny: never
|
|
85
|
+
};
|
|
86
|
+
const pairs = node.policies.map((child) => residualNode(child, context));
|
|
87
|
+
const deny = or(pairs.map((pair) => pair.deny));
|
|
88
|
+
return {
|
|
89
|
+
allow: and([...pairs.map((pair) => pair.allow), not(deny)]),
|
|
90
|
+
deny
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
case "Any": {
|
|
94
|
+
const pairs = node.policies.map((child) => residualNode(child, context));
|
|
95
|
+
const allow = or(pairs.map((pair) => pair.allow));
|
|
96
|
+
return {
|
|
97
|
+
allow,
|
|
98
|
+
deny: and([not(allow), or(pairs.map((pair) => pair.deny))])
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
case "FirstOf": return Arr.reduceRight(node.policies, {
|
|
102
|
+
allow: never,
|
|
103
|
+
deny: never
|
|
104
|
+
}, (rest, child) => {
|
|
105
|
+
const pair = residualNode(child, context);
|
|
106
|
+
return {
|
|
107
|
+
allow: or([pair.allow, and([not(pair.deny), rest.allow])]),
|
|
108
|
+
deny: or([pair.deny, and([not(pair.allow), rest.deny])])
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
case "Ceiling": {
|
|
112
|
+
const cap = residualNode(node.cap, context);
|
|
113
|
+
const inner = residualNode(node.policy, context);
|
|
114
|
+
return {
|
|
115
|
+
allow: and([not(cap.deny), inner.allow]),
|
|
116
|
+
deny: or([cap.deny, inner.deny])
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
/** The name of the predicate of a residual condition that runs `decide`. */
|
|
122
|
+
const policyPredicate = "policy";
|
|
123
|
+
/** Tests if a rule that the request may use has a predicate. */
|
|
124
|
+
const usesPredicate = (node, context) => {
|
|
125
|
+
if (!node.opaque) return false;
|
|
126
|
+
switch (node._tag) {
|
|
127
|
+
case "Rules": return node.rules.some((value) => value.when !== void 0 && toNode(value.when).opaque && matchesTarget(value, context));
|
|
128
|
+
case "All":
|
|
129
|
+
case "Any":
|
|
130
|
+
case "FirstOf": return node.policies.some((child) => usesPredicate(child, context));
|
|
131
|
+
case "Ceiling": return usesPredicate(node.cap, context) || usesPredicate(node.policy, context);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Makes the scope of a request, with the principal in it. A scope comes from
|
|
136
|
+
* code, so a scope that does not fit the domain is a defect. It is bound as a
|
|
137
|
+
* rule for the kind and the action of the request is.
|
|
138
|
+
*/
|
|
139
|
+
const scopeOf = (request, domain, input) => {
|
|
140
|
+
if (request.within === void 0) return always;
|
|
141
|
+
const ast = request.within(builder());
|
|
142
|
+
if (toNode(ast).opaque) throw new globalThis.Error("A scope can not have a predicate");
|
|
143
|
+
const issues = ruleIssues(domain, {
|
|
144
|
+
actions: [request.action],
|
|
145
|
+
resources: [request.kind],
|
|
146
|
+
when: ast
|
|
147
|
+
}, ["within"]);
|
|
148
|
+
if (issues.length > 0) throw new globalThis.Error(`A scope does not fit the domain: ${issues.map((issue) => issue.issue).join("; ")}`);
|
|
149
|
+
return partial(ast, input);
|
|
150
|
+
};
|
|
151
|
+
const residualsAny = (policy, request) => scopedResiduals(policy, request);
|
|
152
|
+
const scopedResiduals = (policy, request) => {
|
|
153
|
+
const descriptor = descriptorOf(policy.domain, request.kind);
|
|
154
|
+
if (descriptor === void 0 || !fitsKind(descriptor, request)) return {
|
|
155
|
+
scope: never,
|
|
156
|
+
allow: never,
|
|
157
|
+
deny: never,
|
|
158
|
+
catalog: void 0
|
|
159
|
+
};
|
|
160
|
+
const input = {
|
|
161
|
+
principal: request.principal,
|
|
162
|
+
path: descriptor.path
|
|
163
|
+
};
|
|
164
|
+
const scope = scopeOf(request, policy.domain, input);
|
|
165
|
+
const and = build.and;
|
|
166
|
+
const inScope = (pair) => ({
|
|
167
|
+
scope,
|
|
168
|
+
allow: and([scope, pair.allow]),
|
|
169
|
+
deny: and([scope, pair.deny]),
|
|
170
|
+
catalog: descriptor.catalog
|
|
171
|
+
});
|
|
172
|
+
if (scope._tag === "False") return inScope({
|
|
173
|
+
allow: never,
|
|
174
|
+
deny: never
|
|
175
|
+
});
|
|
176
|
+
const target = {
|
|
177
|
+
action: request.action,
|
|
178
|
+
kind: request.kind,
|
|
179
|
+
field: request.field,
|
|
180
|
+
input,
|
|
181
|
+
descriptor
|
|
182
|
+
};
|
|
183
|
+
const context = {
|
|
184
|
+
...target,
|
|
185
|
+
conditions: new Map(Arr.map(policy.pipe(nodeOf, rulesOf), (value) => [value, makeCondition(value, target)]))
|
|
186
|
+
};
|
|
187
|
+
if (!usesPredicate(nodeOf(policy), context)) return inScope(residualNode(nodeOf(policy), context));
|
|
188
|
+
const decision = (value) => decideNode(nodeOf(policy), {
|
|
189
|
+
action: request.action,
|
|
190
|
+
kind: request.kind,
|
|
191
|
+
field: request.field,
|
|
192
|
+
input: {
|
|
193
|
+
value,
|
|
194
|
+
principal: request.principal,
|
|
195
|
+
path: descriptor.path
|
|
196
|
+
},
|
|
197
|
+
descriptor,
|
|
198
|
+
trace: void 0
|
|
199
|
+
}, []);
|
|
200
|
+
const test = (tag) => builder().predicate(policyPredicate, (value) => kindOf(value) === request.kind ? Effect.map(decision(value), (result) => result._tag === tag) : Effect.succeed(false));
|
|
201
|
+
return inScope({
|
|
202
|
+
allow: test("Allow"),
|
|
203
|
+
deny: test("Deny")
|
|
204
|
+
});
|
|
205
|
+
};
|
|
206
|
+
/** Gives the condition on resource values for which the policy allows. */
|
|
207
|
+
const residual = (policy, request) => residualsAny(policy, request).allow;
|
|
208
|
+
const coverages = Data.taggedEnum();
|
|
209
|
+
/** Searches for a value of the kind of a scope that makes a condition true. */
|
|
210
|
+
const search = (scoped, ast) => satisfiable(ast, kindsIn(scoped.catalog));
|
|
211
|
+
/** The values in the scope that the policy does not allow. */
|
|
212
|
+
const refusedOf = (scoped) => and([scoped.scope, not(scoped.allow)]);
|
|
213
|
+
const coverageAny = (policy, request) => {
|
|
214
|
+
const scoped = scopedResiduals(policy, request);
|
|
215
|
+
const allowed = scoped.allow;
|
|
216
|
+
const some = search(scoped, allowed);
|
|
217
|
+
if (some._tag === "Unsatisfiable") return coverages.None({ condition: never });
|
|
218
|
+
if (some._tag === "Unknown") return coverages.Unknown({
|
|
219
|
+
condition: allowed,
|
|
220
|
+
reason: some.reason
|
|
221
|
+
});
|
|
222
|
+
const missing = search(scoped, refusedOf(scoped));
|
|
223
|
+
if (missing._tag === "Unsatisfiable") return coverages.All({ condition: scoped.scope });
|
|
224
|
+
if (missing._tag === "Unknown") return coverages.Unknown({
|
|
225
|
+
condition: allowed,
|
|
226
|
+
reason: missing.reason
|
|
227
|
+
});
|
|
228
|
+
return coverages.Some({
|
|
229
|
+
condition: allowed,
|
|
230
|
+
allowed: some.example,
|
|
231
|
+
refused: missing.example
|
|
232
|
+
});
|
|
233
|
+
};
|
|
234
|
+
/** Decides a request about every value in a scope. */
|
|
235
|
+
const decideKind = (policy, request) => {
|
|
236
|
+
const scoped = scopedResiduals(policy, request);
|
|
237
|
+
switch (search(scoped, refusedOf(scoped))._tag) {
|
|
238
|
+
case "Unknown": return Deny({});
|
|
239
|
+
case "Unsatisfiable": return search(scoped, scoped.scope)._tag === "Satisfiable" ? Allow({}) : Deny({});
|
|
240
|
+
case "Satisfiable": return search(scoped, scoped.deny)._tag === "Unsatisfiable" ? NotApplicable : Deny({});
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
export { coverageAny, decideKind, residual, residualsAny };
|
|
244
|
+
|
|
245
|
+
//# sourceMappingURL=analysis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analysis.js","names":["Condition.always","Condition.never","Limits.exceeded","Limits.residualNodes","Algebra.and","Algebra.or","Algebra.not","RuleData.matchesTarget","Condition.partial","Nodes.toNode","Condition.builder","Bind.ruleIssues","DomainData.descriptorOf","PolicyNodes.rulesOf","ResourceData.kindOf","Search.satisfiable","Search.kindsIn","Decision.Deny","Decision.Allow","Decision.NotApplicable"],"sources":["../../src/internal/analysis.ts"],"sourcesContent":["/**\n * The residual conditions and the coverage of a policy. The types are in\n * `Policy.ts`.\n */\n// A policy keeps the error type and the service type of its predicates in the\n// public types. The code below works on the nodes with no types, where they\n// are `unknown`, so the language service check for `unknown` in an Effect is\n// off here.\n// @effect-diagnostics anyUnknownInErrorContext:off\nimport * as Data from \"effect/Data\"\nimport * as Arr from \"effect/Array\"\nimport * as Effect from \"effect/Effect\"\nimport * 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 Resource from \"../Resource.ts\"\nimport * as Algebra from \"./algebra.ts\"\nimport * as Bind from \"./bind.ts\"\nimport type * as Catalog from \"./catalog.ts\"\nimport { decideNode, fitsKind } from \"./decide.ts\"\nimport * as DomainData from \"./domain.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport * as PolicyNodes from \"./policy-nodes.ts\"\nimport { nodeOf } from \"./policy.ts\"\nimport * as ResourceData from \"./resource.ts\"\nimport * as RuleData from \"./rule.ts\"\nimport * as Search from \"./search.ts\"\n\ntype Any = Policy.Any\n\ntype Ast = Condition.Ast\n\nconst always: Ast = Condition.always\nconst never: Ast = Condition.never\n\n/**\n * Makes conditions for one residual, with no types. Each step makes the\n * condition simple, and it stops the work when the condition has more nodes\n * than `residualNodes`. A policy tree may use a condition of a child two\n * times, in its allow condition and in its deny condition, so a small policy\n * may give a very large condition.\n */\ninterface Build {\n\t// A list goes in as an array, not as arguments: a call may take only so\n\t// many arguments, and a policy may have more rules.\n\treadonly and: (values: ReadonlyArray<Ast>) => Ast\n\treadonly or: (values: ReadonlyArray<Ast>) => Ast\n\treadonly not: (value: Ast) => Ast\n}\n\nconst limit = (node: Nodes.Node): Ast => {\n\tif (node.size > Limits.residualNodes) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A residual condition\",\n\t\t\t\"residualNodes\",\n\t\t\t\"nodes\",\n\t\t\tnode.size,\n\t\t\tLimits.residualNodes,\n\t\t)\n\t}\n\treturn node\n}\n\nconst build: Build = {\n\tand: (values) => limit(Algebra.and(values as ReadonlyArray<Nodes.Node>)),\n\tor: (values) => limit(Algebra.or(values as ReadonlyArray<Nodes.Node>)),\n\tnot: (value) => (value as Nodes.Node).pipe(Algebra.not, limit),\n}\n\n/** The two residual conditions of a node, with no types. */\ninterface Pair {\n\treadonly allow: Ast\n\treadonly deny: Ast\n}\n\ninterface ResidualContext {\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\t/** The resource of the kind, for the fields that a rule may grant. */\n\treadonly descriptor: Resource.Descriptor\n\t/**\n\t * The condition of each rule, with the principal in it. They are made\n\t * first, from a flat list of the rules, so the walk does not go down the\n\t * levels of the tree and of a condition at once. It is a native map,\n\t * because it must find a rule by the rule itself: two rules with the same\n\t * data are `Equal`.\n\t */\n\treadonly conditions: Map<RuleData.Rule, Ast>\n}\n\nconst makeCondition = (\n\tvalue: RuleData.Rule,\n\tcontext: Omit<ResidualContext, \"conditions\">,\n): Ast =>\n\t!RuleData.matchesTarget(value, context)\n\t\t? never\n\t\t: value.when === undefined\n\t\t\t? always\n\t\t\t: Condition.partial(value.when as Condition.Any, context.input)\n\nconst ruleCondition = (value: RuleData.Rule, context: ResidualContext): Ast =>\n\tcontext.conditions.get(value) ?? makeCondition(value, context)\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<RuleData.Rule>,\n\tcontext: ResidualContext,\n): Pair => {\n\tconst { and, not, or } = build\n\t// The rules go in runs of one tag, from the last run to the first. The\n\t// conditions of a run go into one node at once. With one node for each\n\t// rule, each step would flatten the whole condition again.\n\tconst runs = Arr.isReadonlyArrayNonEmpty(values)\n\t\t? Arr.groupWith(values, (a, b) => a._tag === b._tag)\n\t\t: []\n\treturn Arr.reduceRight(\n\t\truns,\n\t\t{ allow: never, deny: never },\n\t\t(rest: Pair, run): Pair => {\n\t\t\tconst applies = or(Arr.map(run, (value) => ruleCondition(value, context)))\n\t\t\treturn run[0]._tag === \"Allow\"\n\t\t\t\t? {\n\t\t\t\t\t\tallow: or([applies, rest.allow]),\n\t\t\t\t\t\tdeny: and([not(applies), rest.deny]),\n\t\t\t\t\t}\n\t\t\t\t: {\n\t\t\t\t\t\tallow: and([not(applies), rest.allow]),\n\t\t\t\t\t\tdeny: or([applies, rest.deny]),\n\t\t\t\t\t}\n\t\t},\n\t)\n}\n\n/**\n * Gives the residual pair of a node. A shared child is worked again for each\n * place. The limits of the nodes and of a residual keep this work small.\n */\nconst residualNode = (\n\tnode: PolicyNodes.Node,\n\tcontext: ResidualContext,\n): Pair => {\n\tconst { and, not, or } = build\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\tnode.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\tnot(deny),\n\t\t\t\t\t\tor(\n\t\t\t\t\t\t\tnode.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])\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\tnode.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\tnot(allow),\n\t\t\t\t\t\tor(\n\t\t\t\t\t\t\tnode.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])\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([not(allow), or(pairs.map((pair) => pair.deny))])\n\t\t\treturn { allow, deny }\n\t\t}\n\t\tcase \"FirstOf\": {\n\t\t\t// The first child that gives an answer wins.\n\t\t\treturn Arr.reduceRight(\n\t\t\t\tnode.policies,\n\t\t\t\t{ allow: never, deny: never },\n\t\t\t\t(rest: Pair, child): Pair => {\n\t\t\t\t\tconst pair = residualNode(child, context)\n\t\t\t\t\treturn {\n\t\t\t\t\t\tallow: or([pair.allow, and([not(pair.deny), rest.allow])]),\n\t\t\t\t\t\tdeny: or([pair.deny, and([not(pair.allow), rest.deny])]),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t)\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/** The name of the predicate of a residual condition that runs `decide`. */\nconst policyPredicate = \"policy\"\n\n/** What {@link usesPredicate} needs: the request. */\ninterface PredicateContext {\n\treadonly action: string\n\treadonly kind: string\n\treadonly field: string | undefined\n\treadonly descriptor: Resource.Descriptor\n}\n\n/** Tests if a rule that the request may use has a predicate. */\nconst usesPredicate = (\n\tnode: PolicyNodes.Node,\n\tcontext: PredicateContext,\n): boolean => {\n\tif (!node.opaque) {\n\t\treturn false\n\t}\n\tswitch (node._tag) {\n\t\tcase \"Rules\":\n\t\t\treturn node.rules.some(\n\t\t\t\t(value) =>\n\t\t\t\t\tvalue.when !== undefined &&\n\t\t\t\t\tNodes.toNode(value.when).opaque &&\n\t\t\t\t\tRuleData.matchesTarget(value, context),\n\t\t\t)\n\t\tcase \"All\":\n\t\tcase \"Any\":\n\t\tcase \"FirstOf\":\n\t\t\treturn node.policies.some((child) => usesPredicate(child, context))\n\t\tcase \"Ceiling\":\n\t\t\treturn (\n\t\t\t\tusesPredicate(node.cap, context) || usesPredicate(node.policy, context)\n\t\t\t)\n\t}\n}\n\n/**\n * Makes the scope of a request, with the principal in it. A scope comes from\n * code, so a scope that does not fit the domain is a defect. It is bound as a\n * rule for the kind and the action of the request is.\n */\nconst scopeOf = (\n\trequest: ScopedRequest,\n\tdomain: Domain.Any,\n\tinput: ResidualContext[\"input\"],\n): Ast => {\n\tif (request.within === undefined) {\n\t\treturn always\n\t}\n\t// The builder stops a value that a condition can not hold, such as\n\t// `undefined` from a route parameter. A predicate could make a check pass\n\t// on the wrong values, so a scope with one is a defect too.\n\tconst ast = (request.within as (builder: unknown) => Ast)(Condition.builder())\n\tif (Nodes.toNode(ast).opaque) {\n\t\tthrow new globalThis.Error(\"A scope can not have a predicate\")\n\t}\n\tconst issues = Bind.ruleIssues(\n\t\tdomain,\n\t\t{ actions: [request.action], resources: [request.kind], when: ast },\n\t\t[\"within\"],\n\t)\n\tif (issues.length > 0) {\n\t\tthrow new globalThis.Error(\n\t\t\t`A scope does not fit the domain: ${issues.map((issue) => issue.issue).join(\"; \")}`,\n\t\t)\n\t}\n\treturn Condition.partial(ast as Condition.Condition, input)\n}\n\n/** A request about a kind with no types, with the scope of a target. */\nexport interface ScopedRequest {\n\treadonly principal: object\n\treadonly action: string\n\treadonly kind: string\n\treadonly field?: string | undefined\n\treadonly within?: unknown\n}\n\n/** The scope of a request, and the residual conditions inside it. */\ninterface Scoped extends Pair {\n\treadonly scope: Ast\n\t/** The catalog of the kind, when the request fits the kind. */\n\treadonly catalog: Catalog.Catalog | undefined\n}\n\nexport const residualsAny = (policy: Any, request: ScopedRequest): Pair =>\n\tscopedResiduals(policy, request)\n\nconst scopedResiduals = (policy: Any, request: ScopedRequest): Scoped => {\n\tconst descriptor = DomainData.descriptorOf(policy.domain, request.kind)\n\tif (descriptor === undefined || !fitsKind(descriptor, request)) {\n\t\treturn { scope: never, allow: never, deny: never, catalog: undefined }\n\t}\n\tconst input = { principal: request.principal, path: descriptor.path }\n\tconst scope = scopeOf(request, policy.domain, input)\n\tconst and = build.and\n\tconst inScope = (pair: Pair): Scoped => ({\n\t\tscope,\n\t\tallow: and([scope, pair.allow]),\n\t\tdeny: and([scope, pair.deny]),\n\t\tcatalog: descriptor.catalog,\n\t})\n\tif (scope._tag === \"False\") {\n\t\treturn inScope({ allow: never, deny: never })\n\t}\n\tconst target = {\n\t\taction: request.action,\n\t\tkind: request.kind,\n\t\tfield: request.field,\n\t\tinput,\n\t\tdescriptor,\n\t}\n\tconst context: ResidualContext = {\n\t\t...target,\n\t\tconditions: new Map(\n\t\t\tArr.map(policy.pipe(nodeOf, PolicyNodes.rulesOf), (value) => [\n\t\t\t\tvalue,\n\t\t\t\tmakeCondition(value, target),\n\t\t\t]),\n\t\t),\n\t}\n\t// The type of the residual of a plain policy says that it has no\n\t// predicate, so it can not be the predicate below. A residual past the\n\t// limit is a defect.\n\tif (!usesPredicate(nodeOf(policy), context)) {\n\t\treturn inScope(residualNode(nodeOf(policy), context))\n\t}\n\t// A predicate may fail, and it may give another answer each time it runs.\n\t// A tree of `And` and `Or` nodes can not run the predicates in the same\n\t// order and the same number of times as `decide`. So the residual is one\n\t// predicate that runs `decide` for each value.\n\tconst decision = (value: object) =>\n\t\tdecideNode(\n\t\t\tnodeOf(policy),\n\t\t\t{\n\t\t\t\taction: request.action,\n\t\t\t\tkind: request.kind,\n\t\t\t\tfield: request.field,\n\t\t\t\tinput: {\n\t\t\t\t\tvalue,\n\t\t\t\t\tprincipal: request.principal,\n\t\t\t\t\tpath: descriptor.path,\n\t\t\t\t},\n\t\t\t\tdescriptor,\n\t\t\t\ttrace: undefined,\n\t\t\t},\n\t\t\t[],\n\t\t)\n\t// A value of another kind is not a value that the residual is about, so\n\t// the rules of this kind must not run on it.\n\tconst test = (tag: Decision.Decision[\"_tag\"]): Ast =>\n\t\tCondition.builder<any, any, any>().predicate(policyPredicate, (value) =>\n\t\t\tResourceData.kindOf(value) === request.kind\n\t\t\t\t? Effect.map(decision(value), (result) => result._tag === tag)\n\t\t\t\t: Effect.succeed(false),\n\t\t)\n\t// The scope comes first in each `And`, so the policy runs only on values\n\t// in the scope.\n\treturn inScope({ allow: test(\"Allow\"), deny: test(\"Deny\") })\n}\n\n/** Gives the condition on resource values for which the policy allows. */\nexport const residual = (policy: Any, request: ScopedRequest): Ast =>\n\tresidualsAny(policy, request).allow\n\n/** A {@link Policy.Coverage} with no types. */\nexport type AnyCoverage = Data.TaggedEnum<{\n\tAll: { readonly condition: Ast }\n\tNone: { readonly condition: Ast }\n\tSome: {\n\t\treadonly condition: Ast\n\t\treadonly allowed: Condition.Example\n\t\treadonly refused: Condition.Example\n\t}\n\tUnknown: {\n\t\treadonly condition: Ast\n\t\treadonly reason: \"predicate\" | \"budget\"\n\t}\n}>\n\nconst coverages = Data.taggedEnum<AnyCoverage>()\n\n/** Searches for a value of the kind of a scope that makes a condition true. */\nconst search = (scoped: Scoped, ast: Ast): Condition.Satisfiability =>\n\tSearch.satisfiable(ast as Nodes.Node, Search.kindsIn(scoped.catalog))\n\n/** The values in the scope that the policy does not allow. */\nconst refusedOf = (scoped: Scoped): Ast =>\n\t// Only two nodes more than the residual, so it needs no size limit.\n\tAlgebra.and([\n\t\tscoped.scope as Nodes.Node,\n\t\tAlgebra.not(scoped.allow as Nodes.Node),\n\t])\n\nexport const coverageAny = (\n\tpolicy: Any,\n\trequest: ScopedRequest,\n): AnyCoverage => {\n\tconst scoped = scopedResiduals(policy, request)\n\tconst allowed = scoped.allow\n\tconst some = search(scoped, allowed)\n\tif (some._tag === \"Unsatisfiable\") {\n\t\treturn coverages.None({ condition: never })\n\t}\n\tif (some._tag === \"Unknown\") {\n\t\treturn coverages.Unknown({ condition: allowed, reason: some.reason })\n\t}\n\tconst missing = search(scoped, refusedOf(scoped))\n\tif (missing._tag === \"Unsatisfiable\") {\n\t\treturn coverages.All({ condition: scoped.scope })\n\t}\n\tif (missing._tag === \"Unknown\") {\n\t\treturn coverages.Unknown({ condition: allowed, reason: missing.reason })\n\t}\n\treturn coverages.Some({\n\t\tcondition: allowed,\n\t\tallowed: some.example,\n\t\trefused: missing.example,\n\t})\n}\n\n/** Decides a request about every value in a scope. */\nexport const decideKind = (\n\tpolicy: Any,\n\trequest: ScopedRequest,\n): Decision.Decision => {\n\tconst scoped = scopedResiduals(policy, request)\n\tconst missing = search(scoped, refusedOf(scoped))\n\tswitch (missing._tag) {\n\t\tcase \"Unknown\":\n\t\t\treturn Decision.Deny({})\n\t\tcase \"Unsatisfiable\":\n\t\t\treturn search(scoped, scoped.scope)._tag === \"Satisfiable\"\n\t\t\t\t? Decision.Allow({})\n\t\t\t\t: Decision.Deny({})\n\t\tcase \"Satisfiable\":\n\t\t\t// A refused value is in the scope, so the scope has a value.\n\t\t\treturn search(scoped, scoped.deny)._tag === \"Unsatisfiable\"\n\t\t\t\t? Decision.NotApplicable\n\t\t\t\t: Decision.Deny({})\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAkCA,MAAM,SAAcA;AACpB,MAAM,QAAaC;AAiBnB,MAAM,SAAS,SAA0B;CACxC,IAAI,KAAK,OAAO,KACf,MAAMC,SACL,wBACA,iBACA,SACA,KAAK,MACLC,aACD;CAED,OAAO;AACR;AAEA,MAAM,QAAe;CACpB,MAAM,WAAW,MAAMC,IAAY,MAAmC,CAAC;CACvE,KAAK,WAAW,MAAMC,GAAW,MAAmC,CAAC;CACrE,MAAM,UAAW,MAAqB,KAAKC,KAAa,KAAK;AAC9D;AA4BA,MAAM,iBACL,OACA,YAEA,CAACC,cAAuB,OAAO,OAAO,IACnC,QACA,MAAM,SAAS,KAAA,IACd,SACAC,QAAkB,MAAM,MAAuB,QAAQ,KAAK;AAEjE,MAAM,iBAAiB,OAAsB,YAC5C,QAAQ,WAAW,IAAI,KAAK,KAAK,cAAc,OAAO,OAAO;;;;;AAM9D,MAAM,2BACL,QACA,YACU;CACV,MAAM,EAAE,KAAK,KAAK,OAAO;CAIzB,MAAM,OAAO,IAAI,wBAAwB,MAAM,IAC5C,IAAI,UAAU,SAAS,GAAG,MAAM,EAAE,SAAS,EAAE,IAAI,IACjD,CAAC;CACJ,OAAO,IAAI,YACV,MACA;EAAE,OAAO;EAAO,MAAM;CAAM,IAC3B,MAAY,QAAc;EAC1B,MAAM,UAAU,GAAG,IAAI,IAAI,MAAM,UAAU,cAAc,OAAO,OAAO,CAAC,CAAC;EACzE,OAAO,IAAI,EAAE,CAAC,SAAS,UACpB;GACA,OAAO,GAAG,CAAC,SAAS,KAAK,KAAK,CAAC;GAC/B,MAAM,IAAI,CAAC,IAAI,OAAO,GAAG,KAAK,IAAI,CAAC;EACpC,IACC;GACA,OAAO,IAAI,CAAC,IAAI,OAAO,GAAG,KAAK,KAAK,CAAC;GACrC,MAAM,GAAG,CAAC,SAAS,KAAK,IAAI,CAAC;EAC9B;CACH,CACD;AACD;;;;;AAMA,MAAM,gBACL,MACA,YACU;CACV,MAAM,EAAE,KAAK,KAAK,OAAO;CACzB,QAAQ,KAAK,MAAb;EACC,KAAK,SACJ,QAAQ,KAAK,WAAb;GACC,KAAK,iBAAiB;IACrB,MAAM,OAAO,GACZ,KAAK,MACH,QAAQ,UAAU,MAAM,SAAS,MAAM,CAAC,CACxC,KAAK,UAAU,cAAc,OAAO,OAAO,CAAC,CAC/C;IASA,OAAO;KAAE,OARK,IAAI,CACjB,IAAI,IAAI,GACR,GACC,KAAK,MACH,QAAQ,UAAU,MAAM,SAAS,OAAO,CAAC,CACzC,KAAK,UAAU,cAAc,OAAO,OAAO,CAAC,CAC/C,CACD,CACa;KAAG;IAAK;GACtB;GACA,KAAK,mBAAmB;IACvB,MAAM,QAAQ,GACb,KAAK,MACH,QAAQ,UAAU,MAAM,SAAS,OAAO,CAAC,CACzC,KAAK,UAAU,cAAc,OAAO,OAAO,CAAC,CAC/C;IASA,OAAO;KAAE;KAAO,MARH,IAAI,CAChB,IAAI,KAAK,GACT,GACC,KAAK,MACH,QAAQ,UAAU,MAAM,SAAS,MAAM,CAAC,CACxC,KAAK,UAAU,cAAc,OAAO,OAAO,CAAC,CAC/C,CACD,CACmB;IAAE;GACtB;GACA,KAAK,mBACJ,OAAO,wBAAwB,KAAK,OAAO,OAAO;GACnD,KAAK,kBACJ,OAAO,wBAAwB,KAAK,MAAM,MAAM,CAAC,CAAC,QAAQ,GAAG,OAAO;EACtE;EAGD,KAAK,OAAO;GACX,IAAI,KAAK,SAAS,WAAW,GAC5B,OAAO;IAAE,OAAO;IAAO,MAAM;GAAM;GAEpC,MAAM,QAAQ,KAAK,SAAS,KAAK,UAAU,aAAa,OAAO,OAAO,CAAC;GACvE,MAAM,OAAO,GAAG,MAAM,KAAK,SAAS,KAAK,IAAI,CAAC;GAE9C,OAAO;IAAE,OADK,IAAI,CAAC,GAAG,MAAM,KAAK,SAAS,KAAK,KAAK,GAAG,IAAI,IAAI,CAAC,CACnD;IAAG;GAAK;EACtB;EACA,KAAK,OAAO;GACX,MAAM,QAAQ,KAAK,SAAS,KAAK,UAAU,aAAa,OAAO,OAAO,CAAC;GACvE,MAAM,QAAQ,GAAG,MAAM,KAAK,SAAS,KAAK,KAAK,CAAC;GAEhD,OAAO;IAAE;IAAO,MADH,IAAI,CAAC,IAAI,KAAK,GAAG,GAAG,MAAM,KAAK,SAAS,KAAK,IAAI,CAAC,CAAC,CAC7C;GAAE;EACtB;EACA,KAAK,WAEJ,OAAO,IAAI,YACV,KAAK,UACL;GAAE,OAAO;GAAO,MAAM;EAAM,IAC3B,MAAY,UAAgB;GAC5B,MAAM,OAAO,aAAa,OAAO,OAAO;GACxC,OAAO;IACN,OAAO,GAAG,CAAC,KAAK,OAAO,IAAI,CAAC,IAAI,KAAK,IAAI,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC;IACzD,MAAM,GAAG,CAAC,KAAK,MAAM,IAAI,CAAC,IAAI,KAAK,KAAK,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC;GACxD;EACD,CACD;EAED,KAAK,WAAW;GACf,MAAM,MAAM,aAAa,KAAK,KAAK,OAAO;GAC1C,MAAM,QAAQ,aAAa,KAAK,QAAQ,OAAO;GAC/C,OAAO;IACN,OAAO,IAAI,CAAC,IAAI,IAAI,IAAI,GAAG,MAAM,KAAK,CAAC;IACvC,MAAM,GAAG,CAAC,IAAI,MAAM,MAAM,IAAI,CAAC;GAChC;EACD;CACD;AACD;;AAGA,MAAM,kBAAkB;;AAWxB,MAAM,iBACL,MACA,YACa;CACb,IAAI,CAAC,KAAK,QACT,OAAO;CAER,QAAQ,KAAK,MAAb;EACC,KAAK,SACJ,OAAO,KAAK,MAAM,MAChB,UACA,MAAM,SAAS,KAAA,KACfC,OAAa,MAAM,IAAI,CAAC,CAAC,UACzBF,cAAuB,OAAO,OAAO,CACvC;EACD,KAAK;EACL,KAAK;EACL,KAAK,WACJ,OAAO,KAAK,SAAS,MAAM,UAAU,cAAc,OAAO,OAAO,CAAC;EACnE,KAAK,WACJ,OACC,cAAc,KAAK,KAAK,OAAO,KAAK,cAAc,KAAK,QAAQ,OAAO;CAEzE;AACD;;;;;;AAOA,MAAM,WACL,SACA,QACA,UACS;CACT,IAAI,QAAQ,WAAW,KAAA,GACtB,OAAO;CAKR,MAAM,MAAO,QAAQ,OAAqCG,QAAkB,CAAC;CAC7E,IAAID,OAAa,GAAG,CAAC,CAAC,QACrB,MAAM,IAAI,WAAW,MAAM,kCAAkC;CAE9D,MAAM,SAASE,WACd,QACA;EAAE,SAAS,CAAC,QAAQ,MAAM;EAAG,WAAW,CAAC,QAAQ,IAAI;EAAG,MAAM;CAAI,GAClE,CAAC,QAAQ,CACV;CACA,IAAI,OAAO,SAAS,GACnB,MAAM,IAAI,WAAW,MACpB,oCAAoC,OAAO,KAAK,UAAU,MAAM,KAAK,CAAC,CAAC,KAAK,IAAI,GACjF;CAED,OAAOH,QAAkB,KAA4B,KAAK;AAC3D;AAkBA,MAAa,gBAAgB,QAAa,YACzC,gBAAgB,QAAQ,OAAO;AAEhC,MAAM,mBAAmB,QAAa,YAAmC;CACxE,MAAM,aAAaI,aAAwB,OAAO,QAAQ,QAAQ,IAAI;CACtE,IAAI,eAAe,KAAA,KAAa,CAAC,SAAS,YAAY,OAAO,GAC5D,OAAO;EAAE,OAAO;EAAO,OAAO;EAAO,MAAM;EAAO,SAAS,KAAA;CAAU;CAEtE,MAAM,QAAQ;EAAE,WAAW,QAAQ;EAAW,MAAM,WAAW;CAAK;CACpE,MAAM,QAAQ,QAAQ,SAAS,OAAO,QAAQ,KAAK;CACnD,MAAM,MAAM,MAAM;CAClB,MAAM,WAAW,UAAwB;EACxC;EACA,OAAO,IAAI,CAAC,OAAO,KAAK,KAAK,CAAC;EAC9B,MAAM,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC;EAC5B,SAAS,WAAW;CACrB;CACA,IAAI,MAAM,SAAS,SAClB,OAAO,QAAQ;EAAE,OAAO;EAAO,MAAM;CAAM,CAAC;CAE7C,MAAM,SAAS;EACd,QAAQ,QAAQ;EAChB,MAAM,QAAQ;EACd,OAAO,QAAQ;EACf;EACA;CACD;CACA,MAAM,UAA2B;EAChC,GAAG;EACH,YAAY,IAAI,IACf,IAAI,IAAI,OAAO,KAAK,QAAQC,OAAmB,IAAI,UAAU,CAC5D,OACA,cAAc,OAAO,MAAM,CAC5B,CAAC,CACF;CACD;CAIA,IAAI,CAAC,cAAc,OAAO,MAAM,GAAG,OAAO,GACzC,OAAO,QAAQ,aAAa,OAAO,MAAM,GAAG,OAAO,CAAC;CAMrD,MAAM,YAAY,UACjB,WACC,OAAO,MAAM,GACb;EACC,QAAQ,QAAQ;EAChB,MAAM,QAAQ;EACd,OAAO,QAAQ;EACf,OAAO;GACN;GACA,WAAW,QAAQ;GACnB,MAAM,WAAW;EAClB;EACA;EACA,OAAO,KAAA;CACR,GACA,CAAC,CACF;CAGD,MAAM,QAAQ,QACbH,QAAiC,CAAC,CAAC,UAAU,kBAAkB,UAC9DI,OAAoB,KAAK,MAAM,QAAQ,OACpC,OAAO,IAAI,SAAS,KAAK,IAAI,WAAW,OAAO,SAAS,GAAG,IAC3D,OAAO,QAAQ,KAAK,CACxB;CAGD,OAAO,QAAQ;EAAE,OAAO,KAAK,OAAO;EAAG,MAAM,KAAK,MAAM;CAAE,CAAC;AAC5D;;AAGA,MAAa,YAAY,QAAa,YACrC,aAAa,QAAQ,OAAO,CAAC,CAAC;AAiB/B,MAAM,YAAY,KAAK,WAAwB;;AAG/C,MAAM,UAAU,QAAgB,QAC/BC,YAAmB,KAAmBC,QAAe,OAAO,OAAO,CAAC;;AAGrE,MAAM,aAAa,WAElBZ,IAAY,CACX,OAAO,OACPE,IAAY,OAAO,KAAmB,CACvC,CAAC;AAEF,MAAa,eACZ,QACA,YACiB;CACjB,MAAM,SAAS,gBAAgB,QAAQ,OAAO;CAC9C,MAAM,UAAU,OAAO;CACvB,MAAM,OAAO,OAAO,QAAQ,OAAO;CACnC,IAAI,KAAK,SAAS,iBACjB,OAAO,UAAU,KAAK,EAAE,WAAW,MAAM,CAAC;CAE3C,IAAI,KAAK,SAAS,WACjB,OAAO,UAAU,QAAQ;EAAE,WAAW;EAAS,QAAQ,KAAK;CAAO,CAAC;CAErE,MAAM,UAAU,OAAO,QAAQ,UAAU,MAAM,CAAC;CAChD,IAAI,QAAQ,SAAS,iBACpB,OAAO,UAAU,IAAI,EAAE,WAAW,OAAO,MAAM,CAAC;CAEjD,IAAI,QAAQ,SAAS,WACpB,OAAO,UAAU,QAAQ;EAAE,WAAW;EAAS,QAAQ,QAAQ;CAAO,CAAC;CAExE,OAAO,UAAU,KAAK;EACrB,WAAW;EACX,SAAS,KAAK;EACd,SAAS,QAAQ;CAClB,CAAC;AACF;;AAGA,MAAa,cACZ,QACA,YACuB;CACvB,MAAM,SAAS,gBAAgB,QAAQ,OAAO;CAE9C,QADgB,OAAO,QAAQ,UAAU,MAAM,CACjC,CAAC,CAAC,MAAhB;EACC,KAAK,WACJ,OAAOW,KAAc,CAAC,CAAC;EACxB,KAAK,iBACJ,OAAO,OAAO,QAAQ,OAAO,KAAK,CAAC,CAAC,SAAS,gBAC1CC,MAAe,CAAC,CAAC,IACjBD,KAAc,CAAC,CAAC;EACpB,KAAK,eAEJ,OAAO,OAAO,QAAQ,OAAO,IAAI,CAAC,CAAC,SAAS,kBACzCE,gBACAF,KAAc,CAAC,CAAC;CACrB;AACD"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import "../Domain.js";
|
|
2
|
+
import "../Policy.js";
|
|
3
|
+
import "../Decision.js";
|
|
4
|
+
import "../Authorizer.js";
|
|
5
|
+
import "effect/Context";
|
|
6
|
+
import "effect/Effect";
|
|
7
|
+
import "effect/Layer";
|
|
8
|
+
import * as Schema from "effect/Schema";
|
|
9
|
+
import "effect/Duration";
|
|
10
|
+
declare const Forbidden_base: Schema.Class<Forbidden, Schema.TaggedStruct<"Forbidden", {
|
|
11
|
+
readonly action: Schema.String;
|
|
12
|
+
readonly kind: Schema.String;
|
|
13
|
+
readonly field: Schema.optionalKey<Schema.String>;
|
|
14
|
+
readonly reason: Schema.optionalKey<Schema.String>;
|
|
15
|
+
}>, import("effect/Cause").YieldableError>;
|
|
16
|
+
/** The error when a policy says no. */
|
|
17
|
+
export declare class Forbidden extends Forbidden_base {}
|
|
18
|
+
declare const PolicyUnavailable_base: Schema.Class<PolicyUnavailable, Schema.TaggedStruct<"PolicyUnavailable", {
|
|
19
|
+
readonly cause: Schema.Defect;
|
|
20
|
+
}>, import("effect/Cause").YieldableError>;
|
|
21
|
+
/** The error when the policy of the principal can not be loaded. */
|
|
22
|
+
export declare class PolicyUnavailable extends PolicyUnavailable_base {}
|
|
23
|
+
declare const Pending_base: Schema.Class<Pending, Schema.TaggedStruct<"Pending", {
|
|
24
|
+
readonly ticket: Schema.String;
|
|
25
|
+
readonly reason: Schema.optionalKey<Schema.String>;
|
|
26
|
+
}>, import("effect/Cause").YieldableError>;
|
|
27
|
+
/** The error when a decider must wait for an answer. */
|
|
28
|
+
export declare class Pending extends Pending_base {}
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=authorizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorizer.d.ts","names":[],"sources":["../../src/internal/authorizer.ts"],"mappings":";;;;;;;;;;;;;;;;qBA+Ba,kBAAkB;;;;;qBAUlB,0BAA0B;;;;;;qBAK1B,gBAAgB"}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { reason } from "../Decision.js";
|
|
2
|
+
import { kindOf } from "./resource.js";
|
|
3
|
+
import { descriptorOf } from "./domain.js";
|
|
4
|
+
import { provideContext } from "./policy.js";
|
|
5
|
+
import { decideAny, fitsKind, permittedFields } from "./decide.js";
|
|
6
|
+
import { coverageAny, decideKind } from "./analysis.js";
|
|
7
|
+
import * as Cache from "effect/Cache";
|
|
8
|
+
import * as Context from "effect/Context";
|
|
9
|
+
import * as Effect from "effect/Effect";
|
|
10
|
+
import * as Equal from "effect/Equal";
|
|
11
|
+
import * as Hash from "effect/Hash";
|
|
12
|
+
import * as Layer from "effect/Layer";
|
|
13
|
+
import * as Result from "effect/Result";
|
|
14
|
+
import * as Schema from "effect/Schema";
|
|
15
|
+
// src/internal/authorizer.ts
|
|
16
|
+
/**
|
|
17
|
+
* The values of authorizers: the errors, the service class and the layer.
|
|
18
|
+
* The types are in `Authorizer.ts`.
|
|
19
|
+
*/
|
|
20
|
+
/** The error when a policy says no. */
|
|
21
|
+
var Forbidden = class extends Schema.TaggedError("@nmnmcc/ability/Authorizer/Forbidden")("Forbidden", {
|
|
22
|
+
action: Schema.String,
|
|
23
|
+
kind: Schema.String,
|
|
24
|
+
field: Schema.optionalKey(Schema.String),
|
|
25
|
+
reason: Schema.optionalKey(Schema.String)
|
|
26
|
+
}) {};
|
|
27
|
+
/** The error when the policy of the principal can not be loaded. */
|
|
28
|
+
var PolicyUnavailable = class extends Schema.TaggedError("@nmnmcc/ability/Authorizer/PolicyUnavailable")("PolicyUnavailable", { cause: Schema.Defect() }) {};
|
|
29
|
+
/** The error when a decider must wait for an answer. */
|
|
30
|
+
var Pending = class extends Schema.TaggedError("@nmnmcc/ability/Authorizer/Pending")("Pending", {
|
|
31
|
+
ticket: Schema.String,
|
|
32
|
+
reason: Schema.optionalKey(Schema.String)
|
|
33
|
+
}) {};
|
|
34
|
+
/** Makes an authorizer service class for a domain. */
|
|
35
|
+
const Service = () => (id, options) => {
|
|
36
|
+
const Base = Context.Service()(id);
|
|
37
|
+
return class extends Base {
|
|
38
|
+
static domain = options.domain;
|
|
39
|
+
static principal = options.principal;
|
|
40
|
+
static decide = (target) => this.use((service) => service.decide(target));
|
|
41
|
+
static check = (target) => this.use((service) => service.check(target));
|
|
42
|
+
static guard = (target, options) => this.use((service) => service.guard(target, options));
|
|
43
|
+
static filter = (target) => this.use((service) => service.filter(target));
|
|
44
|
+
static coverage = (target) => this.use((service) => service.coverage(target));
|
|
45
|
+
static permittedFields = (target) => this.use((service) => service.permittedFields(target));
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* A cache key for a principal. Two keys are equal when they have the same
|
|
50
|
+
* text, so two principals with the same key share one policy. The key also
|
|
51
|
+
* keeps the principal, so the cache can load its policy. A plain `Data` value
|
|
52
|
+
* can not do both, so the key gives its own equality with `Equal.Equal`.
|
|
53
|
+
*/
|
|
54
|
+
var PrincipalKey = class PrincipalKey {
|
|
55
|
+
principal;
|
|
56
|
+
text;
|
|
57
|
+
constructor(principal, text) {
|
|
58
|
+
this.principal = principal;
|
|
59
|
+
this.text = text;
|
|
60
|
+
}
|
|
61
|
+
[Equal.symbol](that) {
|
|
62
|
+
return that instanceof PrincipalKey && that.text === this.text;
|
|
63
|
+
}
|
|
64
|
+
[Hash.symbol]() {
|
|
65
|
+
return Hash.string(this.text);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Makes the cache key of a principal from its JSON form. The key keeps the
|
|
70
|
+
* principal that the Schema decodes from that form, so the loader sees only
|
|
71
|
+
* the data that the key shows. A principal gets no key when it does not
|
|
72
|
+
* encode, or when the decoded value is not `Equal` to it. Two classes, or a
|
|
73
|
+
* string and a number, may have one JSON form, as in a union, and `Equal`
|
|
74
|
+
* tells them apart. So two principals with one key are equal, and the cache
|
|
75
|
+
* can not give one the policy of the other.
|
|
76
|
+
*/
|
|
77
|
+
/**
|
|
78
|
+
* Gives the JSON codec of a Schema, or `undefined` when the Schema has no JSON
|
|
79
|
+
* form, as a struct with a symbol key has none.
|
|
80
|
+
*/
|
|
81
|
+
const jsonCodecOf = (schema) => {
|
|
82
|
+
try {
|
|
83
|
+
return Schema.toCodecJson(schema);
|
|
84
|
+
} catch {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const jsonKey = (schema) => {
|
|
89
|
+
const codec = jsonCodecOf(schema);
|
|
90
|
+
if (codec === void 0) return () => void 0;
|
|
91
|
+
const encode = Schema.encodeUnknownResult(codec);
|
|
92
|
+
const decode = Schema.decodeUnknownResult(codec);
|
|
93
|
+
const keyOf = (principal) => Result.match(encode(principal), {
|
|
94
|
+
onFailure: () => void 0,
|
|
95
|
+
onSuccess: (json) => {
|
|
96
|
+
const text = JSON.stringify(json);
|
|
97
|
+
return Result.match(decode(JSON.parse(text)), {
|
|
98
|
+
onFailure: () => void 0,
|
|
99
|
+
onSuccess: (decoded) => Equal.equals(decoded, principal) ? new PrincipalKey(decoded, text) : void 0
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
return (principal) => {
|
|
104
|
+
try {
|
|
105
|
+
return keyOf(principal);
|
|
106
|
+
} catch {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
const forbidden = (target, kind, reason) => Forbidden.make({
|
|
112
|
+
action: target.action,
|
|
113
|
+
kind,
|
|
114
|
+
...target.field === void 0 ? {} : { field: target.field },
|
|
115
|
+
...reason === void 0 ? {} : { reason }
|
|
116
|
+
});
|
|
117
|
+
/** Makes the layer of an authorizer. */
|
|
118
|
+
const makeLayer = (service, options) => {
|
|
119
|
+
const domain = service.domain;
|
|
120
|
+
const canonicalOf = jsonKey(domain.principal);
|
|
121
|
+
const custom = options.cache === false ? void 0 : options.cache?.key;
|
|
122
|
+
const keyOf = (principal, canonical) => {
|
|
123
|
+
if (custom === void 0) return canonical;
|
|
124
|
+
const text = custom(principal);
|
|
125
|
+
return text === void 0 ? void 0 : new PrincipalKey(canonical?.principal ?? principal, text);
|
|
126
|
+
};
|
|
127
|
+
return Layer.effect(service, Effect.gen(function* () {
|
|
128
|
+
const services = yield* Effect.context();
|
|
129
|
+
const load = (principal) => options.policy(principal).pipe(Effect.provideContext(services), Effect.filterOrFail((value) => value.domain === domain, () => new globalThis.Error("The loaded policy is not a policy of this domain")), Effect.mapError((cause) => PolicyUnavailable.make({ cause })), Effect.map((value) => provideContext(value, services)));
|
|
130
|
+
const cache = options.cache === false ? void 0 : yield* Cache.make({
|
|
131
|
+
capacity: options.cache?.capacity ?? 1e3,
|
|
132
|
+
timeToLive: options.cache?.timeToLive ?? "30 seconds",
|
|
133
|
+
lookup: (key) => load(key.principal)
|
|
134
|
+
});
|
|
135
|
+
const policyFor = (principal) => {
|
|
136
|
+
const canonical = canonicalOf(principal);
|
|
137
|
+
const key = cache === void 0 ? void 0 : keyOf(principal, canonical);
|
|
138
|
+
if (cache === void 0 || key === void 0) return load(canonical?.principal ?? principal);
|
|
139
|
+
return Cache.get(cache, key).pipe(Effect.tapError(() => Cache.invalidate(cache, key)));
|
|
140
|
+
};
|
|
141
|
+
const withServices = (effect) => Effect.provideContext(effect, services);
|
|
142
|
+
const current = Effect.gen(function* () {
|
|
143
|
+
const principal = yield* service.principal;
|
|
144
|
+
return {
|
|
145
|
+
principal,
|
|
146
|
+
policy: yield* policyFor(principal)
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
const decideWith = Effect.fnUntraced(function* ({ principal, policy: value }, target) {
|
|
150
|
+
return yield* withServices(decideAny(value, {
|
|
151
|
+
...target,
|
|
152
|
+
principal
|
|
153
|
+
}));
|
|
154
|
+
});
|
|
155
|
+
const decide = (target) => Effect.flatMap(current, (snapshot) => decideWith(snapshot, target));
|
|
156
|
+
const checkWith = Effect.fnUntraced(function* ({ principal, policy: value }, target) {
|
|
157
|
+
const kind = target.resource === void 0 ? target.kind ?? "" : kindOf(target.resource) ?? "";
|
|
158
|
+
const descriptor = descriptorOf(domain, kind);
|
|
159
|
+
if (descriptor === void 0 || !fitsKind(descriptor, target)) return yield* forbidden(target, kind, void 0);
|
|
160
|
+
const fromPolicy = target.resource === void 0 ? decideKind(value, {
|
|
161
|
+
...target,
|
|
162
|
+
principal
|
|
163
|
+
}) : yield* withServices(decideAny(value, {
|
|
164
|
+
...target,
|
|
165
|
+
principal
|
|
166
|
+
}));
|
|
167
|
+
const decision = fromPolicy._tag === "NotApplicable" && options.decider !== void 0 ? yield* options.decider({
|
|
168
|
+
...target,
|
|
169
|
+
principal
|
|
170
|
+
}) : fromPolicy;
|
|
171
|
+
if (decision._tag !== "Allow") return yield* forbidden(target, kind, reason(decision));
|
|
172
|
+
});
|
|
173
|
+
const check = (target) => Effect.flatMap(current, (snapshot) => checkWith(snapshot, target));
|
|
174
|
+
const guard = Effect.fnUntraced(function* (target, guardOptions) {
|
|
175
|
+
const snapshot = yield* current;
|
|
176
|
+
const read = guardOptions.read ?? "read";
|
|
177
|
+
const { field: _field, ...rest } = target;
|
|
178
|
+
if ((yield* decideWith(snapshot, {
|
|
179
|
+
...rest,
|
|
180
|
+
action: read
|
|
181
|
+
}))._tag !== "Allow") return yield* Effect.fail(guardOptions.hide());
|
|
182
|
+
if (target.action !== read || target.field !== void 0) yield* checkWith(snapshot, target);
|
|
183
|
+
});
|
|
184
|
+
const filter = (target) => Effect.map(current, ({ principal, policy: value }) => coverageAny(value, {
|
|
185
|
+
...target,
|
|
186
|
+
principal
|
|
187
|
+
}).condition);
|
|
188
|
+
const coverage = (target) => Effect.map(current, ({ principal, policy: value }) => coverageAny(value, {
|
|
189
|
+
...target,
|
|
190
|
+
principal
|
|
191
|
+
}));
|
|
192
|
+
const permittedFields$1 = Effect.fnUntraced(function* (target) {
|
|
193
|
+
const { principal, policy: value } = yield* current;
|
|
194
|
+
return yield* withServices(permittedFields(value, {
|
|
195
|
+
...target,
|
|
196
|
+
principal
|
|
197
|
+
}));
|
|
198
|
+
});
|
|
199
|
+
const invalidate = (principal) => {
|
|
200
|
+
if (cache === void 0) return Effect.void;
|
|
201
|
+
if (principal === void 0) return Cache.invalidateAll(cache);
|
|
202
|
+
const key = keyOf(principal, canonicalOf(principal));
|
|
203
|
+
return key === void 0 ? Effect.void : Cache.invalidate(cache, key);
|
|
204
|
+
};
|
|
205
|
+
return {
|
|
206
|
+
domain,
|
|
207
|
+
principal: service.principal,
|
|
208
|
+
policy: Effect.map(current, ({ policy: value }) => value),
|
|
209
|
+
decide,
|
|
210
|
+
check,
|
|
211
|
+
guard,
|
|
212
|
+
filter,
|
|
213
|
+
coverage,
|
|
214
|
+
permittedFields: permittedFields$1,
|
|
215
|
+
invalidate
|
|
216
|
+
};
|
|
217
|
+
}));
|
|
218
|
+
};
|
|
219
|
+
export { Forbidden, Pending, PolicyUnavailable, Service, makeLayer };
|
|
220
|
+
|
|
221
|
+
//# sourceMappingURL=authorizer.js.map
|
|
@@ -0,0 +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. The key also\n * keeps the principal, so the cache can load its policy. A plain `Data` value\n * can not do both, so the key gives its own equality with `Equal.Equal`.\n */\nclass PrincipalKey implements Equal.Equal {\n\treadonly principal: object\n\treadonly text: string\n\tconstructor(principal: object, text: string) {\n\t\tthis.principal = principal\n\t\tthis.text = text\n\t}\n\t[Equal.symbol](that: unknown): boolean {\n\t\treturn that instanceof PrincipalKey && that.text === this.text\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)\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)\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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"}
|