simple-authz 1.0.3 → 1.0.5

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 CHANGED
@@ -8,26 +8,36 @@ The goal of this project is to make authorization **simple, fast, readable, and
8
8
 
9
9
  ---
10
10
 
11
+ ## ⚠️ Development Status
12
+
13
+ **simple-authz** is currently under active development.
14
+
15
+ Some features may change, and there may be inconsistencies between the documentation and the package behavior.
16
+
17
+ We recommend testing thoroughly before using it in production.
18
+
19
+ ---
20
+
11
21
  # Table of Contents
12
22
 
13
- * Overview
14
- * Why Simple Authz
15
- * Key Features
16
- * Installation
17
- * Quick Start
18
- * Core Concepts
19
- * Policy Language (TOON)
20
- * Policy Examples
21
- * Using Conditions
22
- * Working with Roles
23
- * Authorization API
24
- * Example Integrations
25
- * Project Structure
26
- * Performance
27
- * Security Model
28
- * Roadmap
29
- * Contributing
30
- * License
23
+ - Overview
24
+ - Why Simple Authz
25
+ - Key Features
26
+ - Installation
27
+ - Quick Start
28
+ - Core Concepts
29
+ - Policy Language (TOON)
30
+ - Policy Examples
31
+ - Using Conditions
32
+ - Working with Roles
33
+ - Authorization API
34
+ - Example Integrations
35
+ - Project Structure
36
+ - Performance
37
+ - Security Model
38
+ - Roadmap
39
+ - Contributing
40
+ - License
31
41
 
32
42
  ---
33
43
 
@@ -48,20 +58,30 @@ Over time this logic spreads across many files and becomes difficult to maintain
48
58
  Example policy file:
49
59
 
50
60
  ```
51
- allow admin *
61
+ rule
62
+ role admin
63
+ action *
64
+ resource *
65
+ end
52
66
 
53
- allow user view listing
67
+ rule
68
+ role broker
69
+ action publish
70
+ resource listing
71
+ end
54
72
 
55
- allow broker publish listing
56
-
57
- allow broker edit listing
58
- condition listing.owner_id == user.id
73
+ rule
74
+ role user
75
+ action edit
76
+ resource listing
77
+ condition listing.owner_id == user.id
78
+ end
59
79
  ```
60
80
 
61
81
  Your application then only needs to ask:
62
82
 
63
83
  ```javascript
64
- authz.can(user, "edit", "listing", listing)
84
+ authz.can(user, "edit", "listing", listing);
65
85
  ```
66
86
 
67
87
  ---
@@ -70,24 +90,24 @@ authz.can(user, "edit", "listing", listing)
70
90
 
71
91
  Benefits of centralized authorization:
72
92
 
73
- * Clear separation between **business logic and security rules**
74
- * Easy permission updates without touching application code
75
- * Better maintainability for large projects
76
- * Safer and more predictable access control
77
- * Easier onboarding for new developers
93
+ - Clear separation between **business logic and security rules**
94
+ - Easy permission updates without touching application code
95
+ - Better maintainability for large projects
96
+ - Safer and more predictable access control
97
+ - Easier onboarding for new developers
78
98
 
79
99
  ---
80
100
 
81
101
  # Key Features
82
102
 
83
- * Simple and readable **policy language**
84
- * Fast permission lookup
85
- * Support for **multiple user roles**
86
- * Logical **conditional rules**
87
- * Policy **validation**
88
- * Policy **AST compilation**
89
- * Lightweight with minimal dependencies
90
- * Works with any Node.js framework
103
+ - Simple and readable **policy language**
104
+ - Fast permission lookup
105
+ - Support for **multiple user roles**
106
+ - Logical **conditional rules**
107
+ - Policy **validation**
108
+ - Policy **AST compilation**
109
+ - Lightweight with minimal dependencies
110
+ - Works with any Node.js framework
91
111
 
92
112
  ---
93
113
 
@@ -106,9 +126,9 @@ npm install simple-authz
106
126
  ## 1. Import the library
107
127
 
108
128
  ```javascript
109
- const Authz = require("simple-authz")
129
+ const Authz = require("simple-authz");
110
130
 
111
- const authz = new Authz()
131
+ const authz = new Authz();
112
132
  ```
113
133
 
114
134
  ---
@@ -124,14 +144,24 @@ authz.toon
124
144
  Example policy:
125
145
 
126
146
  ```
127
- allow admin *
128
-
129
- allow user view listing
147
+ rule
148
+ role admin
149
+ action *
150
+ resource *
151
+ end
130
152
 
131
- allow broker publish listing
153
+ rule
154
+ role broker
155
+ action publish
156
+ resource listing
157
+ end
132
158
 
133
- allow broker edit listing
134
- condition listing.owner_id == user.id
159
+ rule
160
+ role user
161
+ action edit
162
+ resource listing
163
+ condition listing.owner_id == user.id
164
+ end
135
165
  ```
136
166
 
137
167
  ---
@@ -139,7 +169,7 @@ condition listing.owner_id == user.id
139
169
  ## 3. Load the policy
140
170
 
141
171
  ```javascript
142
- authz.load("./authz.toon")
172
+ authz.load("./authz.toon");
143
173
  ```
144
174
 
145
175
  ---
@@ -147,21 +177,66 @@ authz.load("./authz.toon")
147
177
  ## 4. Check permissions
148
178
 
149
179
  ```javascript
150
- const allowed = authz.can(user, "edit", "listing", listing)
180
+ const allowed = authz.can(user, "edit", "listing", listing);
151
181
  ```
152
182
 
153
183
  Example usage:
154
184
 
155
185
  ```javascript
156
- if(authz.can(user,"edit","listing",listing)){
157
- updateListing()
158
- }else{
159
- throw new Error("Access denied")
186
+ if (authz.can(user, "edit", "listing", listing)) {
187
+ updateListing();
188
+ } else {
189
+ throw new Error("Access denied");
160
190
  }
161
191
  ```
162
192
 
163
193
  ---
164
194
 
195
+ ## 5. `authz.explain()` (Optional)
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
208
+
209
+ ```javascript
210
+ const result = authz.explain(user, "edit", "listing", listing);
211
+
212
+ console.log(result);
213
+ ```
214
+
215
+ ### Example Output
216
+
217
+ ```javascript
218
+ {
219
+ allowed: true,
220
+ role: "user",
221
+ resource: "listing",
222
+ action: "edit",
223
+ reason: "condition passed"
224
+ }
225
+ ```
226
+
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
+
165
240
  # Core Concepts
166
241
 
167
242
  Simple Authz follows a common authorization model:
@@ -191,15 +266,34 @@ Policies are written in **TOON format**.
191
266
  Basic rule syntax:
192
267
 
193
268
  ```
194
- allow <role> <action> <resource>
269
+ rule
270
+ role <Role>
271
+ action <Action>
272
+ resource <Resource>
273
+ end
195
274
  ```
196
275
 
197
276
  Example:
198
277
 
199
278
  ```
200
- allow admin *
201
- allow user view listing
202
- allow broker publish listing
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
290
+
291
+ rule
292
+ role user
293
+ action edit
294
+ resource listing
295
+ condition listing.owner_id == user.id
296
+ end
203
297
  ```
204
298
 
205
299
  ---
@@ -211,7 +305,11 @@ You can use `*` to allow everything.
211
305
  Example:
212
306
 
213
307
  ```
214
- allow admin *
308
+ rule
309
+ role admin
310
+ action *
311
+ resource *
312
+ end
215
313
  ```
216
314
 
217
315
  This allows the admin role to perform **any action on any resource**.
@@ -225,15 +323,23 @@ Policies can include conditions.
225
323
  Syntax:
226
324
 
227
325
  ```
228
- allow <role> <action> <resource>
229
- condition <condition>
326
+ rule
327
+ role <role>
328
+ action <action>
329
+ resource <resource>
330
+ condition <condition1> OR <condition2>
331
+ end
230
332
  ```
231
333
 
232
334
  Example:
233
335
 
234
336
  ```
235
- allow broker edit listing
236
- condition listing.owner_id == user.id
337
+ rule
338
+ role user
339
+ action view
340
+ resource listing
341
+ condition listing.status == "public" OR listing.owner_id == user.id
342
+ end
237
343
  ```
238
344
 
239
345
  Meaning:
@@ -255,8 +361,12 @@ Conditions can reference:
255
361
  Example:
256
362
 
257
363
  ```
258
- allow user edit profile
259
- condition user.id == resource.id
364
+ rule
365
+ role user
366
+ action edit
367
+ resource profile
368
+ condition user.id == resource.id
369
+ end
260
370
  ```
261
371
 
262
372
  ---
@@ -266,11 +376,23 @@ condition user.id == resource.id
266
376
  ## Example 1 – Basic Roles
267
377
 
268
378
  ```
269
- allow admin *
379
+ rule
380
+ role admin
381
+ action *
382
+ resource *
383
+ end
270
384
 
271
- allow user view listing
385
+ rule
386
+ role broker
387
+ action publish
388
+ resource listing
389
+ end
272
390
 
273
- allow broker publish listing
391
+ rule
392
+ role user
393
+ action edit
394
+ resource listing
395
+ end
274
396
  ```
275
397
 
276
398
  ---
@@ -278,8 +400,12 @@ allow broker publish listing
278
400
  ## Example 2 – Ownership Rules
279
401
 
280
402
  ```
281
- allow broker edit listing
282
- condition listing.owner_id == user.id
403
+ rule
404
+ role broker
405
+ action edit
406
+ resource listing
407
+ condition listing.owner_id == user.id
408
+ end
283
409
  ```
284
410
 
285
411
  ---
@@ -287,14 +413,24 @@ condition listing.owner_id == user.id
287
413
  ## Example 3 – Multiple Rules
288
414
 
289
415
  ```
290
- allow admin *
291
-
292
- allow user view listing
416
+ rule
417
+ role admin
418
+ action *
419
+ resource *
420
+ end
293
421
 
294
- allow broker publish listing
422
+ rule
423
+ role broker
424
+ action publish
425
+ resource listing
426
+ end
295
427
 
296
- allow broker edit listing
297
- condition listing.owner_id == user.id
428
+ rule
429
+ role user
430
+ action edit
431
+ resource listing
432
+ condition listing.owner_id == user.id
433
+ end
298
434
  ```
299
435
 
300
436
  ---
@@ -307,18 +443,18 @@ Example user:
307
443
 
308
444
  ```javascript
309
445
  const user = {
310
- id: 10,
311
- roles: ["broker"]
312
- }
446
+ id: 10,
447
+ roles: ["broker"],
448
+ };
313
449
  ```
314
450
 
315
451
  Multiple roles:
316
452
 
317
453
  ```javascript
318
454
  const user = {
319
- id: 5,
320
- roles: ["user","editor"]
321
- }
455
+ id: 5,
456
+ roles: ["user", "editor"],
457
+ };
322
458
  ```
323
459
 
324
460
  The engine checks permissions against **all roles**.
@@ -330,7 +466,7 @@ The engine checks permissions against **all roles**.
330
466
  ## Load policy file
331
467
 
332
468
  ```javascript
333
- authz.load("./authz.toon")
469
+ authz.load("./authz.toon");
334
470
  ```
335
471
 
336
472
  ---
@@ -338,13 +474,13 @@ authz.load("./authz.toon")
338
474
  ## Check permission
339
475
 
340
476
  ```javascript
341
- authz.can(user, action, resource, object)
477
+ authz.can(user, action, resource, object);
342
478
  ```
343
479
 
344
480
  Example:
345
481
 
346
482
  ```javascript
347
- authz.can(user,"edit","listing",listing)
483
+ authz.can(user, "edit", "listing", listing);
348
484
  ```
349
485
 
350
486
  Returns:
@@ -365,22 +501,22 @@ User:
365
501
  ```javascript
366
502
  const user = {
367
503
  id: 10,
368
- roles: ["broker"]
369
- }
504
+ roles: ["broker"],
505
+ };
370
506
  ```
371
507
 
372
508
  Listing:
373
509
 
374
510
  ```javascript
375
511
  const listing = {
376
- owner_id: 10
377
- }
512
+ owner_id: 10,
513
+ };
378
514
  ```
379
515
 
380
516
  Permission check:
381
517
 
382
518
  ```javascript
383
- authz.can(user,"edit","listing",listing)
519
+ authz.can(user, "edit", "listing", listing);
384
520
  ```
385
521
 
386
522
  Result:
@@ -396,21 +532,15 @@ true
396
532
  Example route protection:
397
533
 
398
534
  ```javascript
399
- app.put("/listing/:id", async (req,res)=>{
535
+ app.put("/listing/:id", async (req, res) => {
536
+ const allowed = authz.can(req.user, "edit", "listing", req.listing);
400
537
 
401
- const allowed = authz.can(
402
- req.user,
403
- "edit",
404
- "listing",
405
- req.listing
406
- )
538
+ if (!allowed) {
539
+ return res.status(403).send("Access denied");
540
+ }
407
541
 
408
- if(!allowed){
409
- return res.status(403).send("Access denied")
410
- }
411
-
412
- updateListing()
413
- })
542
+ updateListing();
543
+ });
414
544
  ```
415
545
 
416
546
  ---
@@ -465,13 +595,13 @@ This reduces the risk of unintended access.
465
595
 
466
596
  Future versions may include:
467
597
 
468
- * Policy hot reloading
469
- * Role inheritance
470
- * Policy debugger
471
- * Middleware helpers
472
- * Permission caching
473
- * Modular policy files
474
- * Distributed policy storage
598
+ - Policy hot reloading
599
+ - Role inheritance
600
+ - Policy debugger
601
+ - Middleware helpers
602
+ - Permission caching
603
+ - Modular policy files
604
+ - Distributed policy storage
475
605
 
476
606
  ---
477
607
 
@@ -498,5 +628,4 @@ This project is licensed under the [Apache 2.0 license](LICENSE).
498
628
 
499
629
  Simple Authz was created to simplify authorization logic in modern Node.js applications.
500
630
 
501
-
502
- # Keywords
631
+ # Keywords
package/lib/core/authz.js CHANGED
@@ -76,6 +76,95 @@ class Authz {
76
76
 
77
77
  return false
78
78
  }
79
+
80
+ explain(user, action, resourceType, data = {}) {
81
+
82
+ const roles = normalizeUser(user)
83
+
84
+ for (const role of roles) {
85
+
86
+ const rolePerm = this.permissions[role]
87
+
88
+ // ---------- Permission check ----------
89
+ if (rolePerm) {
90
+
91
+ const resourcePerm = rolePerm[resourceType]
92
+
93
+ if (resourcePerm) {
94
+
95
+ if (resourcePerm.has("*") || resourcePerm.has(action)) {
96
+ return {
97
+ allowed: true,
98
+ role,
99
+ resource: resourceType,
100
+ action,
101
+ reason: "direct permission match"
102
+ }
103
+ }
104
+ }
105
+
106
+ if (rolePerm["*"]) {
107
+ if (rolePerm["*"].has("*") || rolePerm["*"].has(action)) {
108
+ return {
109
+ allowed: true,
110
+ role,
111
+ resource: "*",
112
+ action,
113
+ reason: "wildcard permission"
114
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ // ---------- Condition check ----------
120
+ const roleCond = this.conditions[role]
121
+
122
+ if (!roleCond) continue
123
+
124
+ const resourceCond = roleCond[resourceType]
125
+
126
+ if (!resourceCond) continue
127
+
128
+ const actionCond = resourceCond[action]
129
+
130
+ if (!actionCond) continue
131
+
132
+ for (const cond of actionCond) {
133
+
134
+ const context = {
135
+ user,
136
+ [resourceType]: data
137
+ }
138
+
139
+ const result = evaluateAST(cond, context)
140
+
141
+ if (result) {
142
+ return {
143
+ allowed: true,
144
+ role,
145
+ resource: resourceType,
146
+ action,
147
+ reason: "condition passed",
148
+ condition: cond
149
+ }
150
+ }
151
+ }
152
+
153
+ return {
154
+ allowed: false,
155
+ role,
156
+ resource: resourceType,
157
+ action,
158
+ reason: "condition failed"
159
+ }
160
+
161
+ }
162
+
163
+ return {
164
+ allowed: false,
165
+ reason: "no matching rule"
166
+ }
167
+ }
79
168
  }
80
169
 
81
170
  module.exports = Authz
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "simple-authz",
3
- "version": "1.0.3",
4
- "description": "Simple authorization engine with TOON policy language",
5
- "main": "lib/core/authz.js",
3
+ "version": "1.0.5",
4
+ "description": "Lightweight policy-based authorization engine for Node.js using TOON policy language",
5
+ "main": "lib/index.js",
6
6
  "scripts": {
7
7
  "test": "node bin/test.js",
8
8
  "dev": "node lib/core/authz.js"
@@ -28,5 +28,9 @@
28
28
  "repository": {
29
29
  "type": "git",
30
30
  "url": "https://github.com/dhruvil05/simple-authz"
31
- }
31
+ },
32
+ "bugs": {
33
+ "url": "https://github.com/dhruvil05/simple-authz/issues"
34
+ },
35
+ "homepage": "https://github.com/dhruvil05/simple-authz#readme"
32
36
  }
Binary file
Binary file