@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
package/README.md
CHANGED
|
@@ -15,6 +15,8 @@ The library is made with [Effect](https://effect.website) only:
|
|
|
15
15
|
- An `Authorizer` is an Effect service. A "no" goes into the error channel. When
|
|
16
16
|
a policy can not be loaded, the check fails, so an error is never a "yes".
|
|
17
17
|
|
|
18
|
+
Version 2.0 changed the API. See [MIGRATION.md](MIGRATION.md) to move from 1.x.
|
|
19
|
+
|
|
18
20
|
## Requirements
|
|
19
21
|
|
|
20
22
|
- Use strict type checking. Without it, the types do not give all of their
|
|
@@ -36,9 +38,7 @@ import {
|
|
|
36
38
|
Decision,
|
|
37
39
|
Domain,
|
|
38
40
|
Policy,
|
|
39
|
-
Principal,
|
|
40
41
|
Resource,
|
|
41
|
-
Rule,
|
|
42
42
|
} from "@nmnmcc/ability"
|
|
43
43
|
import * as AbilityHttpApi from "@nmnmcc/ability/HttpApi"
|
|
44
44
|
```
|
|
@@ -46,13 +46,12 @@ import * as AbilityHttpApi from "@nmnmcc/ability/HttpApi"
|
|
|
46
46
|
| Module | What it is |
|
|
47
47
|
| ------------ | --------------------------------------------------------------------------- |
|
|
48
48
|
| `Resource` | A kind of thing to act on, with its actions, its Schema, and a path. |
|
|
49
|
-
| `
|
|
50
|
-
| `Domain` | The principal and all the resources. Every other type comes from it. |
|
|
49
|
+
| `Domain` | The principal Schema and all the resources. Every type comes from it. |
|
|
51
50
|
| `Condition` | A test on a resource value, as plain data. |
|
|
52
|
-
| `
|
|
53
|
-
| `Policy` | A tree of rules that gives a decision, a residual condition, and fields. |
|
|
51
|
+
| `Policy` | Rules, and a tree of rules that gives a decision, a residual and fields. |
|
|
54
52
|
| `Decision` | `Allow`, `Deny` or `NotApplicable`. |
|
|
55
53
|
| `Authorizer` | An Effect service that checks the principal of the current request. |
|
|
54
|
+
| `Limits` | The limits that keep every walk and every decoder small. |
|
|
56
55
|
| `HttpApi` | Annotations and a middleware for `effect/http-api`. A separate entry point. |
|
|
57
56
|
|
|
58
57
|
## First Steps
|
|
@@ -63,9 +62,9 @@ the same time:
|
|
|
63
62
|
|
|
64
63
|
```ts
|
|
65
64
|
import { Effect, Schema } from "effect"
|
|
66
|
-
import { Decision, Domain, Policy,
|
|
65
|
+
import { Decision, Domain, Policy, Resource } from "@nmnmcc/ability"
|
|
67
66
|
|
|
68
|
-
class User extends
|
|
67
|
+
class User extends Schema.Class<User>("User")({
|
|
69
68
|
id: Schema.String,
|
|
70
69
|
}) {}
|
|
71
70
|
|
|
@@ -183,9 +182,12 @@ _.allow("read", "*", {
|
|
|
183
182
|
and the resource may be lists.
|
|
184
183
|
- The action `"*"` means every action. The resource `"*"` means every resource.
|
|
185
184
|
- `when` is the condition. It gets a builder with the types of the resource.
|
|
186
|
-
- `fields` makes the rule about some fields only.
|
|
187
|
-
|
|
188
|
-
|
|
185
|
+
- `fields` makes the rule about some fields only. A field name covers the fields
|
|
186
|
+
below it too, so `settings` covers `settings.model`. In a pattern, `*` stands
|
|
187
|
+
for any text inside one part of a name, as in `secret*`. `**` stands for any
|
|
188
|
+
number of parts, also none, and it must be a whole part: `**.secret` covers
|
|
189
|
+
`secret` and `settings.secret`. A pattern that matches no field of the
|
|
190
|
+
resources of the rule is an error, so a mistake in a name shows at once.
|
|
189
191
|
- `reason`, `id` and `meta` are kept with the rule. `meta` is extra data, such
|
|
190
192
|
as the scope of a grant. Give its Schema to `Domain.make({ meta })`.
|
|
191
193
|
|
|
@@ -193,11 +195,23 @@ An allow rule with fields covers a request for the whole value: "may update some
|
|
|
193
195
|
fields" is a yes. This is true only for a kind that has one of these fields. A
|
|
194
196
|
deny rule with fields does not stop a request for the whole value.
|
|
195
197
|
|
|
196
|
-
To know if a kind has a field that a pattern fits, the library
|
|
197
|
-
of the kind: its fields
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
198
|
+
To know if a kind has a field that a pattern fits, the library reads the shape
|
|
199
|
+
of the Schema of the kind: its fields and the keys of its records. A name in a
|
|
200
|
+
pattern fits a key of a record when the Schema of the keys takes it. A part with
|
|
201
|
+
a star fits any key.
|
|
202
|
+
|
|
203
|
+
`Policy.define` checks every rule against the domain when the policy is made. A
|
|
204
|
+
rule with a mistake is a `PolicyError`, with each problem and its path. For a
|
|
205
|
+
name with a mistake, it gives the name that you may mean:
|
|
206
|
+
|
|
207
|
+
```text
|
|
208
|
+
A rule does not fit the domain: The field "titel" is not a field of every
|
|
209
|
+
resource of the rule. Did you mean "title"?
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
A condition reads only the data fields of a value: an own field that a loop over
|
|
213
|
+
the keys sees. A rule that names a getter of a class is a `PolicyError`. Use a
|
|
214
|
+
predicate for a value that comes from code.
|
|
201
215
|
|
|
202
216
|
## Conditions
|
|
203
217
|
|
|
@@ -226,8 +240,12 @@ or a query, and the type system stops you when you try.
|
|
|
226
240
|
|
|
227
241
|
A predicate also keeps the types of the value and the principal that it reads.
|
|
228
242
|
So a predicate for a Document can not go into a rule for a Comment. Make
|
|
229
|
-
conditions with the builder: a node made by hand
|
|
230
|
-
types.
|
|
243
|
+
conditions with the builder: a node made by hand, or a spread of a node, is not
|
|
244
|
+
a condition, in the types or when the code runs.
|
|
245
|
+
|
|
246
|
+
A condition may have at most 100000 nodes and 500 levels, with a shared part
|
|
247
|
+
counted for each place. The builder throws an error for a larger condition. So
|
|
248
|
+
no condition can make a check slow or fill the stack.
|
|
231
249
|
|
|
232
250
|
## Policies
|
|
233
251
|
|
|
@@ -258,11 +276,15 @@ Put policies together:
|
|
|
258
276
|
| `Policy.firstOf([a, b])` | The first policy that allows or denies. |
|
|
259
277
|
| `Policy.ceiling(policy, cap)` | A deny from `cap` always wins. An allow from `cap` does nothing. |
|
|
260
278
|
|
|
261
|
-
A policy tree may have at most 256 levels
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
field path may have at most
|
|
279
|
+
A policy tree may have at most 256 levels and 100000 nodes, with its rules and
|
|
280
|
+
the nodes of their conditions, and a shared part counted for each place. A
|
|
281
|
+
combinator throws an error for a larger tree, and the policy Schema does not
|
|
282
|
+
decode one. The decoder reads at most 1000000 objects and array items, so no
|
|
283
|
+
input can fill the stack or take a very long time. A field path may have at most
|
|
284
|
+
5 parts.
|
|
285
|
+
|
|
286
|
+
A shared part of a tree is decided again for each place, so its predicates run
|
|
287
|
+
each time, and `Policy.explain` lists its rules each time.
|
|
266
288
|
|
|
267
289
|
For example, a user token may never do more than its member, and the platform
|
|
268
290
|
has deny rules for everyone:
|
|
@@ -299,9 +321,7 @@ it gives the same answers and the same failures as `Policy.decide`.
|
|
|
299
321
|
|
|
300
322
|
A deep policy tree may give a very large residual. When it would have more than
|
|
301
323
|
10000 nodes, `Policy.residual` throws an error, and a check in an `Authorizer`
|
|
302
|
-
fails.
|
|
303
|
-
getter of a class: the read may fail when the policy runs, and a smaller
|
|
304
|
-
condition could leave it out.
|
|
324
|
+
fails.
|
|
305
325
|
|
|
306
326
|
### Scopes
|
|
307
327
|
|
|
@@ -342,32 +362,19 @@ list query for the scope:
|
|
|
342
362
|
so a scope with a mistake never gives a "yes".
|
|
343
363
|
- `Some`: the policy allows some values and not others. It gives an example of
|
|
344
364
|
each, `allowed` and `refused`.
|
|
345
|
-
- `Unknown`: the policy has a predicate,
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
value
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
a
|
|
358
|
-
other than finite and integer checks, on a path of required fields of plain
|
|
359
|
-
structs. For another field, such as a class, a record, a union of structs, a
|
|
360
|
-
date or a field of `Schema.declare`, a value may have data that the Schema does
|
|
361
|
-
not show, so the answer is `Unknown`. A check on a kind then says no.
|
|
362
|
-
`Resource.isPlainField` tells if a field is plain. This is true for each rule
|
|
363
|
-
that the request may use, also for a test that a smaller condition leaves out: a
|
|
364
|
-
read of a field that is not plain, such as a getter, may fail in a check.
|
|
365
|
-
|
|
366
|
-
A "yes" also needs a value of the whole kind, so each required field of the kind
|
|
367
|
-
must surely have values, also a field that the condition does not test. A
|
|
368
|
-
required field with a check other than finite and integer checks, such as a
|
|
369
|
-
minimum length, may have no value, so then the answer is `Unknown` in place of
|
|
370
|
-
`All` or `Some`. `Resource.hasValue` tells if a Schema surely has a value.
|
|
365
|
+
- `Unknown`: the policy has a predicate, or the search went past its budget.
|
|
366
|
+
|
|
367
|
+
`Condition.satisfiable` does the search. Each test in a condition compares one
|
|
368
|
+
field with fixed values, so these values cut the values of the field into a few
|
|
369
|
+
parts, and every value in one part gives the same answer. The search tries one
|
|
370
|
+
value from each part, one field at a time.
|
|
371
|
+
|
|
372
|
+
The search reads the shape of the Schema of the resource: the fields, their
|
|
373
|
+
literals, `null` for a nullable or optional field, and finite or integer
|
|
374
|
+
numbers. It does not read other checks, such as a minimum length. A check only
|
|
375
|
+
keeps values out, so the shape holds every real value and more. So `All` and
|
|
376
|
+
`None` are right for every value of the Schema. `Some` means that, by the shape,
|
|
377
|
+
a value may get either answer. A check may still stop one of the examples.
|
|
371
378
|
|
|
372
379
|
"Some" means that some value of the Schema is allowed. It does not mean that
|
|
373
380
|
such a value is in your database. To know that, run the condition as a query.
|
|
@@ -426,21 +433,27 @@ request, loads its policy, and keeps the policy in a cache.
|
|
|
426
433
|
```ts
|
|
427
434
|
class Identity extends Context.Service<Identity, User>()("app/Identity") {}
|
|
428
435
|
|
|
429
|
-
class Auth extends Authorizer.Service<Auth
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
) {}
|
|
436
|
+
class Auth extends Authorizer.Service<Auth>()("app/Auth", {
|
|
437
|
+
domain: Blog,
|
|
438
|
+
principal: Identity,
|
|
439
|
+
}) {}
|
|
433
440
|
|
|
434
441
|
const AuthLive = Authorizer.layer(Auth, {
|
|
435
|
-
principal: Identity,
|
|
436
442
|
policy: (user) => loadPolicyOf(user),
|
|
437
443
|
cache: { capacity: 1000, timeToLive: "30 seconds" },
|
|
438
444
|
})
|
|
439
445
|
```
|
|
440
446
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
447
|
+
The error type and the service type of every check come from the `principal`
|
|
448
|
+
Effect.
|
|
449
|
+
|
|
450
|
+
The cache key of a principal is the text of its JSON form, from the Schema of
|
|
451
|
+
the principal of the domain. A principal is kept only when the value that the
|
|
452
|
+
Schema decodes from that form is equal to it, so two principals with one key are
|
|
453
|
+
equal. The loader always gets that decoded copy, also with no cache, so it must
|
|
454
|
+
decide by the data of the principal, not by the identity of its objects. Give
|
|
455
|
+
`cache: { key }` to make the key in another way, or `cache: false` to load the
|
|
456
|
+
policy for each check.
|
|
444
457
|
|
|
445
458
|
Use it in a handler:
|
|
446
459
|
|
|
@@ -499,7 +512,6 @@ service must answer later:
|
|
|
499
512
|
|
|
500
513
|
```ts
|
|
501
514
|
Authorizer.layer(Auth, {
|
|
502
|
-
principal: Identity,
|
|
503
515
|
policy: loadPolicyOf,
|
|
504
516
|
decider: (request) =>
|
|
505
517
|
requests
|
|
@@ -548,9 +560,9 @@ const AuthorizationLive = AbilityHttpApi.layer(Authorization, Auth)
|
|
|
548
560
|
|
|
549
561
|
An act on a part of a kind takes a scope from the path parameters. A list takes
|
|
550
562
|
`lists`: the middleware does not check every value, because the handler gets its
|
|
551
|
-
query
|
|
552
|
-
unless you give `nonEmpty: true`. Then the middleware needs a coverage of
|
|
553
|
-
or `Some`, so `Unknown` fails too:
|
|
563
|
+
query from the same requirement, with the same scope. An empty list is not an
|
|
564
|
+
error, unless you give `nonEmpty: true`. Then the middleware needs a coverage of
|
|
565
|
+
`All` or `Some`, so `Unknown` fails too:
|
|
554
566
|
|
|
555
567
|
```ts
|
|
556
568
|
const create = HttpApiEndpoint.post("create", "/users/:user/posts", {
|
|
@@ -561,13 +573,23 @@ const create = HttpApiEndpoint.post("create", "/users/:user/posts", {
|
|
|
561
573
|
}),
|
|
562
574
|
])
|
|
563
575
|
|
|
576
|
+
const postsOfUser = AbilityHttpApi.lists(Auth, "read", "Post", {
|
|
577
|
+
within: (params) => ($) => $.eq("authorId", params["user"] ?? ""),
|
|
578
|
+
})
|
|
579
|
+
|
|
564
580
|
const list = HttpApiEndpoint.get("list", "/users/:user/posts", {
|
|
565
581
|
params: { user: Schema.String },
|
|
566
|
-
}).annotate(AbilityHttpApi.Required, [
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
582
|
+
}).annotate(AbilityHttpApi.Required, [postsOfUser])
|
|
583
|
+
|
|
584
|
+
const PostsLive = HttpApiBuilder.group(Api, "posts", (handlers) =>
|
|
585
|
+
Effect.map(Auth, (auth) =>
|
|
586
|
+
handlers.handle("list", () =>
|
|
587
|
+
postsOfUser
|
|
588
|
+
.filter(auth)
|
|
589
|
+
.pipe(Effect.flatMap(toSql), Effect.flatMap(Posts.query)),
|
|
590
|
+
),
|
|
591
|
+
),
|
|
592
|
+
)
|
|
571
593
|
```
|
|
572
594
|
|
|
573
595
|
A path parameter may be `undefined`. A scope with `undefined` in it is a defect,
|
|
@@ -581,6 +603,27 @@ group without the middleware is not checked.
|
|
|
581
603
|
one for every other method. `AbilityHttpApi.errors` gives the errors with HTTP
|
|
582
604
|
status codes: 403, 503, and 409.
|
|
583
605
|
|
|
606
|
+
## Limits
|
|
607
|
+
|
|
608
|
+
Each limit keeps a walk, a decoder or a search small, so no input can fill the
|
|
609
|
+
stack or take a very long time. An input past a limit is an error, as in "A
|
|
610
|
+
condition has 100001 nodes. The most is 100000.". In an `Authorizer`, such an
|
|
611
|
+
error is never a "yes". The `Limits` module gives each value.
|
|
612
|
+
|
|
613
|
+
| Limit | Value | What it limits |
|
|
614
|
+
| ---------------- | ------- | ------------------------------------------------------- |
|
|
615
|
+
| `fieldPathParts` | 5 | The parts of a field path, as in `settings.model`. |
|
|
616
|
+
| `patternParts` | 16 | The parts of a field pattern. |
|
|
617
|
+
| `conditionNodes` | 100000 | The nodes of a condition, a shared part for each place. |
|
|
618
|
+
| `conditionDepth` | 500 | The levels of a condition. |
|
|
619
|
+
| `residualNodes` | 10000 | The nodes of a residual condition. |
|
|
620
|
+
| `policyDepth` | 256 | The levels of a policy tree. |
|
|
621
|
+
| `policyNodes` | 100000 | The nodes of a policy tree, with its rules. |
|
|
622
|
+
| `jsonNodes` | 1000000 | The objects and items that a decoder reads. |
|
|
623
|
+
| `catalogFields` | 10000 | The fields of the Schema of a resource. |
|
|
624
|
+
| `searchSteps` | 1000000 | The steps of one search, as in `Policy.coverage`. |
|
|
625
|
+
| `searchFields` | 256 | The fields that one search may test. |
|
|
626
|
+
|
|
584
627
|
## Examples
|
|
585
628
|
|
|
586
629
|
Each example is a small program. Run it with Node.js, which runs `.ts` files
|
package/dist/Authorizer.d.ts
CHANGED
|
@@ -1,21 +1,15 @@
|
|
|
1
1
|
import { Condition, Opaque } from "./Condition.js";
|
|
2
2
|
import { Any, CommonAction, Kind, Principal } from "./Domain.js";
|
|
3
|
-
import { Decision } from "./Decision.js";
|
|
4
3
|
import { Coverage, FieldlessTarget, KindRequest, KindTarget, Policy, Request, ResidualReads, Target as Target$1 } from "./Policy.js";
|
|
4
|
+
import { Decision } from "./Decision.js";
|
|
5
|
+
import { Forbidden as Forbidden$1, Pending as Pending$1, PolicyUnavailable as PolicyUnavailable$1 } from "./internal/authorizer.js";
|
|
5
6
|
import * as Context from "effect/Context";
|
|
6
7
|
import * as Effect from "effect/Effect";
|
|
7
8
|
import * as Layer from "effect/Layer";
|
|
8
|
-
import * as Schema from "effect/Schema";
|
|
9
9
|
import * as Duration from "effect/Duration";
|
|
10
10
|
declare namespace Authorizer_d_exports {
|
|
11
|
-
export { AnyServiceClass, Authorizer,
|
|
11
|
+
export { AnyServiceClass, Authorizer, DeciderRequest, Forbidden, GuardOptions, Options, Pending, PolicyUnavailable, Service, ServiceClass, Target, layer };
|
|
12
12
|
}
|
|
13
|
-
declare const Forbidden_base: Schema.Class<Forbidden, Schema.TaggedStruct<"Forbidden", {
|
|
14
|
-
readonly action: Schema.String;
|
|
15
|
-
readonly kind: Schema.String;
|
|
16
|
-
readonly field: Schema.optionalKey<Schema.String>;
|
|
17
|
-
readonly reason: Schema.optionalKey<Schema.String>;
|
|
18
|
-
}>, import("effect/Cause").YieldableError>;
|
|
19
13
|
/**
|
|
20
14
|
* The error when a policy says no.
|
|
21
15
|
*
|
|
@@ -23,10 +17,13 @@ declare const Forbidden_base: Schema.Class<Forbidden, Schema.TaggedStruct<"Forbi
|
|
|
23
17
|
* @category errors
|
|
24
18
|
* @since 0.3.0
|
|
25
19
|
*/
|
|
26
|
-
export declare
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
export declare const Forbidden: typeof Forbidden$1;
|
|
21
|
+
/**
|
|
22
|
+
* @stability stable
|
|
23
|
+
* @category errors
|
|
24
|
+
* @since 0.3.0
|
|
25
|
+
*/
|
|
26
|
+
export type Forbidden = Forbidden$1;
|
|
30
27
|
/**
|
|
31
28
|
* The error when the policy of the principal can not be loaded. The cause is
|
|
32
29
|
* kept, but it is not shown to the client.
|
|
@@ -35,11 +32,13 @@ declare const PolicyUnavailable_base: Schema.Class<PolicyUnavailable, Schema.Tag
|
|
|
35
32
|
* @category errors
|
|
36
33
|
* @since 0.3.0
|
|
37
34
|
*/
|
|
38
|
-
export declare
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
export declare const PolicyUnavailable: typeof PolicyUnavailable$1;
|
|
36
|
+
/**
|
|
37
|
+
* @stability stable
|
|
38
|
+
* @category errors
|
|
39
|
+
* @since 0.3.0
|
|
40
|
+
*/
|
|
41
|
+
export type PolicyUnavailable = PolicyUnavailable$1;
|
|
43
42
|
/**
|
|
44
43
|
* The error when a decider must wait for an answer. `ticket` names the
|
|
45
44
|
* request that waits, so the client can come back for the answer.
|
|
@@ -48,25 +47,13 @@ declare const Pending_base: Schema.Class<Pending, Schema.TaggedStruct<"Pending",
|
|
|
48
47
|
* @category errors
|
|
49
48
|
* @since 0.3.0
|
|
50
49
|
*/
|
|
51
|
-
export declare
|
|
50
|
+
export declare const Pending: typeof Pending$1;
|
|
52
51
|
/**
|
|
53
|
-
* The services and the errors of the principal and the decider of an
|
|
54
|
-
* authorizer.
|
|
55
|
-
*
|
|
56
52
|
* @stability stable
|
|
57
|
-
* @category
|
|
53
|
+
* @category errors
|
|
58
54
|
* @since 0.3.0
|
|
59
55
|
*/
|
|
60
|
-
export
|
|
61
|
-
readonly requires?: unknown;
|
|
62
|
-
readonly error?: unknown;
|
|
63
|
-
}
|
|
64
|
-
type RequiresOf<C> = C extends {
|
|
65
|
-
readonly requires: infer R;
|
|
66
|
-
} ? R : never;
|
|
67
|
-
type ErrorOf<C> = C extends {
|
|
68
|
-
readonly error: infer E;
|
|
69
|
-
} ? E : never;
|
|
56
|
+
export type Pending = Pending$1;
|
|
70
57
|
/**
|
|
71
58
|
* What a check is about: an act on a resource value, or an act on a kind of
|
|
72
59
|
* resource. A check on a kind passes only when the policy allows the act on
|
|
@@ -125,7 +112,7 @@ export interface Authorizer<D extends Any, E, R> {
|
|
|
125
112
|
* The policy of the principal of the current request. Its predicates have
|
|
126
113
|
* the services of the layer. It may have predicates, so it is opaque.
|
|
127
114
|
*/
|
|
128
|
-
readonly policy: Effect.Effect<Policy<D, E
|
|
115
|
+
readonly policy: Effect.Effect<Policy<D, Opaque<E>>, PolicyUnavailable | E, R>;
|
|
129
116
|
/** Gives the decision of the policy. It does not ask the decider. */
|
|
130
117
|
readonly decide: (target: Target$1<D>) => Effect.Effect<Decision, PolicyUnavailable | E, R>;
|
|
131
118
|
/** Succeeds when the principal may do the act, and fails when it may not. */
|
|
@@ -143,13 +130,13 @@ export interface Authorizer<D extends Any, E, R> {
|
|
|
143
130
|
* with `Condition.fold`. It is `False` when the principal may act on no
|
|
144
131
|
* value, so the list is empty: it is not an error.
|
|
145
132
|
*/
|
|
146
|
-
readonly filter: <const T extends KindTarget<D>>(target: T) => Effect.Effect<Condition<
|
|
133
|
+
readonly filter: <const T extends KindTarget<D>>(target: T) => Effect.Effect<Condition<ResidualReads<D, T["kind"]>, Opaque<E>>, PolicyUnavailable | E, R>;
|
|
147
134
|
/**
|
|
148
135
|
* Tells if the principal may act on all, none or some of the values in
|
|
149
136
|
* the scope of the target. See `Policy.coverage`. Use it to show a button
|
|
150
137
|
* only when the answer is not `None`.
|
|
151
138
|
*/
|
|
152
|
-
readonly coverage: <const T extends KindTarget<D>>(target: T) => Effect.Effect<Coverage<
|
|
139
|
+
readonly coverage: <const T extends KindTarget<D>>(target: T) => Effect.Effect<Coverage<ResidualReads<D, T["kind"]>, Opaque<E>>, PolicyUnavailable | E, R>;
|
|
153
140
|
/** Gives the fields of a value that the principal may use. */
|
|
154
141
|
readonly permittedFields: (target: FieldlessTarget<D>) => Effect.Effect<ReadonlyArray<string>, PolicyUnavailable | E, R>;
|
|
155
142
|
/**
|
|
@@ -168,11 +155,13 @@ export interface Authorizer<D extends Any, E, R> {
|
|
|
168
155
|
*/
|
|
169
156
|
export interface ServiceClass<Self, Id extends string, D extends Any, E, R> extends Context.ServiceClass<Self, Id, Authorizer<D, E, R>> {
|
|
170
157
|
readonly domain: D;
|
|
158
|
+
/** The Effect that gives the principal of the current request. */
|
|
159
|
+
readonly principal: Effect.Effect<Principal<D>, E, R>;
|
|
171
160
|
readonly decide: (target: Target$1<D>) => Effect.Effect<Decision, PolicyUnavailable | E, R | Self>;
|
|
172
161
|
readonly check: (target: Target<D>) => Effect.Effect<void, Forbidden | Pending | PolicyUnavailable | E, R | Self>;
|
|
173
162
|
readonly guard: <const T extends Target$1<D>, E2>(target: T, options: GuardOptions<D, E2, T["resource"]["_tag"]>) => Effect.Effect<void, E2 | Forbidden | Pending | PolicyUnavailable | E, R | Self>;
|
|
174
|
-
readonly filter: <const T extends KindTarget<D>>(target: T) => Effect.Effect<Condition<
|
|
175
|
-
readonly coverage: <const T extends KindTarget<D>>(target: T) => Effect.Effect<Coverage<
|
|
163
|
+
readonly filter: <const T extends KindTarget<D>>(target: T) => Effect.Effect<Condition<ResidualReads<D, T["kind"]>, Opaque<E>>, PolicyUnavailable | E, R | Self>;
|
|
164
|
+
readonly coverage: <const T extends KindTarget<D>>(target: T) => Effect.Effect<Coverage<ResidualReads<D, T["kind"]>, Opaque<E>>, PolicyUnavailable | E, R | Self>;
|
|
176
165
|
readonly permittedFields: (target: FieldlessTarget<D>) => Effect.Effect<ReadonlyArray<string>, PolicyUnavailable | E, R | Self>;
|
|
177
166
|
}
|
|
178
167
|
/**
|
|
@@ -184,6 +173,7 @@ export interface ServiceClass<Self, Id extends string, D extends Any, E, R> exte
|
|
|
184
173
|
*/
|
|
185
174
|
export type AnyServiceClass = Context.Key<any, any> & {
|
|
186
175
|
readonly domain: Any;
|
|
176
|
+
readonly principal: Effect.Effect<any, any, any>;
|
|
187
177
|
};
|
|
188
178
|
/**
|
|
189
179
|
* Makes an authorizer service class for a domain.
|
|
@@ -192,7 +182,11 @@ export type AnyServiceClass = Context.Key<any, any> & {
|
|
|
192
182
|
* @category constructors
|
|
193
183
|
* @since 0.3.0
|
|
194
184
|
*/
|
|
195
|
-
export declare const Service: <Self
|
|
185
|
+
export declare const Service: <Self>() => <const Id extends string, D extends Any, E = never, R = never>(id: Id, options: {
|
|
186
|
+
readonly domain: D;
|
|
187
|
+
/** Gives the principal of the current request. */
|
|
188
|
+
readonly principal: Effect.Effect<Principal<NoInfer<D>>, E, R>;
|
|
189
|
+
}) => ServiceClass<Self, Id, D, E, R>;
|
|
196
190
|
/**
|
|
197
191
|
* The options of {@link layer}.
|
|
198
192
|
*
|
|
@@ -201,14 +195,12 @@ export declare const Service: <Self, C extends Config = {}>() => <const Id exten
|
|
|
201
195
|
* @since 0.3.0
|
|
202
196
|
*/
|
|
203
197
|
export interface Options<D extends Any, E, R, EL, RL, RP> {
|
|
204
|
-
/** Gives the principal of the current request. */
|
|
205
|
-
readonly principal: Effect.Effect<Principal<D>, E, R>;
|
|
206
198
|
/**
|
|
207
199
|
* Loads the policy of a principal. A failure becomes
|
|
208
200
|
* {@link PolicyUnavailable}. The services of the predicates of the policy
|
|
209
201
|
* are taken when the layer is built.
|
|
210
202
|
*/
|
|
211
|
-
readonly policy: (principal: Principal<D>) => Effect.Effect<Policy<D, E, RP
|
|
203
|
+
readonly policy: (principal: Principal<D>) => Effect.Effect<Policy<D, Opaque<E, RP>>, EL, RL>;
|
|
212
204
|
/**
|
|
213
205
|
* Answers when no rule gives an answer. Without a decider, such a request
|
|
214
206
|
* fails with {@link Forbidden}.
|
|
@@ -218,15 +210,19 @@ export interface Options<D extends Any, E, R, EL, RL, RP> {
|
|
|
218
210
|
* How the policies are kept. The default keeps 1000 policies for 30 seconds.
|
|
219
211
|
* Give `false` to load the policy for each check.
|
|
220
212
|
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
213
|
+
* The key of a principal is the text of its JSON form, from the Schema of
|
|
214
|
+
* the principal of the domain. A principal is kept only when the value that
|
|
215
|
+
* the Schema decodes from that form is `Equal` to it. The loader always
|
|
216
|
+
* gets that decoded copy, also with no cache, so two principals with one
|
|
217
|
+
* key get one policy, and the cache can not change an answer. So the loader
|
|
218
|
+
* must decide by the data of the principal, not by the identity of its
|
|
219
|
+
* objects. A principal with no JSON form is not kept, and the loader gets
|
|
220
|
+
* it as it is. Give `key` to make the key in another way.
|
|
226
221
|
*/
|
|
227
222
|
readonly cache?: {
|
|
228
223
|
readonly capacity?: number;
|
|
229
224
|
readonly timeToLive?: Duration.Input;
|
|
225
|
+
readonly key?: (principal: Principal<D>) => string | undefined;
|
|
230
226
|
} | false;
|
|
231
227
|
}
|
|
232
228
|
/**
|
package/dist/Authorizer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Authorizer.d.ts","names":["Policy.Target","Policy.KindTarget","Policy.Request","Policy.KindRequest","Domain.Kind","Domain.CommonAction","Domain.Any","Domain.Principal","Condition.Opaque","Policy.Policy","Decision.Decision","Policy.ResidualReads","Condition.Condition","Policy.Coverage","Policy.FieldlessTarget"],"sources":["../src/Authorizer.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"Authorizer.d.ts","names":["Internal.Forbidden","Internal.PolicyUnavailable","Internal.Pending","Policy.Target","Policy.KindTarget","Policy.Request","Policy.KindRequest","Domain.Kind","Domain.CommonAction","Domain.Any","Domain.Principal","Condition.Opaque","Policy.Policy","Decision.Decision","Policy.ResidualReads","Condition.Condition","Policy.Coverage","Policy.FieldlessTarget"],"sources":["../src/Authorizer.ts"],"mappings":"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"}
|