simple-authz 1.0.5 → 1.2.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 +152 -348
- package/bin/simple-authz/index.js +132 -0
- package/lib/cache.js +149 -0
- package/lib/core/authz.js +73 -5
- package/lib/core/middleware.js +44 -0
- package/lib/engine/conditionCompiler.js +122 -21
- package/lib/engine/conditionExecutor.js +30 -6
- package/lib/rules/parser.js +62 -12
- package/lib/rules/validator.js +16 -10
- package/package.json +52 -36
- package/types/index.d.ts +31 -0
- package/simple-authz-1.0.1.tgz +0 -0
- package/simple-authz-1.0.2.tgz +0 -0
- package/simple-authz-1.0.3.tgz +0 -0
- package/simple-authz-1.0.4.tgz +0 -0
- package/simple-authz-1.0.5.tgz +0 -0
- package/simple-authz-v1.0.1.tgz +0 -0
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
---
|
|
2
|
+
|
|
1
3
|
# Simple Authz
|
|
2
4
|
|
|
3
5
|
A lightweight and flexible **authorization engine for Node.js** built around a simple policy language called **TOON (Token-Oriented Object Notation)**.
|
|
@@ -31,6 +33,7 @@ We recommend testing thoroughly before using it in production.
|
|
|
31
33
|
- Using Conditions
|
|
32
34
|
- Working with Roles
|
|
33
35
|
- Authorization API
|
|
36
|
+
- Middleware (Express)
|
|
34
37
|
- Example Integrations
|
|
35
38
|
- Project Structure
|
|
36
39
|
- Performance
|
|
@@ -55,66 +58,38 @@ Over time this logic spreads across many files and becomes difficult to maintain
|
|
|
55
58
|
|
|
56
59
|
**Simple Authz solves this by moving authorization rules into policy files.**
|
|
57
60
|
|
|
58
|
-
Example policy file:
|
|
59
|
-
|
|
60
|
-
```
|
|
61
|
-
rule
|
|
62
|
-
role admin
|
|
63
|
-
action *
|
|
64
|
-
resource *
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
rule
|
|
68
|
-
role broker
|
|
69
|
-
action publish
|
|
70
|
-
resource listing
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
rule
|
|
74
|
-
role user
|
|
75
|
-
action edit
|
|
76
|
-
resource listing
|
|
77
|
-
condition listing.owner_id == user.id
|
|
78
|
-
end
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
Your application then only needs to ask:
|
|
82
|
-
|
|
83
|
-
```javascript
|
|
84
|
-
authz.can(user, "edit", "listing", listing);
|
|
85
|
-
```
|
|
86
|
-
|
|
87
61
|
---
|
|
88
62
|
|
|
89
63
|
# Why Simple Authz
|
|
90
64
|
|
|
91
65
|
Benefits of centralized authorization:
|
|
92
66
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
67
|
+
* Clear separation between **business logic and security rules**
|
|
68
|
+
* Easy permission updates without touching application code
|
|
69
|
+
* Better maintainability for large projects
|
|
70
|
+
* Safer and more predictable access control
|
|
71
|
+
* Easier onboarding for new developers
|
|
98
72
|
|
|
99
73
|
---
|
|
100
74
|
|
|
101
75
|
# Key Features
|
|
102
76
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
77
|
+
* Simple and readable **policy language (TOON)**
|
|
78
|
+
* Fast permission lookup (compiled rules)
|
|
79
|
+
* Built-in **caching for repeated checks**
|
|
80
|
+
* Support for **multiple user roles**
|
|
81
|
+
* Logical **conditional rules (AND / OR)**
|
|
82
|
+
* Policy **validation**
|
|
83
|
+
* Policy **AST compilation (no eval)**
|
|
84
|
+
* Express middleware support
|
|
85
|
+
* Debugging via `authz.explain()`
|
|
86
|
+
* TypeScript support
|
|
87
|
+
* Lightweight with minimal dependencies
|
|
111
88
|
|
|
112
89
|
---
|
|
113
90
|
|
|
114
91
|
# Installation
|
|
115
92
|
|
|
116
|
-
Install using npm:
|
|
117
|
-
|
|
118
93
|
```bash
|
|
119
94
|
npm install simple-authz
|
|
120
95
|
```
|
|
@@ -126,36 +101,24 @@ npm install simple-authz
|
|
|
126
101
|
## 1. Import the library
|
|
127
102
|
|
|
128
103
|
```javascript
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
const authz = new Authz();
|
|
104
|
+
const authz = require("simple-authz");
|
|
132
105
|
```
|
|
133
106
|
|
|
134
107
|
---
|
|
135
108
|
|
|
136
109
|
## 2. Create a policy file
|
|
137
110
|
|
|
138
|
-
Create a file called:
|
|
139
|
-
|
|
140
111
|
```
|
|
141
112
|
authz.toon
|
|
142
113
|
```
|
|
143
114
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
```
|
|
115
|
+
```toon
|
|
147
116
|
rule
|
|
148
117
|
role admin
|
|
149
118
|
action *
|
|
150
119
|
resource *
|
|
151
120
|
end
|
|
152
121
|
|
|
153
|
-
rule
|
|
154
|
-
role broker
|
|
155
|
-
action publish
|
|
156
|
-
resource listing
|
|
157
|
-
end
|
|
158
|
-
|
|
159
122
|
rule
|
|
160
123
|
role user
|
|
161
124
|
action edit
|
|
@@ -177,34 +140,12 @@ authz.load("./authz.toon");
|
|
|
177
140
|
## 4. Check permissions
|
|
178
141
|
|
|
179
142
|
```javascript
|
|
180
|
-
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
Example usage:
|
|
184
|
-
|
|
185
|
-
```javascript
|
|
186
|
-
if (authz.can(user, "edit", "listing", listing)) {
|
|
187
|
-
updateListing();
|
|
188
|
-
} else {
|
|
189
|
-
throw new Error("Access denied");
|
|
190
|
-
}
|
|
143
|
+
authz.can(user, "edit", "listing", listing);
|
|
191
144
|
```
|
|
192
145
|
|
|
193
146
|
---
|
|
194
147
|
|
|
195
|
-
## 5. `authz.explain()`
|
|
196
|
-
|
|
197
|
-
The `authz.explain()` method helps debug authorization decisions by returning detailed information about **why access was allowed or denied**.
|
|
198
|
-
|
|
199
|
-
Unlike `authz.can()`, which returns only `true` or `false`, this method returns an object explaining the result.
|
|
200
|
-
|
|
201
|
-
### Usage
|
|
202
|
-
|
|
203
|
-
```javascript
|
|
204
|
-
authz.explain(user, "edit", "listing", listing);
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
### Example
|
|
148
|
+
## 5. Debug with `authz.explain()`
|
|
208
149
|
|
|
209
150
|
```javascript
|
|
210
151
|
const result = authz.explain(user, "edit", "listing", listing);
|
|
@@ -212,7 +153,7 @@ const result = authz.explain(user, "edit", "listing", listing);
|
|
|
212
153
|
console.log(result);
|
|
213
154
|
```
|
|
214
155
|
|
|
215
|
-
|
|
156
|
+
Example output:
|
|
216
157
|
|
|
217
158
|
```javascript
|
|
218
159
|
{
|
|
@@ -224,207 +165,125 @@ console.log(result);
|
|
|
224
165
|
}
|
|
225
166
|
```
|
|
226
167
|
|
|
227
|
-
If no rule matches:
|
|
228
|
-
|
|
229
|
-
```javascript
|
|
230
|
-
{
|
|
231
|
-
allowed: false,
|
|
232
|
-
reason: "no matching rule"
|
|
233
|
-
}
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
Use `authz.explain()` for **debugging and development**, and `authz.can()` for **regular authorization checks**.
|
|
237
|
-
|
|
238
|
-
---
|
|
239
|
-
|
|
240
|
-
# Core Concepts
|
|
241
|
-
|
|
242
|
-
Simple Authz follows a common authorization model:
|
|
243
|
-
|
|
244
|
-
```
|
|
245
|
-
Subject → Action → Resource
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
| Component | Description |
|
|
249
|
-
| --------- | ------------------------------ |
|
|
250
|
-
| Subject | The user performing the action |
|
|
251
|
-
| Action | What the user wants to do |
|
|
252
|
-
| Resource | The object being accessed |
|
|
253
|
-
|
|
254
|
-
Example:
|
|
255
|
-
|
|
256
|
-
```
|
|
257
|
-
broker → edit → listing
|
|
258
|
-
```
|
|
259
|
-
|
|
260
168
|
---
|
|
261
169
|
|
|
262
|
-
#
|
|
263
|
-
|
|
264
|
-
Policies are written in **TOON format**.
|
|
170
|
+
# Middleware (Express)
|
|
265
171
|
|
|
266
|
-
|
|
172
|
+
Protect routes easily:
|
|
267
173
|
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
action <Action>
|
|
272
|
-
resource <Resource>
|
|
273
|
-
end
|
|
274
|
-
```
|
|
174
|
+
```javascript
|
|
175
|
+
const express = require("express");
|
|
176
|
+
const authz = require("simple-authz");
|
|
275
177
|
|
|
276
|
-
|
|
178
|
+
const app = express();
|
|
179
|
+
app.use(express.json());
|
|
277
180
|
|
|
278
|
-
|
|
279
|
-
rule
|
|
280
|
-
role admin
|
|
281
|
-
action *
|
|
282
|
-
resource *
|
|
283
|
-
end
|
|
284
|
-
|
|
285
|
-
rule
|
|
286
|
-
role broker
|
|
287
|
-
action publish
|
|
288
|
-
resource listing
|
|
289
|
-
end
|
|
181
|
+
authz.load("./authz.toon");
|
|
290
182
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
183
|
+
app.post(
|
|
184
|
+
"/listing",
|
|
185
|
+
authz.middleware("edit", "listing"),
|
|
186
|
+
(req, res) => {
|
|
187
|
+
res.send("Allowed");
|
|
188
|
+
}
|
|
189
|
+
);
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
> ⚠️ **Security note:** by default the middleware checks against `req.body`.
|
|
193
|
+
> That's fine for actions where the body *is* the thing being created, but
|
|
194
|
+
> if your policy has conditions like `listing.owner_id == user.id`, a client
|
|
195
|
+
> can put whatever `owner_id` it wants in its own request body and bypass
|
|
196
|
+
> the check. Pass a third argument — an (optionally async) function that
|
|
197
|
+
> loads the **real** resource from your database — whenever a condition
|
|
198
|
+
> depends on data the client shouldn't be trusted to supply:
|
|
199
|
+
>
|
|
200
|
+
> ```javascript
|
|
201
|
+
> app.post(
|
|
202
|
+
> "/listing/:id",
|
|
203
|
+
> authz.middleware("edit", "listing", async (req) => {
|
|
204
|
+
> return await db.listings.findById(req.params.id);
|
|
205
|
+
> }),
|
|
206
|
+
> (req, res) => {
|
|
207
|
+
> res.send("Allowed");
|
|
208
|
+
> }
|
|
209
|
+
> );
|
|
210
|
+
> ```
|
|
298
211
|
|
|
299
212
|
---
|
|
300
213
|
|
|
301
|
-
#
|
|
302
|
-
|
|
303
|
-
You can use `*` to allow everything.
|
|
304
|
-
|
|
305
|
-
Example:
|
|
214
|
+
# Core Concepts
|
|
306
215
|
|
|
307
216
|
```
|
|
308
|
-
|
|
309
|
-
role admin
|
|
310
|
-
action *
|
|
311
|
-
resource *
|
|
312
|
-
end
|
|
217
|
+
Subject → Action → Resource
|
|
313
218
|
```
|
|
314
219
|
|
|
315
|
-
|
|
220
|
+
| Component | Description |
|
|
221
|
+
| --------- | ------------- |
|
|
222
|
+
| Subject | User |
|
|
223
|
+
| Action | Operation |
|
|
224
|
+
| Resource | Target object |
|
|
316
225
|
|
|
317
226
|
---
|
|
318
227
|
|
|
319
|
-
#
|
|
320
|
-
|
|
321
|
-
Policies can include conditions.
|
|
228
|
+
# Policy Language (TOON)
|
|
322
229
|
|
|
323
|
-
|
|
230
|
+
Basic syntax:
|
|
324
231
|
|
|
325
232
|
```
|
|
326
233
|
rule
|
|
327
234
|
role <role>
|
|
328
235
|
action <action>
|
|
329
236
|
resource <resource>
|
|
330
|
-
condition <condition1> OR <condition2>
|
|
331
|
-
end
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
Example:
|
|
335
|
-
|
|
336
|
-
```
|
|
337
|
-
rule
|
|
338
|
-
role user
|
|
339
|
-
action view
|
|
340
|
-
resource listing
|
|
341
|
-
condition listing.status == "public" OR listing.owner_id == user.id
|
|
342
237
|
end
|
|
343
238
|
```
|
|
344
239
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
240
|
+
The parser is strict on purpose: a stray `end`, a `rule` opened twice without
|
|
241
|
+
closing it, an unrecognized key (e.g. a typo'd `roel`), or a key with no
|
|
242
|
+
value all throw a clear error with the line number, instead of silently
|
|
243
|
+
dropping or mis-parsing part of your policy. `authz.load()` runs full
|
|
244
|
+
validation too — every condition is actually compiled during validation, so
|
|
245
|
+
a broken condition (missing operand, mixed `AND`/`OR`, etc.) is reported
|
|
246
|
+
against its rule number at load time rather than failing later, mid-request,
|
|
247
|
+
somewhere inside the evaluator.
|
|
348
248
|
|
|
349
249
|
---
|
|
350
250
|
|
|
351
|
-
#
|
|
352
|
-
|
|
353
|
-
Conditions can reference:
|
|
354
|
-
|
|
355
|
-
| Variable | Description |
|
|
356
|
-
| -------- | --------------------- |
|
|
357
|
-
| user | authenticated user |
|
|
358
|
-
| resource | target resource |
|
|
359
|
-
| context | optional request data |
|
|
360
|
-
|
|
361
|
-
Example:
|
|
251
|
+
# Wildcards
|
|
362
252
|
|
|
363
253
|
```
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
action edit
|
|
367
|
-
resource profile
|
|
368
|
-
condition user.id == resource.id
|
|
369
|
-
end
|
|
254
|
+
action *
|
|
255
|
+
resource *
|
|
370
256
|
```
|
|
371
257
|
|
|
372
258
|
---
|
|
373
259
|
|
|
374
|
-
#
|
|
260
|
+
# Conditional Rules
|
|
375
261
|
|
|
376
|
-
|
|
262
|
+
Example:
|
|
377
263
|
|
|
378
264
|
```
|
|
379
|
-
|
|
380
|
-
role admin
|
|
381
|
-
action *
|
|
382
|
-
resource *
|
|
383
|
-
end
|
|
384
|
-
|
|
385
|
-
rule
|
|
386
|
-
role broker
|
|
387
|
-
action publish
|
|
388
|
-
resource listing
|
|
389
|
-
end
|
|
390
|
-
|
|
391
|
-
rule
|
|
392
|
-
role user
|
|
393
|
-
action edit
|
|
394
|
-
resource listing
|
|
395
|
-
end
|
|
265
|
+
condition listing.owner_id == user.id AND listing.status != "published"
|
|
396
266
|
```
|
|
397
267
|
|
|
398
|
-
|
|
268
|
+
Chains of any length work: `a == 1 AND b == 2 AND c == 3`. You can also
|
|
269
|
+
compare against numbers and booleans directly (`listing.views > 100`), not
|
|
270
|
+
just strings.
|
|
399
271
|
|
|
400
|
-
|
|
272
|
+
Only one operator type per condition — `AND` and `OR` can't be mixed in the
|
|
273
|
+
same condition (`a == 1 AND b == 2 OR c == 3` throws an error). Split that
|
|
274
|
+
into two separate rules instead.
|
|
401
275
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
action edit
|
|
406
|
-
resource listing
|
|
407
|
-
condition listing.owner_id == user.id
|
|
408
|
-
end
|
|
409
|
-
```
|
|
276
|
+
String literals are quote-safe: a value like
|
|
277
|
+
`doc.title == "Terms AND Conditions"` is treated as one literal, not
|
|
278
|
+
accidentally split on the "AND" inside it.
|
|
410
279
|
|
|
411
280
|
---
|
|
412
281
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
```
|
|
416
|
-
rule
|
|
417
|
-
role admin
|
|
418
|
-
action *
|
|
419
|
-
resource *
|
|
420
|
-
end
|
|
282
|
+
# Policy Examples
|
|
421
283
|
|
|
422
|
-
|
|
423
|
-
role broker
|
|
424
|
-
action publish
|
|
425
|
-
resource listing
|
|
426
|
-
end
|
|
284
|
+
### Ownership Rule
|
|
427
285
|
|
|
286
|
+
```
|
|
428
287
|
rule
|
|
429
288
|
role user
|
|
430
289
|
action edit
|
|
@@ -437,33 +296,23 @@ end
|
|
|
437
296
|
|
|
438
297
|
# Working with Roles
|
|
439
298
|
|
|
440
|
-
Users can have one or multiple roles.
|
|
441
|
-
|
|
442
|
-
Example user:
|
|
443
|
-
|
|
444
299
|
```javascript
|
|
445
|
-
|
|
446
|
-
id: 10,
|
|
447
|
-
roles: ["broker"],
|
|
448
|
-
};
|
|
300
|
+
user.role = "admin";
|
|
449
301
|
```
|
|
450
302
|
|
|
451
|
-
|
|
303
|
+
or
|
|
452
304
|
|
|
453
305
|
```javascript
|
|
454
|
-
|
|
455
|
-
id: 5,
|
|
456
|
-
roles: ["user", "editor"],
|
|
457
|
-
};
|
|
306
|
+
user.roles = ["user", "editor"];
|
|
458
307
|
```
|
|
459
308
|
|
|
460
|
-
|
|
309
|
+
Access is granted if **any role matches**.
|
|
461
310
|
|
|
462
311
|
---
|
|
463
312
|
|
|
464
313
|
# Authorization API
|
|
465
314
|
|
|
466
|
-
## Load policy
|
|
315
|
+
## Load policy
|
|
467
316
|
|
|
468
317
|
```javascript
|
|
469
318
|
authz.load("./authz.toon");
|
|
@@ -474,91 +323,37 @@ authz.load("./authz.toon");
|
|
|
474
323
|
## Check permission
|
|
475
324
|
|
|
476
325
|
```javascript
|
|
477
|
-
authz.can(user, action, resource,
|
|
478
|
-
```
|
|
479
|
-
|
|
480
|
-
Example:
|
|
481
|
-
|
|
482
|
-
```javascript
|
|
483
|
-
authz.can(user, "edit", "listing", listing);
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
Returns:
|
|
487
|
-
|
|
488
|
-
```
|
|
489
|
-
true
|
|
490
|
-
false
|
|
326
|
+
authz.can(user, action, resource, data);
|
|
491
327
|
```
|
|
492
328
|
|
|
493
329
|
---
|
|
494
330
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
## Example: Listing System
|
|
498
|
-
|
|
499
|
-
User:
|
|
500
|
-
|
|
501
|
-
```javascript
|
|
502
|
-
const user = {
|
|
503
|
-
id: 10,
|
|
504
|
-
roles: ["broker"],
|
|
505
|
-
};
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
Listing:
|
|
331
|
+
## Explain decision
|
|
509
332
|
|
|
510
333
|
```javascript
|
|
511
|
-
|
|
512
|
-
owner_id: 10,
|
|
513
|
-
};
|
|
514
|
-
```
|
|
515
|
-
|
|
516
|
-
Permission check:
|
|
517
|
-
|
|
518
|
-
```javascript
|
|
519
|
-
authz.can(user, "edit", "listing", listing);
|
|
520
|
-
```
|
|
521
|
-
|
|
522
|
-
Result:
|
|
523
|
-
|
|
524
|
-
```
|
|
525
|
-
true
|
|
334
|
+
authz.explain(user, action, resource, data);
|
|
526
335
|
```
|
|
527
336
|
|
|
528
337
|
---
|
|
529
338
|
|
|
530
|
-
# Example
|
|
531
|
-
|
|
532
|
-
Example route protection:
|
|
339
|
+
# Example Integration
|
|
533
340
|
|
|
534
341
|
```javascript
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
if (!allowed) {
|
|
539
|
-
return res.status(403).send("Access denied");
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
updateListing();
|
|
543
|
-
});
|
|
342
|
+
if (!authz.can(req.user, "edit", "listing", req.listing)) {
|
|
343
|
+
return res.status(403).send("Access denied");
|
|
344
|
+
}
|
|
544
345
|
```
|
|
545
346
|
|
|
546
347
|
---
|
|
547
348
|
|
|
548
349
|
# Project Structure Example
|
|
549
350
|
|
|
550
|
-
Recommended project layout:
|
|
551
|
-
|
|
552
351
|
```
|
|
553
|
-
project
|
|
554
|
-
|
|
555
|
-
├── policies
|
|
352
|
+
project/
|
|
353
|
+
├── policies/
|
|
556
354
|
│ └── authz.toon
|
|
557
|
-
|
|
558
|
-
├── src
|
|
559
|
-
│
|
|
355
|
+
├── src/
|
|
560
356
|
├── app.js
|
|
561
|
-
│
|
|
562
357
|
└── package.json
|
|
563
358
|
```
|
|
564
359
|
|
|
@@ -566,66 +361,75 @@ project
|
|
|
566
361
|
|
|
567
362
|
# Performance
|
|
568
363
|
|
|
569
|
-
|
|
364
|
+
* Rules compiled into indexed structure
|
|
365
|
+
* AST-based condition evaluation
|
|
366
|
+
* O(1) permission lookup
|
|
367
|
+
* Built-in LRU cache with TTL-based expiration, keyed by user + action +
|
|
368
|
+
resource + object id (falls back to a data snapshot if there's no id)
|
|
570
369
|
|
|
571
|
-
|
|
370
|
+
**Caching caveat:** because the cache keys on object *identity* (id/owner_id),
|
|
371
|
+
if a resource's *other* fields change while its id stays the same (e.g. a
|
|
372
|
+
document gets locked) within the TTL window, a stale decision can be served.
|
|
373
|
+
Call `authz.clearCache()` right after any write that could flip a condition's
|
|
374
|
+
outcome, or lower `cacheTTL` / set `cacheEnabled: false` for policies with
|
|
375
|
+
conditions on fast-changing fields:
|
|
572
376
|
|
|
573
|
-
|
|
377
|
+
```javascript
|
|
378
|
+
const authz = new Authz({ cacheEnabled: true, cacheSize: 1000, cacheTTL: 60 });
|
|
379
|
+
```
|
|
574
380
|
|
|
575
381
|
---
|
|
576
382
|
|
|
577
|
-
#
|
|
383
|
+
# CLI
|
|
578
384
|
|
|
579
|
-
|
|
385
|
+
```bash
|
|
386
|
+
npx simple-authz validate ./policies/authz.toon
|
|
387
|
+
npx simple-authz check ./policies/authz.toon --user '{"id":1,"roles":["user"]}' --action edit --resource listing --context '{"owner_id":1}'
|
|
388
|
+
npx simple-authz benchmark ./policies/authz.toon --iterations 10000
|
|
389
|
+
```
|
|
580
390
|
|
|
581
|
-
|
|
391
|
+
* `validate` — parses and validates a policy file, lists roles found
|
|
392
|
+
* `check` — runs `authz.explain()` against a policy and prints the decision
|
|
393
|
+
* `benchmark` — runs repeated checks and prints throughput + cache stats
|
|
582
394
|
|
|
583
|
-
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
# Security Model
|
|
398
|
+
|
|
399
|
+
**Deny by default**
|
|
584
400
|
|
|
585
401
|
```
|
|
586
402
|
no rule → deny
|
|
587
403
|
rule exists → allow
|
|
588
404
|
```
|
|
589
405
|
|
|
590
|
-
This reduces the risk of unintended access.
|
|
591
|
-
|
|
592
406
|
---
|
|
593
407
|
|
|
594
408
|
# Roadmap
|
|
595
409
|
|
|
596
|
-
|
|
410
|
+
### v1.2
|
|
597
411
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
- Policy debugger
|
|
601
|
-
- Middleware helpers
|
|
602
|
-
- Permission caching
|
|
603
|
-
- Modular policy files
|
|
604
|
-
- Distributed policy storage
|
|
412
|
+
* Role hierarchy
|
|
413
|
+
* Modular policies
|
|
605
414
|
|
|
606
|
-
|
|
415
|
+
### v2.0
|
|
607
416
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
Steps:
|
|
613
|
-
|
|
614
|
-
1. Fork the repository
|
|
615
|
-
2. Create a feature branch
|
|
616
|
-
3. Commit changes
|
|
617
|
-
4. Open a pull request
|
|
417
|
+
* Advanced ABAC
|
|
418
|
+
* Distributed policies
|
|
419
|
+
* Policy versioning
|
|
618
420
|
|
|
619
421
|
---
|
|
620
422
|
|
|
621
|
-
#
|
|
423
|
+
# Contributing
|
|
622
424
|
|
|
623
|
-
|
|
425
|
+
1. Fork repository
|
|
426
|
+
2. Create branch
|
|
427
|
+
3. Submit PR
|
|
624
428
|
|
|
625
429
|
---
|
|
626
430
|
|
|
627
|
-
#
|
|
431
|
+
# License
|
|
628
432
|
|
|
629
|
-
|
|
433
|
+
Apache 2.0
|
|
630
434
|
|
|
631
|
-
|
|
435
|
+
---
|