@nmnmcc/ability 0.2.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 +322 -469
- 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 -3
- package/dist/index.js +9 -3
- 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 -72
- 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 -491
- package/dist/Ability.js +0 -207
- package/dist/AbilityExtra.d.ts +0 -79
- package/dist/AbilityExtra.js +0 -187
- package/dist/internal/ability.d.ts +0 -90
- package/dist/internal/ability.js +0 -550
- package/dist/internal/serializable.js +0 -22
package/src/Policy.ts
ADDED
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A policy is a tree that gives a {@link Decision.Decision} for a request.
|
|
3
|
+
*
|
|
4
|
+
* The leaves of the tree are lists of rules. A combining algorithm decides
|
|
5
|
+
* which rule gives the answer when more than one rule applies:
|
|
6
|
+
* - `denyOverrides` (the default): a deny rule wins over every allow rule.
|
|
7
|
+
* - `permitOverrides`: an allow rule wins over every deny rule.
|
|
8
|
+
* - `firstApplicable`: the first rule that applies wins.
|
|
9
|
+
* - `lastApplicable`: the last rule that applies wins.
|
|
10
|
+
*
|
|
11
|
+
* The branches put policies together:
|
|
12
|
+
* - {@link all}: every policy must allow, and one deny is enough to deny. Use
|
|
13
|
+
* it when a user token may never do more than its user.
|
|
14
|
+
* - {@link any}: one allow is enough.
|
|
15
|
+
* - {@link firstOf}: the first policy that gives an answer wins. Use it for
|
|
16
|
+
* grants first, then rules.
|
|
17
|
+
* - {@link ceiling}: the deny rules of a second policy are a limit for
|
|
18
|
+
* everyone. Its allow rules do nothing.
|
|
19
|
+
*
|
|
20
|
+
* A policy gives three kinds of answers:
|
|
21
|
+
* - {@link decide}: the decision for one resource value.
|
|
22
|
+
* - {@link residual}: a condition on resource values. A value passes when the
|
|
23
|
+
* policy allows the request for that value. Use it to make a database
|
|
24
|
+
* query, or to ask "may this principal do this act on some resource of
|
|
25
|
+
* this kind?"
|
|
26
|
+
* - {@link permittedFields}: the fields that the policy lets a request use.
|
|
27
|
+
*
|
|
28
|
+
* A policy without predicates can become JSON with {@link schema}.
|
|
29
|
+
*
|
|
30
|
+
* @stability stable
|
|
31
|
+
* @since 0.3.0
|
|
32
|
+
*/
|
|
33
|
+
import type * as Effect from "effect/Effect"
|
|
34
|
+
import type * as Equal from "effect/Equal"
|
|
35
|
+
import type * as Inspectable from "effect/Inspectable"
|
|
36
|
+
import type * as Pipeable from "effect/Pipeable"
|
|
37
|
+
import type * as Schema from "effect/Schema"
|
|
38
|
+
import type * as Condition from "./Condition.ts"
|
|
39
|
+
import type * as Decision from "./Decision.ts"
|
|
40
|
+
import type * as Domain from "./Domain.ts"
|
|
41
|
+
import * as internal from "./internal/policy.ts"
|
|
42
|
+
import type * as Rule from "./Rule.ts"
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @stability stable
|
|
46
|
+
* @category type IDs
|
|
47
|
+
* @since 0.3.0
|
|
48
|
+
*/
|
|
49
|
+
export const TypeId: "~@nmnmcc/ability/Policy" = internal.TypeId
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @stability stable
|
|
53
|
+
* @category type IDs
|
|
54
|
+
* @since 0.3.0
|
|
55
|
+
*/
|
|
56
|
+
export type TypeId = typeof TypeId
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @stability stable
|
|
60
|
+
* @category models
|
|
61
|
+
* @since 0.3.0
|
|
62
|
+
*/
|
|
63
|
+
export type Algorithm =
|
|
64
|
+
"denyOverrides" | "permitOverrides" | "firstApplicable" | "lastApplicable"
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @stability stable
|
|
68
|
+
* @category models
|
|
69
|
+
* @since 0.3.0
|
|
70
|
+
*/
|
|
71
|
+
export interface RulesNode {
|
|
72
|
+
readonly _tag: "Rules"
|
|
73
|
+
readonly algorithm: Algorithm
|
|
74
|
+
readonly rules: ReadonlyArray<Rule.Any>
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @stability stable
|
|
79
|
+
* @category models
|
|
80
|
+
* @since 0.3.0
|
|
81
|
+
*/
|
|
82
|
+
export interface AllNode {
|
|
83
|
+
readonly _tag: "All"
|
|
84
|
+
readonly policies: ReadonlyArray<Node>
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @stability stable
|
|
89
|
+
* @category models
|
|
90
|
+
* @since 0.3.0
|
|
91
|
+
*/
|
|
92
|
+
export interface AnyNode {
|
|
93
|
+
readonly _tag: "Any"
|
|
94
|
+
readonly policies: ReadonlyArray<Node>
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @stability stable
|
|
99
|
+
* @category models
|
|
100
|
+
* @since 0.3.0
|
|
101
|
+
*/
|
|
102
|
+
export interface FirstOfNode {
|
|
103
|
+
readonly _tag: "FirstOf"
|
|
104
|
+
readonly policies: ReadonlyArray<Node>
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @stability stable
|
|
109
|
+
* @category models
|
|
110
|
+
* @since 0.3.0
|
|
111
|
+
*/
|
|
112
|
+
export interface CeilingNode {
|
|
113
|
+
readonly _tag: "Ceiling"
|
|
114
|
+
readonly cap: Node
|
|
115
|
+
readonly policy: Node
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* The nodes of a policy tree.
|
|
120
|
+
*
|
|
121
|
+
* @stability stable
|
|
122
|
+
* @category models
|
|
123
|
+
* @since 0.3.0
|
|
124
|
+
*/
|
|
125
|
+
export type Node = RulesNode | AllNode | AnyNode | FirstOfNode | CeilingNode
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* A policy for the domain `D`. `E`, `R` and `O` come from the conditions of
|
|
129
|
+
* its rules. See `Condition.Condition`.
|
|
130
|
+
*
|
|
131
|
+
* @stability stable
|
|
132
|
+
* @category models
|
|
133
|
+
* @since 0.3.0
|
|
134
|
+
*/
|
|
135
|
+
export interface Policy<D extends Domain.Any, E = never, R = never, O = never>
|
|
136
|
+
extends Equal.Equal, Inspectable.Inspectable, Pipeable.Pipeable {
|
|
137
|
+
readonly [TypeId]: TypeId
|
|
138
|
+
readonly domain: D
|
|
139
|
+
readonly node: Node
|
|
140
|
+
readonly "~Policy"?: Condition.Variance<E, R, O>
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Any policy.
|
|
145
|
+
*
|
|
146
|
+
* @stability stable
|
|
147
|
+
* @category models
|
|
148
|
+
* @since 0.3.0
|
|
149
|
+
*/
|
|
150
|
+
export type Any = Policy<any, any, any, any>
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* The domain of a policy.
|
|
154
|
+
*
|
|
155
|
+
* @stability stable
|
|
156
|
+
* @category utility types
|
|
157
|
+
* @since 0.3.0
|
|
158
|
+
*/
|
|
159
|
+
export type DomainOf<P> = P extends Policy<infer D, any, any, any> ? D : never
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The error type of a policy.
|
|
163
|
+
*
|
|
164
|
+
* @stability stable
|
|
165
|
+
* @category utility types
|
|
166
|
+
* @since 0.3.0
|
|
167
|
+
*/
|
|
168
|
+
export type Error<P> = P extends {
|
|
169
|
+
readonly "~Policy"?: Condition.Variance<infer E, infer _R, infer _O>
|
|
170
|
+
}
|
|
171
|
+
? E
|
|
172
|
+
: never
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The service type of a policy.
|
|
176
|
+
*
|
|
177
|
+
* @stability stable
|
|
178
|
+
* @category utility types
|
|
179
|
+
* @since 0.3.0
|
|
180
|
+
*/
|
|
181
|
+
export type Services<P> = P extends {
|
|
182
|
+
readonly "~Policy"?: Condition.Variance<infer _E, infer R, infer _O>
|
|
183
|
+
}
|
|
184
|
+
? R
|
|
185
|
+
: never
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* `Condition.Opaque` when a policy has a predicate.
|
|
189
|
+
*
|
|
190
|
+
* @stability stable
|
|
191
|
+
* @category utility types
|
|
192
|
+
* @since 0.3.0
|
|
193
|
+
*/
|
|
194
|
+
export type OpaqueOf<P> = P extends {
|
|
195
|
+
readonly "~Policy"?: Condition.Variance<infer _E, infer _R, infer O>
|
|
196
|
+
}
|
|
197
|
+
? O
|
|
198
|
+
: never
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* One act on one resource value, for each kind of the domain. The action must
|
|
202
|
+
* be an action of the kind, and the field must be a field of the kind.
|
|
203
|
+
*
|
|
204
|
+
* @stability stable
|
|
205
|
+
* @category models
|
|
206
|
+
* @since 0.3.0
|
|
207
|
+
*/
|
|
208
|
+
export type Target<D> = {
|
|
209
|
+
[K in Domain.Kind<D>]: {
|
|
210
|
+
readonly action: Domain.ActionOf<D, K>
|
|
211
|
+
readonly resource: Domain.ValueOf<D, K>
|
|
212
|
+
readonly field?: Condition.FieldPath<Domain.DataOf<D, K>>
|
|
213
|
+
}
|
|
214
|
+
}[Domain.Kind<D>]
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* One act on one kind of resource, with no value.
|
|
218
|
+
*
|
|
219
|
+
* @stability stable
|
|
220
|
+
* @category models
|
|
221
|
+
* @since 0.3.0
|
|
222
|
+
*/
|
|
223
|
+
export type KindTarget<D> = {
|
|
224
|
+
[K in Domain.Kind<D>]: {
|
|
225
|
+
readonly action: Domain.ActionOf<D, K>
|
|
226
|
+
readonly kind: K
|
|
227
|
+
readonly field?: Condition.FieldPath<Domain.DataOf<D, K>>
|
|
228
|
+
}
|
|
229
|
+
}[Domain.Kind<D>]
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* A request: a principal, an action, and a resource value.
|
|
233
|
+
*
|
|
234
|
+
* @stability stable
|
|
235
|
+
* @category models
|
|
236
|
+
* @since 0.3.0
|
|
237
|
+
*/
|
|
238
|
+
export type Request<D> = Target<D> & { readonly principal: Domain.Principal<D> }
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* A request about a kind of resource, with no value.
|
|
242
|
+
*
|
|
243
|
+
* @stability stable
|
|
244
|
+
* @category models
|
|
245
|
+
* @since 0.3.0
|
|
246
|
+
*/
|
|
247
|
+
export type KindRequest<D> = KindTarget<D> & {
|
|
248
|
+
readonly principal: Domain.Principal<D>
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* A request with no types.
|
|
253
|
+
*
|
|
254
|
+
* @stability stable
|
|
255
|
+
* @category models
|
|
256
|
+
* @since 0.3.0
|
|
257
|
+
*/
|
|
258
|
+
export interface AnyRequest {
|
|
259
|
+
readonly principal: object
|
|
260
|
+
readonly action: string
|
|
261
|
+
readonly resource: object
|
|
262
|
+
readonly field?: string | undefined
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* One rule that a decision tested. `path` gives the place of the rule in the
|
|
267
|
+
* policy tree: the index of each child, then the index of the rule.
|
|
268
|
+
*
|
|
269
|
+
* @stability stable
|
|
270
|
+
* @category models
|
|
271
|
+
* @since 0.3.0
|
|
272
|
+
*/
|
|
273
|
+
export interface Step {
|
|
274
|
+
readonly path: ReadonlyArray<number>
|
|
275
|
+
readonly rule: Rule.Any
|
|
276
|
+
readonly applies: boolean
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* A decision, and the rules that were tested to make it, in order. Keep it in
|
|
281
|
+
* an audit log.
|
|
282
|
+
*
|
|
283
|
+
* @stability stable
|
|
284
|
+
* @category models
|
|
285
|
+
* @since 0.3.0
|
|
286
|
+
*/
|
|
287
|
+
export interface Explanation {
|
|
288
|
+
readonly decision: Decision.Decision
|
|
289
|
+
readonly steps: ReadonlyArray<Step>
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* The options of a list of rules.
|
|
294
|
+
*
|
|
295
|
+
* @stability stable
|
|
296
|
+
* @category models
|
|
297
|
+
* @since 0.3.0
|
|
298
|
+
*/
|
|
299
|
+
export interface Options {
|
|
300
|
+
/** The default is `denyOverrides`. */
|
|
301
|
+
readonly algorithm?: Algorithm
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Makes a policy from a generator function that gives rules. It is pure and it
|
|
306
|
+
* gives its result at once.
|
|
307
|
+
*
|
|
308
|
+
* @example
|
|
309
|
+
* ```ts
|
|
310
|
+
* const policy = Policy.define(App)(function* (_) {
|
|
311
|
+
* yield* _.allow("read", Bot)
|
|
312
|
+
* yield* _.allow(["update", "delete"], Bot, {
|
|
313
|
+
* when: ($) => $.eq("ownerUserId", $.principal("userId")),
|
|
314
|
+
* })
|
|
315
|
+
* })
|
|
316
|
+
* ```
|
|
317
|
+
*
|
|
318
|
+
* @stability stable
|
|
319
|
+
* @category constructors
|
|
320
|
+
* @since 0.3.0
|
|
321
|
+
*/
|
|
322
|
+
export const define: <D extends Domain.Any>(
|
|
323
|
+
domain: D,
|
|
324
|
+
options?: Options,
|
|
325
|
+
) => <Rules extends Rule.Any, A>(
|
|
326
|
+
body: (builder: Rule.Builder<D>) => Generator<Rules, A, unknown>,
|
|
327
|
+
) => Policy<D, Rule.Error<Rules>, Rule.Services<Rules>, Rule.OpaqueOf<Rules>> =
|
|
328
|
+
internal.define as never
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Makes a policy from a list of rules.
|
|
332
|
+
*
|
|
333
|
+
* @stability stable
|
|
334
|
+
* @category constructors
|
|
335
|
+
* @since 0.3.0
|
|
336
|
+
*/
|
|
337
|
+
export const rules: <D extends Domain.Any, Rules extends Rule.Any>(
|
|
338
|
+
domain: D,
|
|
339
|
+
rules: Iterable<Rules>,
|
|
340
|
+
options?: Options,
|
|
341
|
+
) => Policy<D, Rule.Error<Rules>, Rule.Services<Rules>, Rule.OpaqueOf<Rules>> =
|
|
342
|
+
internal.rules
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* A policy with no rules. It is not applicable to any request.
|
|
346
|
+
*
|
|
347
|
+
* @stability stable
|
|
348
|
+
* @category constructors
|
|
349
|
+
* @since 0.3.0
|
|
350
|
+
*/
|
|
351
|
+
export const empty: <D extends Domain.Any>(domain: D) => Policy<D> =
|
|
352
|
+
internal.empty
|
|
353
|
+
|
|
354
|
+
type Combined<D extends Domain.Any, Ps extends ReadonlyArray<Any>> = Policy<
|
|
355
|
+
D,
|
|
356
|
+
Error<Ps[number]>,
|
|
357
|
+
Services<Ps[number]>,
|
|
358
|
+
OpaqueOf<Ps[number]>
|
|
359
|
+
>
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Every policy must allow, and one deny is enough to deny. With some policies
|
|
363
|
+
* not applicable and none that deny, the result is not applicable.
|
|
364
|
+
*
|
|
365
|
+
* @stability stable
|
|
366
|
+
* @category combinators
|
|
367
|
+
* @since 0.3.0
|
|
368
|
+
*/
|
|
369
|
+
export const all: <
|
|
370
|
+
const Ps extends readonly [Policy<any, any, any, any>, ...Array<Any>],
|
|
371
|
+
>(
|
|
372
|
+
policies: Ps,
|
|
373
|
+
) => Combined<DomainOf<Ps[0]>, Ps> = internal.all
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* One allow is enough. Without an allow, one deny gives a deny.
|
|
377
|
+
*
|
|
378
|
+
* @stability stable
|
|
379
|
+
* @category combinators
|
|
380
|
+
* @since 0.3.0
|
|
381
|
+
*/
|
|
382
|
+
export const any: <
|
|
383
|
+
const Ps extends readonly [Policy<any, any, any, any>, ...Array<Any>],
|
|
384
|
+
>(
|
|
385
|
+
policies: Ps,
|
|
386
|
+
) => Combined<DomainOf<Ps[0]>, Ps> = internal.any
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* The first policy that allows or denies gives the answer.
|
|
390
|
+
*
|
|
391
|
+
* @stability stable
|
|
392
|
+
* @category combinators
|
|
393
|
+
* @since 0.3.0
|
|
394
|
+
*/
|
|
395
|
+
export const firstOf: <
|
|
396
|
+
const Ps extends readonly [Policy<any, any, any, any>, ...Array<Any>],
|
|
397
|
+
>(
|
|
398
|
+
policies: Ps,
|
|
399
|
+
) => Combined<DomainOf<Ps[0]>, Ps> = internal.firstOf
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Puts a limit on a policy. A deny from `cap` always wins. An allow from `cap`
|
|
403
|
+
* does nothing.
|
|
404
|
+
*
|
|
405
|
+
* @stability stable
|
|
406
|
+
* @category combinators
|
|
407
|
+
* @since 0.3.0
|
|
408
|
+
*/
|
|
409
|
+
export const ceiling: {
|
|
410
|
+
<D extends Domain.Any, E2, R2, O2>(
|
|
411
|
+
cap: Policy<D, E2, R2, O2>,
|
|
412
|
+
): <E, R, O>(policy: Policy<D, E, R, O>) => Policy<D, E | E2, R | R2, O | O2>
|
|
413
|
+
<D extends Domain.Any, E, R, O, E2, R2, O2>(
|
|
414
|
+
policy: Policy<D, E, R, O>,
|
|
415
|
+
cap: Policy<D, E2, R2, O2>,
|
|
416
|
+
): Policy<D, E | E2, R | R2, O | O2>
|
|
417
|
+
} = internal.ceiling
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Gives the decision of a policy for a request.
|
|
421
|
+
*
|
|
422
|
+
* @stability stable
|
|
423
|
+
* @category evaluation
|
|
424
|
+
* @since 0.3.0
|
|
425
|
+
*/
|
|
426
|
+
export const decide: {
|
|
427
|
+
<D extends Domain.Any>(
|
|
428
|
+
request: Request<D>,
|
|
429
|
+
): <E, R, O>(
|
|
430
|
+
policy: Policy<D, E, R, O>,
|
|
431
|
+
) => Effect.Effect<Decision.Decision, E, R>
|
|
432
|
+
<D extends Domain.Any, E, R, O>(
|
|
433
|
+
policy: Policy<D, E, R, O>,
|
|
434
|
+
request: Request<NoInfer<D>>,
|
|
435
|
+
): Effect.Effect<Decision.Decision, E, R>
|
|
436
|
+
} = internal.decide as never
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Gives the decision of a policy with no predicates, at once.
|
|
440
|
+
*
|
|
441
|
+
* @stability stable
|
|
442
|
+
* @category evaluation
|
|
443
|
+
* @since 0.3.0
|
|
444
|
+
*/
|
|
445
|
+
export const decideSync: {
|
|
446
|
+
<D extends Domain.Any>(
|
|
447
|
+
request: Request<D>,
|
|
448
|
+
): (policy: Policy<D>) => Decision.Decision
|
|
449
|
+
<D extends Domain.Any>(
|
|
450
|
+
policy: Policy<D>,
|
|
451
|
+
request: Request<NoInfer<D>>,
|
|
452
|
+
): Decision.Decision
|
|
453
|
+
} = internal.decideSync
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Gives the decision of a policy and the rules that were tested to make it.
|
|
457
|
+
*
|
|
458
|
+
* @stability stable
|
|
459
|
+
* @category evaluation
|
|
460
|
+
* @since 0.3.0
|
|
461
|
+
*/
|
|
462
|
+
export const explain: {
|
|
463
|
+
<D extends Domain.Any>(
|
|
464
|
+
request: Request<D>,
|
|
465
|
+
): <E, R, O>(policy: Policy<D, E, R, O>) => Effect.Effect<Explanation, E, R>
|
|
466
|
+
<D extends Domain.Any, E, R, O>(
|
|
467
|
+
policy: Policy<D, E, R, O>,
|
|
468
|
+
request: Request<NoInfer<D>>,
|
|
469
|
+
): Effect.Effect<Explanation, E, R>
|
|
470
|
+
} = internal.explain as never
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Gives the condition on resource values for which the policy allows a
|
|
474
|
+
* request. It has no principal fields and no `Under` nodes, so you can give it
|
|
475
|
+
* to `Condition.fold`. It is `True` when the policy allows every value of the
|
|
476
|
+
* kind, and `False` when it allows none.
|
|
477
|
+
*
|
|
478
|
+
* Predicates stay in the condition. They take the principal with them.
|
|
479
|
+
*
|
|
480
|
+
* @stability stable
|
|
481
|
+
* @category evaluation
|
|
482
|
+
* @since 0.3.0
|
|
483
|
+
*/
|
|
484
|
+
export const residual: {
|
|
485
|
+
<D extends Domain.Any>(
|
|
486
|
+
request: KindRequest<D>,
|
|
487
|
+
): <E, R, O>(policy: Policy<D, E, R, O>) => Condition.Condition<E, R, O>
|
|
488
|
+
<D extends Domain.Any, E, R, O>(
|
|
489
|
+
policy: Policy<D, E, R, O>,
|
|
490
|
+
request: KindRequest<NoInfer<D>>,
|
|
491
|
+
): Condition.Condition<E, R, O>
|
|
492
|
+
} = internal.residual as never
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Gives the fields of a resource value that the policy lets a request use. It
|
|
496
|
+
* tests each field of the resource Schema, with dots for nested fields.
|
|
497
|
+
*
|
|
498
|
+
* @stability stable
|
|
499
|
+
* @category evaluation
|
|
500
|
+
* @since 0.3.0
|
|
501
|
+
*/
|
|
502
|
+
export const permittedFields: {
|
|
503
|
+
<D extends Domain.Any>(
|
|
504
|
+
request: Omit<Request<D>, "field">,
|
|
505
|
+
): <E, R, O>(
|
|
506
|
+
policy: Policy<D, E, R, O>,
|
|
507
|
+
) => Effect.Effect<ReadonlyArray<string>, E, R>
|
|
508
|
+
<D extends Domain.Any, E, R, O>(
|
|
509
|
+
policy: Policy<D, E, R, O>,
|
|
510
|
+
request: Omit<Request<NoInfer<D>>, "field">,
|
|
511
|
+
): Effect.Effect<ReadonlyArray<string>, E, R>
|
|
512
|
+
} = internal.permittedFields as never
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* The JSON form of a rule.
|
|
516
|
+
*
|
|
517
|
+
* @stability stable
|
|
518
|
+
* @category models
|
|
519
|
+
* @since 0.3.0
|
|
520
|
+
*/
|
|
521
|
+
export interface EncodedRule {
|
|
522
|
+
readonly _tag: "Allow" | "Deny"
|
|
523
|
+
readonly actions: ReadonlyArray<string>
|
|
524
|
+
readonly resources: ReadonlyArray<string>
|
|
525
|
+
readonly when?: Condition.Encoded
|
|
526
|
+
readonly fields?: ReadonlyArray<string>
|
|
527
|
+
readonly reason?: string
|
|
528
|
+
readonly id?: string
|
|
529
|
+
readonly meta?: unknown
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* The JSON form of a policy.
|
|
534
|
+
*
|
|
535
|
+
* @stability stable
|
|
536
|
+
* @category models
|
|
537
|
+
* @since 0.3.0
|
|
538
|
+
*/
|
|
539
|
+
export type EncodedNode =
|
|
540
|
+
| {
|
|
541
|
+
readonly _tag: "Rules"
|
|
542
|
+
readonly algorithm: Algorithm
|
|
543
|
+
readonly rules: ReadonlyArray<EncodedRule>
|
|
544
|
+
}
|
|
545
|
+
| {
|
|
546
|
+
readonly _tag: "All" | "Any" | "FirstOf"
|
|
547
|
+
readonly policies: ReadonlyArray<EncodedNode>
|
|
548
|
+
}
|
|
549
|
+
| {
|
|
550
|
+
readonly _tag: "Ceiling"
|
|
551
|
+
readonly cap: EncodedNode
|
|
552
|
+
readonly policy: EncodedNode
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* The Schema of the policies of a domain with no predicates. Decoding checks
|
|
557
|
+
* every kind, action, field and principal field against the domain, and the
|
|
558
|
+
* `meta` of each rule against the `meta` Schema of the domain.
|
|
559
|
+
*
|
|
560
|
+
* @stability stable
|
|
561
|
+
* @category schemas
|
|
562
|
+
* @since 0.3.0
|
|
563
|
+
*/
|
|
564
|
+
export const schema: <D extends Domain.Any>(
|
|
565
|
+
domain: D,
|
|
566
|
+
) => Schema.Codec<Policy<D>, EncodedNode> = internal.schema
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Tests if a value is a policy.
|
|
570
|
+
*
|
|
571
|
+
* @stability stable
|
|
572
|
+
* @category guards
|
|
573
|
+
* @since 0.3.0
|
|
574
|
+
*/
|
|
575
|
+
export const isPolicy: (u: unknown) => u is Any = internal.isPolicy
|
package/src/Principal.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A principal is the one who asks to do an act. It may be a person, a user
|
|
3
|
+
* token, or a Bot.
|
|
4
|
+
*
|
|
5
|
+
* A principal is a Schema. Make it with {@link Class}, in the same way as
|
|
6
|
+
* `Schema.Class`, or use any `Schema.Struct`. Conditions can use the fields of
|
|
7
|
+
* the principal. See `Condition.Builder.principal`.
|
|
8
|
+
*
|
|
9
|
+
* @stability stable
|
|
10
|
+
* @since 0.3.0
|
|
11
|
+
*/
|
|
12
|
+
import * as Schema from "effect/Schema"
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Any Schema that can be a principal. Its type must be an object.
|
|
16
|
+
*
|
|
17
|
+
* @stability stable
|
|
18
|
+
* @category models
|
|
19
|
+
* @since 0.3.0
|
|
20
|
+
*/
|
|
21
|
+
export type Any = Schema.Top & { readonly Type: object }
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Makes a principal as a Schema class. It is the same as `Schema.Class`.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* import { Schema } from "effect"
|
|
29
|
+
* import { Principal } from "@nmnmcc/ability"
|
|
30
|
+
*
|
|
31
|
+
* class Caller extends Principal.Class<Caller>("Caller")({
|
|
32
|
+
* userId: Schema.String,
|
|
33
|
+
* organizationId: Schema.String,
|
|
34
|
+
* }) {}
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @stability stable
|
|
38
|
+
* @category constructors
|
|
39
|
+
* @since 0.3.0
|
|
40
|
+
*/
|
|
41
|
+
export const Class: typeof Schema.Class = Schema.Class
|