@nmnmcc/ability 0.1.0 → 0.3.0
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 +353 -170
- package/dist/Authorizer.d.ts +216 -0
- package/dist/Authorizer.d.ts.map +1 -0
- package/dist/Authorizer.js +55 -0
- package/dist/Authorizer.js.map +1 -0
- package/dist/Condition.d.ts +536 -0
- package/dist/Condition.d.ts.map +1 -0
- package/dist/Condition.js +141 -0
- package/dist/Condition.js.map +1 -0
- package/dist/Decision.d.ts +131 -0
- package/dist/Decision.d.ts.map +1 -0
- package/dist/Decision.js +83 -0
- package/dist/Decision.js.map +1 -0
- package/dist/Domain.d.ts +148 -0
- package/dist/Domain.d.ts.map +1 -0
- package/dist/Domain.js +34 -0
- package/dist/Domain.js.map +1 -0
- package/dist/HttpApi.d.ts +129 -0
- package/dist/HttpApi.d.ts.map +1 -0
- package/dist/HttpApi.js +95 -0
- package/dist/HttpApi.js.map +1 -0
- package/dist/Policy.d.ts +429 -0
- package/dist/Policy.d.ts.map +1 -0
- package/dist/Policy.js +162 -0
- package/dist/Policy.js.map +1 -0
- package/dist/Principal.d.ts +35 -0
- package/dist/Principal.d.ts.map +1 -0
- package/dist/Principal.js +37 -0
- package/dist/Principal.js.map +1 -0
- package/dist/Resource.d.ts +213 -0
- package/dist/Resource.d.ts.map +1 -0
- package/dist/Resource.js +71 -0
- package/dist/Resource.js.map +1 -0
- package/dist/Rule.d.ts +189 -0
- package/dist/Rule.d.ts.map +1 -0
- package/dist/Rule.js +50 -0
- package/dist/Rule.js.map +1 -0
- package/dist/_virtual/_rolldown/runtime.js +1 -2
- package/dist/index.d.ts +9 -4
- package/dist/index.js +9 -4
- package/dist/internal/authorizer.d.ts +28 -0
- package/dist/internal/authorizer.d.ts.map +1 -0
- package/dist/internal/authorizer.js +153 -0
- package/dist/internal/authorizer.js.map +1 -0
- package/dist/internal/condition.d.ts +11 -0
- package/dist/internal/condition.d.ts.map +1 -0
- package/dist/internal/condition.js +393 -0
- package/dist/internal/condition.js.map +1 -0
- package/dist/internal/domain.js +34 -0
- package/dist/internal/domain.js.map +1 -0
- package/dist/internal/httpApi.js +41 -0
- package/dist/internal/httpApi.js.map +1 -0
- package/dist/internal/policy.js +524 -0
- package/dist/internal/policy.js.map +1 -0
- package/dist/internal/resource.js +66 -0
- package/dist/internal/resource.js.map +1 -0
- package/dist/internal/rule.js +131 -0
- package/dist/internal/rule.js.map +1 -0
- package/package.json +61 -74
- package/src/Authorizer.ts +330 -0
- package/src/Condition.ts +706 -0
- package/src/Decision.ts +117 -0
- package/src/Domain.ts +189 -0
- package/src/HttpApi.ts +148 -0
- package/src/Policy.ts +575 -0
- package/src/Principal.ts +41 -0
- package/src/Resource.ts +290 -0
- package/src/Rule.ts +265 -0
- package/src/index.ts +57 -0
- package/src/internal/authorizer.ts +271 -0
- package/src/internal/condition.ts +607 -0
- package/src/internal/domain.ts +56 -0
- package/src/internal/httpApi.ts +84 -0
- package/src/internal/policy.ts +815 -0
- package/src/internal/resource.ts +98 -0
- package/src/internal/rule.ts +203 -0
- package/dist/Ability.d.ts +0 -516
- package/dist/Ability.js +0 -211
- package/dist/AbilityExtra.d.ts +0 -79
- package/dist/AbilityExtra.js +0 -195
- package/dist/AbilityRef.d.ts +0 -110
- package/dist/AbilityRef.js +0 -160
- package/dist/internal/ability.d.ts +0 -114
- package/dist/internal/ability.js +0 -624
- package/dist/internal/serializable.js +0 -22
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
import { Allow, Deny, NotApplicable } from "../Decision.js";
|
|
2
|
+
import { getDescriptor, kindOf } from "./domain.js";
|
|
3
|
+
import { and as and$1, condition_exports, evaluateOf, fields, not as not$1, or as or$1, partialOf, schema as schema$1 } from "./condition.js";
|
|
4
|
+
import { builder, make, matchesTarget, toData } from "./rule.js";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
import * as Equal from "effect/Equal";
|
|
7
|
+
import * as Hash from "effect/Hash";
|
|
8
|
+
import * as Schema from "effect/Schema";
|
|
9
|
+
import * as Predicate from "effect/Predicate";
|
|
10
|
+
import * as Formatter from "effect/Formatter";
|
|
11
|
+
import { dual } from "effect/Function";
|
|
12
|
+
import * as Inspectable from "effect/Inspectable";
|
|
13
|
+
import * as Pipeable from "effect/Pipeable";
|
|
14
|
+
import * as SchemaTransformation from "effect/SchemaTransformation";
|
|
15
|
+
// src/internal/policy.ts
|
|
16
|
+
/** @internal */
|
|
17
|
+
const TypeId = "~@nmnmcc/ability/Policy";
|
|
18
|
+
/** @internal */
|
|
19
|
+
const encodeNode = (node) => {
|
|
20
|
+
switch (node._tag) {
|
|
21
|
+
case "Rules": return {
|
|
22
|
+
_tag: "Rules",
|
|
23
|
+
algorithm: node.algorithm,
|
|
24
|
+
rules: node.rules.map((value) => toData(value))
|
|
25
|
+
};
|
|
26
|
+
case "All":
|
|
27
|
+
case "Any":
|
|
28
|
+
case "FirstOf": return {
|
|
29
|
+
_tag: node._tag,
|
|
30
|
+
policies: node.policies.map(encodeNode)
|
|
31
|
+
};
|
|
32
|
+
case "Ceiling": return {
|
|
33
|
+
_tag: "Ceiling",
|
|
34
|
+
cap: encodeNode(node.cap),
|
|
35
|
+
policy: encodeNode(node.policy)
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const Proto = {
|
|
40
|
+
...Pipeable.Prototype,
|
|
41
|
+
[TypeId]: TypeId,
|
|
42
|
+
[Equal.symbol](that) {
|
|
43
|
+
return isPolicy(that) && this.domain === that.domain && Equal.equals(this.node, that.node);
|
|
44
|
+
},
|
|
45
|
+
[Hash.symbol]() {
|
|
46
|
+
return Hash.hash(this.node);
|
|
47
|
+
},
|
|
48
|
+
toJSON() {
|
|
49
|
+
return encodeNode(this.node);
|
|
50
|
+
},
|
|
51
|
+
toString() {
|
|
52
|
+
return Formatter.format(encodeNode(this.node), { space: 2 });
|
|
53
|
+
},
|
|
54
|
+
[Inspectable.NodeInspectSymbol]() {
|
|
55
|
+
return encodeNode(this.node);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
/** @internal */
|
|
59
|
+
const isPolicy = (u) => Predicate.hasProperty(u, TypeId);
|
|
60
|
+
/** @internal */
|
|
61
|
+
const fromNode = (domain, node) => {
|
|
62
|
+
const self = Object.create(Proto);
|
|
63
|
+
self.domain = domain;
|
|
64
|
+
self.node = node;
|
|
65
|
+
return Object.freeze(self);
|
|
66
|
+
};
|
|
67
|
+
const nodeOf = (policy) => policy.node;
|
|
68
|
+
const domainOf = (policy) => policy.domain;
|
|
69
|
+
/** @internal */
|
|
70
|
+
const rules = (domain, values, options) => fromNode(domain, Object.freeze({
|
|
71
|
+
_tag: "Rules",
|
|
72
|
+
algorithm: options?.algorithm ?? "denyOverrides",
|
|
73
|
+
rules: Object.freeze(Array.from(values))
|
|
74
|
+
}));
|
|
75
|
+
/** @internal */
|
|
76
|
+
const empty = (domain) => rules(domain, []);
|
|
77
|
+
/** @internal */
|
|
78
|
+
const define = (domain, options) => (body) => {
|
|
79
|
+
const collected = [];
|
|
80
|
+
const iterator = body(builder(domain));
|
|
81
|
+
let state = iterator.next();
|
|
82
|
+
while (state.done !== true) {
|
|
83
|
+
collected.push(state.value);
|
|
84
|
+
state = iterator.next(state.value);
|
|
85
|
+
}
|
|
86
|
+
return rules(domain, collected, options);
|
|
87
|
+
};
|
|
88
|
+
const combine = (tag) => (policies) => {
|
|
89
|
+
const domain = domainOf(policies[0]);
|
|
90
|
+
for (const policy of policies) if (domainOf(policy) !== domain) throw new Error("Policies from two domains can not be put together");
|
|
91
|
+
return fromNode(domain, Object.freeze({
|
|
92
|
+
_tag: tag,
|
|
93
|
+
policies: Object.freeze(policies.map(nodeOf))
|
|
94
|
+
}));
|
|
95
|
+
};
|
|
96
|
+
/** @internal */
|
|
97
|
+
const all = combine("All");
|
|
98
|
+
/** @internal */
|
|
99
|
+
const any = combine("Any");
|
|
100
|
+
/** @internal */
|
|
101
|
+
const firstOf = combine("FirstOf");
|
|
102
|
+
/** @internal */
|
|
103
|
+
const ceiling = dual(2, (policy, cap) => {
|
|
104
|
+
if (domainOf(policy) !== domainOf(cap)) throw new Error("Policies from two domains can not be put together");
|
|
105
|
+
return fromNode(domainOf(policy), Object.freeze({
|
|
106
|
+
_tag: "Ceiling",
|
|
107
|
+
cap: nodeOf(cap),
|
|
108
|
+
policy: nodeOf(policy)
|
|
109
|
+
}));
|
|
110
|
+
});
|
|
111
|
+
const ruleApplies = (value, context, path) => {
|
|
112
|
+
if (!matchesTarget(value, context.action, context.kind, context.field)) return Effect.succeed(false);
|
|
113
|
+
const test = value.when === void 0 ? Effect.succeed(true) : evaluateOf(value.when, context.input);
|
|
114
|
+
return context.trace === void 0 ? test : Effect.tap(test, (applies) => Effect.sync(() => {
|
|
115
|
+
context.trace.push({
|
|
116
|
+
path,
|
|
117
|
+
rule: value,
|
|
118
|
+
applies
|
|
119
|
+
});
|
|
120
|
+
}));
|
|
121
|
+
};
|
|
122
|
+
const fromRule = (value) => value._tag === "Allow" ? Allow({ rule: value }) : value.reason === void 0 ? Deny({ rule: value }) : Deny({
|
|
123
|
+
rule: value,
|
|
124
|
+
reason: value.reason
|
|
125
|
+
});
|
|
126
|
+
const firstApplying = Effect.fnUntraced(function* (candidates, context, path) {
|
|
127
|
+
for (const [value, index] of candidates) if (yield* ruleApplies(value, context, [...path, index])) return value;
|
|
128
|
+
});
|
|
129
|
+
const decideRules = Effect.fnUntraced(function* (node, context, path) {
|
|
130
|
+
const indexed = node.rules.map((value, index) => [value, index]);
|
|
131
|
+
const denies = indexed.filter(([value]) => value._tag === "Deny");
|
|
132
|
+
const allows = indexed.filter(([value]) => value._tag === "Allow");
|
|
133
|
+
let found;
|
|
134
|
+
switch (node.algorithm) {
|
|
135
|
+
case "denyOverrides":
|
|
136
|
+
found = (yield* firstApplying(denies, context, path)) ?? (yield* firstApplying(allows, context, path));
|
|
137
|
+
break;
|
|
138
|
+
case "permitOverrides":
|
|
139
|
+
found = (yield* firstApplying(allows, context, path)) ?? (yield* firstApplying(denies, context, path));
|
|
140
|
+
break;
|
|
141
|
+
case "firstApplicable":
|
|
142
|
+
found = yield* firstApplying(indexed, context, path);
|
|
143
|
+
break;
|
|
144
|
+
case "lastApplicable": found = yield* firstApplying(indexed.slice().reverse(), context, path);
|
|
145
|
+
}
|
|
146
|
+
return found === void 0 ? NotApplicable : fromRule(found);
|
|
147
|
+
});
|
|
148
|
+
const decideNode = (node, context, path) => {
|
|
149
|
+
switch (node._tag) {
|
|
150
|
+
case "Rules": return decideRules(node, context, path);
|
|
151
|
+
case "All": return Effect.gen(function* () {
|
|
152
|
+
let firstAllow;
|
|
153
|
+
let allAllow = node.policies.length > 0;
|
|
154
|
+
for (const [index, child] of node.policies.entries()) {
|
|
155
|
+
const result = yield* decideNode(child, context, [...path, index]);
|
|
156
|
+
if (result._tag === "Deny") return result;
|
|
157
|
+
if (result._tag === "Allow") firstAllow ??= result;
|
|
158
|
+
else allAllow = false;
|
|
159
|
+
}
|
|
160
|
+
return allAllow && firstAllow !== void 0 ? firstAllow : NotApplicable;
|
|
161
|
+
});
|
|
162
|
+
case "Any": return Effect.gen(function* () {
|
|
163
|
+
let firstDeny;
|
|
164
|
+
for (const [index, child] of node.policies.entries()) {
|
|
165
|
+
const result = yield* decideNode(child, context, [...path, index]);
|
|
166
|
+
if (result._tag === "Allow") return result;
|
|
167
|
+
if (result._tag === "Deny") firstDeny ??= result;
|
|
168
|
+
}
|
|
169
|
+
return firstDeny ?? NotApplicable;
|
|
170
|
+
});
|
|
171
|
+
case "FirstOf": return Effect.gen(function* () {
|
|
172
|
+
for (const [index, child] of node.policies.entries()) {
|
|
173
|
+
const result = yield* decideNode(child, context, [...path, index]);
|
|
174
|
+
if (result._tag !== "NotApplicable") return result;
|
|
175
|
+
}
|
|
176
|
+
return NotApplicable;
|
|
177
|
+
});
|
|
178
|
+
case "Ceiling": return Effect.gen(function* () {
|
|
179
|
+
const capped = yield* decideNode(node.cap, context, [...path, 0]);
|
|
180
|
+
if (capped._tag === "Deny") return capped;
|
|
181
|
+
return yield* decideNode(node.policy, context, [...path, 1]);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
const makeContext = (domain, request, trace) => {
|
|
186
|
+
const kind = kindOf(request.resource);
|
|
187
|
+
const descriptor = kind === void 0 ? void 0 : getDescriptor(domain, kind);
|
|
188
|
+
if (kind === void 0 || descriptor === void 0) return;
|
|
189
|
+
return {
|
|
190
|
+
principal: request.principal,
|
|
191
|
+
action: request.action,
|
|
192
|
+
kind,
|
|
193
|
+
value: request.resource,
|
|
194
|
+
field: request.field,
|
|
195
|
+
input: {
|
|
196
|
+
value: request.resource,
|
|
197
|
+
principal: request.principal,
|
|
198
|
+
path: descriptor.path
|
|
199
|
+
},
|
|
200
|
+
trace
|
|
201
|
+
};
|
|
202
|
+
};
|
|
203
|
+
/** @internal */
|
|
204
|
+
const decideOf = (policy, request) => {
|
|
205
|
+
const context = makeContext(domainOf(policy), request, void 0);
|
|
206
|
+
return context === void 0 ? Effect.succeed(NotApplicable) : decideNode(nodeOf(policy), context, []);
|
|
207
|
+
};
|
|
208
|
+
/** @internal */
|
|
209
|
+
const decide = dual(2, decideOf);
|
|
210
|
+
/** @internal */
|
|
211
|
+
const decideSync = dual(2, (policy, request) => Effect.runSync(decideOf(policy, request)));
|
|
212
|
+
/** @internal */
|
|
213
|
+
const explainOf = (policy, request) => {
|
|
214
|
+
const trace = [];
|
|
215
|
+
const context = makeContext(domainOf(policy), request, trace);
|
|
216
|
+
if (context === void 0) return Effect.succeed({
|
|
217
|
+
decision: NotApplicable,
|
|
218
|
+
steps: []
|
|
219
|
+
});
|
|
220
|
+
return Effect.map(decideNode(nodeOf(policy), context, []), (decision) => ({
|
|
221
|
+
decision,
|
|
222
|
+
steps: Object.freeze(trace)
|
|
223
|
+
}));
|
|
224
|
+
};
|
|
225
|
+
/** @internal */
|
|
226
|
+
const explain = dual(2, explainOf);
|
|
227
|
+
/** @internal */
|
|
228
|
+
const permittedFieldsOf = Effect.fnUntraced(function* (policy, request) {
|
|
229
|
+
const domain = domainOf(policy);
|
|
230
|
+
const kind = kindOf(request.resource);
|
|
231
|
+
const descriptor = kind === void 0 ? void 0 : getDescriptor(domain, kind);
|
|
232
|
+
if (descriptor === void 0) return [];
|
|
233
|
+
const output = [];
|
|
234
|
+
for (const field of descriptor.fieldPaths) if ((yield* decideOf(policy, {
|
|
235
|
+
...request,
|
|
236
|
+
field
|
|
237
|
+
}))._tag === "Allow") output.push(field);
|
|
238
|
+
return Object.freeze(output);
|
|
239
|
+
});
|
|
240
|
+
/** @internal */
|
|
241
|
+
const permittedFields = dual(2, permittedFieldsOf);
|
|
242
|
+
const { always, never, simplify } = condition_exports;
|
|
243
|
+
const and = (...values) => simplify(and$1(...values));
|
|
244
|
+
const or = (...values) => simplify(or$1(...values));
|
|
245
|
+
const not = (value) => simplify(not$1(value));
|
|
246
|
+
const ruleCondition = (value, context) => !matchesTarget(value, context.action, context.kind, context.field) ? never : value.when === void 0 ? always : partialOf(value.when, context.input);
|
|
247
|
+
/**
|
|
248
|
+
* Gives the conditions for "the first rule that applies is an allow rule" and
|
|
249
|
+
* "the first rule that applies is a deny rule".
|
|
250
|
+
*/
|
|
251
|
+
const firstApplicableResidual = (values, context) => {
|
|
252
|
+
let allow = never;
|
|
253
|
+
let deny = never;
|
|
254
|
+
for (const value of values.slice().reverse()) {
|
|
255
|
+
const applies = ruleCondition(value, context);
|
|
256
|
+
if (value._tag === "Allow") {
|
|
257
|
+
allow = or(applies, allow);
|
|
258
|
+
deny = and(not(applies), deny);
|
|
259
|
+
} else {
|
|
260
|
+
deny = or(applies, deny);
|
|
261
|
+
allow = and(not(applies), allow);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return {
|
|
265
|
+
allow,
|
|
266
|
+
deny
|
|
267
|
+
};
|
|
268
|
+
};
|
|
269
|
+
const residualNode = (node, context) => {
|
|
270
|
+
switch (node._tag) {
|
|
271
|
+
case "Rules": switch (node.algorithm) {
|
|
272
|
+
case "denyOverrides": {
|
|
273
|
+
const deny = or(...node.rules.filter((value) => value._tag === "Deny").map((value) => ruleCondition(value, context)));
|
|
274
|
+
return {
|
|
275
|
+
allow: and(or(...node.rules.filter((value) => value._tag === "Allow").map((value) => ruleCondition(value, context))), not(deny)),
|
|
276
|
+
deny
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
case "permitOverrides": {
|
|
280
|
+
const allow = or(...node.rules.filter((value) => value._tag === "Allow").map((value) => ruleCondition(value, context)));
|
|
281
|
+
return {
|
|
282
|
+
allow,
|
|
283
|
+
deny: and(or(...node.rules.filter((value) => value._tag === "Deny").map((value) => ruleCondition(value, context))), not(allow))
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
case "firstApplicable": return firstApplicableResidual(node.rules, context);
|
|
287
|
+
case "lastApplicable": return firstApplicableResidual(node.rules.slice().reverse(), context);
|
|
288
|
+
}
|
|
289
|
+
case "All": {
|
|
290
|
+
if (node.policies.length === 0) return {
|
|
291
|
+
allow: never,
|
|
292
|
+
deny: never
|
|
293
|
+
};
|
|
294
|
+
const pairs = node.policies.map((child) => residualNode(child, context));
|
|
295
|
+
const deny = or(...pairs.map((pair) => pair.deny));
|
|
296
|
+
return {
|
|
297
|
+
allow: and(...pairs.map((pair) => pair.allow), not(deny)),
|
|
298
|
+
deny
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
case "Any": {
|
|
302
|
+
const pairs = node.policies.map((child) => residualNode(child, context));
|
|
303
|
+
const allow = or(...pairs.map((pair) => pair.allow));
|
|
304
|
+
return {
|
|
305
|
+
allow,
|
|
306
|
+
deny: and(or(...pairs.map((pair) => pair.deny)), not(allow))
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
case "FirstOf": {
|
|
310
|
+
let allow = never;
|
|
311
|
+
let deny = never;
|
|
312
|
+
for (const child of node.policies.slice().reverse()) {
|
|
313
|
+
const pair = residualNode(child, context);
|
|
314
|
+
const nextAllow = or(pair.allow, and(not(pair.deny), allow));
|
|
315
|
+
deny = or(pair.deny, and(not(pair.allow), deny));
|
|
316
|
+
allow = nextAllow;
|
|
317
|
+
}
|
|
318
|
+
return {
|
|
319
|
+
allow,
|
|
320
|
+
deny
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
case "Ceiling": {
|
|
324
|
+
const cap = residualNode(node.cap, context);
|
|
325
|
+
const inner = residualNode(node.policy, context);
|
|
326
|
+
return {
|
|
327
|
+
allow: and(not(cap.deny), inner.allow),
|
|
328
|
+
deny: or(cap.deny, inner.deny)
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
/** @internal */
|
|
334
|
+
const residualPair = (policy, request) => {
|
|
335
|
+
const domain = domainOf(policy);
|
|
336
|
+
const descriptor = getDescriptor(domain, request.kind);
|
|
337
|
+
if (descriptor === void 0) return {
|
|
338
|
+
allow: never,
|
|
339
|
+
deny: never
|
|
340
|
+
};
|
|
341
|
+
return residualNode(nodeOf(policy), {
|
|
342
|
+
principal: request.principal,
|
|
343
|
+
action: request.action,
|
|
344
|
+
kind: request.kind,
|
|
345
|
+
field: request.field,
|
|
346
|
+
input: {
|
|
347
|
+
principal: request.principal,
|
|
348
|
+
path: descriptor.path
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
};
|
|
352
|
+
/** @internal */
|
|
353
|
+
const residualOf = (policy, request) => residualPair(policy, request).allow;
|
|
354
|
+
/** @internal */
|
|
355
|
+
const residual = dual(2, residualOf);
|
|
356
|
+
const Algorithm = Schema.Literals([
|
|
357
|
+
"denyOverrides",
|
|
358
|
+
"permitOverrides",
|
|
359
|
+
"firstApplicable",
|
|
360
|
+
"lastApplicable"
|
|
361
|
+
]);
|
|
362
|
+
const encodedRuleSchema = (meta) => Schema.Struct({
|
|
363
|
+
_tag: Schema.Literals(["Allow", "Deny"]),
|
|
364
|
+
actions: Schema.Array(Schema.String),
|
|
365
|
+
resources: Schema.Array(Schema.String),
|
|
366
|
+
when: Schema.optionalKey(schema$1),
|
|
367
|
+
fields: Schema.String.pipe(Schema.Array, Schema.optionalKey),
|
|
368
|
+
reason: Schema.optionalKey(Schema.String),
|
|
369
|
+
id: Schema.optionalKey(Schema.String),
|
|
370
|
+
meta: Schema.optionalKey(meta ?? Schema.Never)
|
|
371
|
+
});
|
|
372
|
+
const encodedNodeSchema = (meta) => {
|
|
373
|
+
const self = Schema.Union([
|
|
374
|
+
Schema.TaggedStruct("Rules", {
|
|
375
|
+
algorithm: Algorithm,
|
|
376
|
+
rules: Schema.Array(encodedRuleSchema(meta))
|
|
377
|
+
}),
|
|
378
|
+
Schema.TaggedStruct("All", { policies: Schema.Array(Schema.suspend(() => self)) }),
|
|
379
|
+
Schema.TaggedStruct("Any", { policies: Schema.Array(Schema.suspend(() => self)) }),
|
|
380
|
+
Schema.TaggedStruct("FirstOf", { policies: Schema.Array(Schema.suspend(() => self)) }),
|
|
381
|
+
Schema.TaggedStruct("Ceiling", {
|
|
382
|
+
cap: Schema.suspend(() => self),
|
|
383
|
+
policy: Schema.suspend(() => self)
|
|
384
|
+
})
|
|
385
|
+
]);
|
|
386
|
+
return self;
|
|
387
|
+
};
|
|
388
|
+
const principalFieldPaths = (domain) => {
|
|
389
|
+
const output = /* @__PURE__ */ new Set();
|
|
390
|
+
const visit = (schema, prefix) => {
|
|
391
|
+
if (!Predicate.hasProperty(schema, "fields") || !Predicate.isObject(schema.fields)) return;
|
|
392
|
+
const fields = schema.fields;
|
|
393
|
+
for (const key of Object.keys(fields)) {
|
|
394
|
+
const path = prefix === "" ? key : `${prefix}.${key}`;
|
|
395
|
+
output.add(path);
|
|
396
|
+
visit(fields[key], path);
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
visit(domain.principal, "");
|
|
400
|
+
return output;
|
|
401
|
+
};
|
|
402
|
+
const principalRefs = (ast) => {
|
|
403
|
+
const output = [];
|
|
404
|
+
const visitOperand = (operand) => {
|
|
405
|
+
if (operand._tag === "Principal") output.push(operand.path);
|
|
406
|
+
};
|
|
407
|
+
const visit = (node) => {
|
|
408
|
+
switch (node._tag) {
|
|
409
|
+
case "Compare":
|
|
410
|
+
visitOperand(node.value);
|
|
411
|
+
return;
|
|
412
|
+
case "In":
|
|
413
|
+
node.values.forEach(visitOperand);
|
|
414
|
+
return;
|
|
415
|
+
case "Under":
|
|
416
|
+
node.prefix.forEach(visitOperand);
|
|
417
|
+
return;
|
|
418
|
+
case "And":
|
|
419
|
+
case "Or":
|
|
420
|
+
node.conditions.forEach(visit);
|
|
421
|
+
return;
|
|
422
|
+
case "Not":
|
|
423
|
+
visit(node.condition);
|
|
424
|
+
return;
|
|
425
|
+
default: return;
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
visit(ast);
|
|
429
|
+
return output;
|
|
430
|
+
};
|
|
431
|
+
/**
|
|
432
|
+
* Checks the names in an encoded policy against the domain. It gives one
|
|
433
|
+
* message for each problem.
|
|
434
|
+
*/
|
|
435
|
+
const validate = (domain, root) => {
|
|
436
|
+
const issues = [];
|
|
437
|
+
const principalPaths = principalFieldPaths(domain);
|
|
438
|
+
const visit = (node, path) => {
|
|
439
|
+
switch (node._tag) {
|
|
440
|
+
case "Rules":
|
|
441
|
+
node.rules.forEach((value, index) => {
|
|
442
|
+
const at = [
|
|
443
|
+
...path,
|
|
444
|
+
"rules",
|
|
445
|
+
index
|
|
446
|
+
];
|
|
447
|
+
const kinds = value.resources.includes("*") ? void 0 : value.resources;
|
|
448
|
+
const descriptors = [];
|
|
449
|
+
for (const kind of value.resources) {
|
|
450
|
+
if (kind === "*") continue;
|
|
451
|
+
const descriptor = getDescriptor(domain, kind);
|
|
452
|
+
if (descriptor === void 0) issues.push({
|
|
453
|
+
path: [...at, "resources"],
|
|
454
|
+
issue: `There is no resource with the kind "${kind}"`
|
|
455
|
+
});
|
|
456
|
+
else descriptors.push(descriptor);
|
|
457
|
+
}
|
|
458
|
+
const known = kinds === void 0 ? Array.from(domain.resources.values()) : descriptors;
|
|
459
|
+
for (const action of value.actions) {
|
|
460
|
+
if (action === "*") continue;
|
|
461
|
+
if (!known.some((descriptor) => descriptor.actions.includes(action))) issues.push({
|
|
462
|
+
path: [...at, "actions"],
|
|
463
|
+
issue: `The action "${action}" is not an action of these resources`
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
const usedFields = [...(value.fields ?? []).filter((field) => !field.includes("*")), ...value.when === void 0 ? [] : fields(value.when)];
|
|
467
|
+
for (const field of usedFields) if (!(kinds !== void 0 && descriptors.length > 0 && descriptors.every((descriptor) => descriptor.fieldPaths.includes(field)))) issues.push({
|
|
468
|
+
path: at,
|
|
469
|
+
issue: `The field "${field}" is not a field of every resource of the rule`
|
|
470
|
+
});
|
|
471
|
+
if (value.when !== void 0) {
|
|
472
|
+
for (const ref of principalRefs(value.when)) if (!principalPaths.has(ref)) issues.push({
|
|
473
|
+
path: [...at, "when"],
|
|
474
|
+
issue: `The principal has no field "${ref}"`
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
return;
|
|
479
|
+
case "All":
|
|
480
|
+
case "Any":
|
|
481
|
+
case "FirstOf":
|
|
482
|
+
node.policies.forEach((child, index) => visit(child, [
|
|
483
|
+
...path,
|
|
484
|
+
"policies",
|
|
485
|
+
index
|
|
486
|
+
]));
|
|
487
|
+
return;
|
|
488
|
+
case "Ceiling":
|
|
489
|
+
visit(node.cap, [...path, "cap"]);
|
|
490
|
+
visit(node.policy, [...path, "policy"]);
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
visit(root, []);
|
|
495
|
+
return issues;
|
|
496
|
+
};
|
|
497
|
+
const decodeNode = (node) => {
|
|
498
|
+
switch (node._tag) {
|
|
499
|
+
case "Rules": return Object.freeze({
|
|
500
|
+
_tag: "Rules",
|
|
501
|
+
algorithm: node.algorithm,
|
|
502
|
+
rules: Object.freeze(node.rules.map((value) => make(value)))
|
|
503
|
+
});
|
|
504
|
+
case "All":
|
|
505
|
+
case "Any":
|
|
506
|
+
case "FirstOf": return Object.freeze({
|
|
507
|
+
_tag: node._tag,
|
|
508
|
+
policies: Object.freeze(node.policies.map(decodeNode))
|
|
509
|
+
});
|
|
510
|
+
case "Ceiling": return Object.freeze({
|
|
511
|
+
_tag: "Ceiling",
|
|
512
|
+
cap: decodeNode(node.cap),
|
|
513
|
+
policy: decodeNode(node.policy)
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
/** @internal */
|
|
518
|
+
const schema = (domain) => encodedNodeSchema(domain.meta).pipe(Schema.check(Schema.makeFilter((node) => validate(domain, node))), Schema.decodeTo(Schema.declare((u) => isPolicy(u) && domainOf(u) === domain), SchemaTransformation.transform({
|
|
519
|
+
decode: (node) => fromNode(domain, decodeNode(node)),
|
|
520
|
+
encode: (policy) => encodeNode(policy.node)
|
|
521
|
+
})));
|
|
522
|
+
export { TypeId, all, any, ceiling, decide, decideOf, decideSync, define, empty, encodeNode, explain, explainOf, firstOf, fromNode, isPolicy, permittedFields, permittedFieldsOf, residual, residualOf, residualPair, rules, schema };
|
|
523
|
+
|
|
524
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
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
|