Skip to content

Commit f3a11b3

Browse files
committed
feat!: rewrite to simpler interface
BREAKING CHANGE: the 2.x interface drops environments and resource types
1 parent 095b2da commit f3a11b3

17 files changed

+371
-478
lines changed

README.md

Lines changed: 45 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,15 @@ could-could is yet another authorization library built atop [JsonLogic](https://
55
## Features
66

77
- Generates the set of policies ahead of time for predictable performance
8-
- Allows for defining policies across `environments` (e.g. `development`, `production-use1`, etc) as well as `actions` (e.g. `create`, `kitty:PetKitty`, etc) on a given resource type. Policies will be merged with an `or` so any one matching policy is sufficient
98
- Provides for both `allow` and `deny` constraints. `deny` constraints will always take precedence
109
- Allows for a context to be provided when evaluating the policy, allowing for more complex conditional logic
11-
- Uses a portable [JSONSchema](./schemas/resource-policy-2022-04.schema.json) for validating the shape of the policies
10+
- Uses a portable [JSONSchema](./schemas/resource-policy-2023-02.schema.json) for validating the shape of the policies
1211
- Can extend JsonLogic with custom functions if needed (not recommended as you will need to implement the same functions in each consumer of the policy)
1312

1413
## Terms
1514

16-
- `resourceType`: an identifier for a type of resource to validate (e.g. `BlogPost`, `com.kitties.Kitty`)
17-
- `environment`: an arbitrary identifier for an operating context, allowing for different constraints in `test`, non-production (e.g. `alpha`, `preview`), or different regions of `production` (e.g. `production-use1`)
18-
- `action`: an identifier of what kind of action is being requested (e.g. `create`, `kittes:PetKitty`). Actions can be specified as a single action, an array of actions, or `*` which will match any action.
15+
- `action`: an identifier of what kind of action(s) are being requested (e.g. `create`, `kitties:PetKitty`)
16+
- Actions can be specified as a single action, an array of actions, or `*` which will match any action.
1917
- `effect`: what the result should be if the `constraint` is true
2018
- `constraint`: the rules that will be evaluated to determine if an action can occur
2119
- `context`: the extra data that can be provided to make decisions, such as a principal (e.g. user/service object) or the resource in question
@@ -24,72 +22,54 @@ could-could is yet another authorization library built atop [JsonLogic](https://
2422

2523
You will need a few pieces of information up front when creating a collection of policies:
2624

27-
1. a resource type naming convention
2825
1. a list of actions that are permitted for each resource type. All other actions will evaluate to `false`
29-
1. what environments you plan to use across all resource types, such as `development`, `test`, `beta`, `production`
26+
- TIP: use a namespacing scheme such as `documents:deleteDocument` to group actions by domain
3027
1. what information will be available in the context when evaluating an action
3128

3229
```jsonc
3330
// example policy stored somewhere that can be consumed at runtime
3431
{
35-
"resourceType": "Kitty",
36-
"actions": ["create", "read", "update", "pet"],
37-
"definitions": [
32+
"actions": ["kitty:create", "kitty:read", "kitty:update", "kitty:pet"],
33+
"policies": [
3834
{
39-
"environment": "*", // applies across all environments
40-
"policies": [
41-
{
42-
"description": "Allow everyone to create a kitty",
43-
"action": "create", // specify a single action
44-
"effect": "allow",
45-
"constraint": true
46-
},
47-
{
48-
"description": "Allow admins to take any action",
49-
"action": "*",
50-
"effect": "allow",
51-
"constraint": {
52-
// expects that context will include a subject object with a role property
53-
"===": [
54-
{ "var": "subject.role" },
55-
"admin"
56-
]
57-
}
58-
},
59-
{
60-
"description": "allow owners to read, update and pet the kitty",
61-
"action": ["read", "update", "pet"], // specify a list of actions
62-
"effect": "allow",
63-
"constraint": {
64-
"===": [
65-
{ "var": "subject.id" },
66-
{ "var": "kitty.ownerId" }
67-
]
68-
}
69-
}
70-
{
71-
"description": "Do not allow users (even admins!) to pet if the kitty is sleeping or eating",
72-
"action": "pet",
73-
"effect": "deny", // will override any allow statement
74-
"constraint": {
75-
"in": [
76-
{ "var": "kitty.state" },
77-
["eating", "sleeping"]
78-
]
79-
}
80-
}
81-
]
35+
"description": "Allow everyone to create a kitty",
36+
"action": "kitty:create", // specify a single action
37+
"effect": "allow",
38+
"constraint": true
8239
},
8340
{
84-
"environment": ["development", "alpha", "beta"], // specify a list of environments
85-
"policies": [
86-
{
87-
"description": "allow developers to update any kitty in non-prod environments",
88-
"action": "update",
89-
"effect": "allow",
90-
"constraint": true
91-
}
92-
]
41+
"description": "Allow admins to take any action",
42+
"action": "*",
43+
"effect": "allow",
44+
"constraint": {
45+
// expects that context will include a subject object with a role property
46+
"===": [
47+
{ "var": "subject.role" },
48+
"admin"
49+
]
50+
}
51+
},
52+
{
53+
"description": "allow owners to read, update and pet the kitty",
54+
"action": ["kitty:read", "kitty:update", "kitty:pet"], // specify a list of actions
55+
"effect": "allow",
56+
"constraint": {
57+
"===": [
58+
{ "var": "subject.id" },
59+
{ "var": "kitty.ownerId" }
60+
]
61+
}
62+
}
63+
{
64+
"description": "Do not allow users (even admins!) to pet if the kitty is sleeping or eating",
65+
"action": "kitty:pet",
66+
"effect": "deny", // will override any allow statement
67+
"constraint": {
68+
"in": [
69+
{ "var": "kitty.state" },
70+
["eating", "sleeping"]
71+
]
72+
}
9373
}
9474
]
9575
}
@@ -100,11 +80,11 @@ Next, in the consuming application create a resolver and make it available for u
10080
```ts
10181
import { createPolicyResolver } from '@freakyfelt/could-could'
10282

103-
const resolver = createPolicyResolver({ policies, targetEnvironment: process.env.NODE_ENV })
83+
const resolver = createPolicyResolver({ policies })
10484

10585
// ...then at runtime
10686
function petKitty(kitty: Kitty, { subject }: RequestContext) {
107-
if (!resolver.can({ action: 'pet', resourceType: 'Kitty' }, { kitty, subject })) {
87+
if (!resolver.can({ action: 'kitty:pet' }, { kitty, subject })) {
10888
throw new NotAuthorizedError()
10989
}
11090
}
@@ -122,4 +102,4 @@ The package is broken into three major areas: the validator, the parser, and the
122102

123103
* the validator checks that the provided resource policy matches the schema, has listed all potential actions defined in the constraints, and has an evaluatable set of constraints
124104
* the parser does the heavy lifting of turning a resource policy into a compiled Map of action => JsonLogic
125-
* the resolver accepts a policyStore and handles the runtime evaluation logic based on the provided action, resource type, and context
105+
* the resolver accepts a policyStore and handles the runtime evaluation logic based on the provided action and context

examples/Kitty.jsonc

Lines changed: 38 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -14,65 +14,49 @@
1414
}
1515
*/
1616
{
17-
"$schema": "../schemas/resource-policy-2022-04.schema.json",
17+
"$schema": "../schemas/resource-policy-2023-02.schema.json",
1818
"resourceType": "Kitty",
1919
"actions": ["create", "read", "update", "pet"],
20-
"definitions": [
20+
"policies": [
2121
{
22-
"environment": "*", // applies across all environments
23-
"policies": [
24-
{
25-
"description": "Allow everyone to create a kitty",
26-
"action": "create", // specify a single action
27-
"effect": "allow",
28-
"constraint": true
29-
},
30-
{
31-
"description": "Allow admins to take any action",
32-
"action": "*",
33-
"effect": "allow",
34-
"constraint": {
35-
// expects that context will include a subject object with a role property
36-
"===": [
37-
{ "var": "subject.role" },
38-
"admin"
39-
]
40-
}
41-
},
42-
{
43-
"description": "allow owners to read, update and pet the kitty",
44-
"action": ["read", "update", "pet"], // specify a list of actions
45-
"effect": "allow",
46-
"constraint": {
47-
"===": [
48-
{ "var": "subject.id" },
49-
{ "var": "kitty.ownerId" }
50-
]
51-
}
52-
},
53-
{
54-
"description": "Do not allow users (even admins!) to pet if the kitty is sleeping or eating",
55-
"action": "pet",
56-
"effect": "deny", // will override any allow statement
57-
"constraint": {
58-
"in": [
59-
{ "var": "kitty.state" },
60-
["eating", "sleeping"]
61-
]
62-
}
63-
}
64-
]
22+
"description": "Allow everyone to create a kitty",
23+
"action": "create", // specify a single action
24+
"effect": "allow",
25+
"constraint": true
6526
},
6627
{
67-
"environment": ["development", "alpha", "beta"], // specify a list of environments
68-
"policies": [
69-
{
70-
"description": "allow developers to update any kitty in non-prod environments",
71-
"action": "update",
72-
"effect": "allow",
73-
"constraint": true
74-
}
75-
]
28+
"description": "Allow admins to take any action",
29+
"action": "*",
30+
"effect": "allow",
31+
"constraint": {
32+
// expects that context will include a subject object with a role property
33+
"===": [
34+
{ "var": "subject.role" },
35+
"admin"
36+
]
37+
}
38+
},
39+
{
40+
"description": "allow owners to read, update and pet the kitty",
41+
"action": ["read", "update", "pet"], // specify a list of actions
42+
"effect": "allow",
43+
"constraint": {
44+
"===": [
45+
{ "var": "subject.id" },
46+
{ "var": "kitty.ownerId" }
47+
]
48+
}
49+
},
50+
{
51+
"description": "Do not allow users (even admins!) to pet if the kitty is sleeping or eating",
52+
"action": "pet",
53+
"effect": "deny", // will override any allow statement
54+
"constraint": {
55+
"in": [
56+
{ "var": "kitty.state" },
57+
["eating", "sleeping"]
58+
]
59+
}
7660
}
7761
]
7862
}

0 commit comments

Comments
 (0)