@arikajs/authorization 0.0.4 → 0.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 +173 -78
- package/dist/AuthResponse.d.ts +23 -0
- package/dist/AuthResponse.d.ts.map +1 -0
- package/dist/AuthResponse.js +40 -0
- package/dist/AuthResponse.js.map +1 -0
- package/dist/AuthorizationContext.d.ts +31 -0
- package/dist/AuthorizationContext.d.ts.map +1 -0
- package/dist/AuthorizationContext.js +87 -0
- package/dist/AuthorizationContext.js.map +1 -0
- package/dist/AuthorizationManager.d.ts +15 -7
- package/dist/AuthorizationManager.d.ts.map +1 -1
- package/dist/AuthorizationManager.js +46 -12
- package/dist/AuthorizationManager.js.map +1 -1
- package/dist/Exceptions/AuthorizationException.d.ts +2 -1
- package/dist/Exceptions/AuthorizationException.d.ts.map +1 -1
- package/dist/Exceptions/AuthorizationException.js +2 -1
- package/dist/Exceptions/AuthorizationException.js.map +1 -1
- package/dist/Gate.d.ts +39 -3
- package/dist/Gate.d.ts.map +1 -1
- package/dist/Gate.js +121 -14
- package/dist/Gate.js.map +1 -1
- package/dist/Middleware/Authorize.d.ts +5 -4
- package/dist/Middleware/Authorize.d.ts.map +1 -1
- package/dist/Middleware/Authorize.js +24 -6
- package/dist/Middleware/Authorize.js.map +1 -1
- package/dist/PolicyResolver.d.ts +7 -2
- package/dist/PolicyResolver.d.ts.map +1 -1
- package/dist/PolicyResolver.js +26 -4
- package/dist/PolicyResolver.js.map +1 -1
- package/dist/RolePermission.d.ts +36 -0
- package/dist/RolePermission.d.ts.map +1 -0
- package/dist/RolePermission.js +59 -0
- package/dist/RolePermission.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/src/AuthResponse.d.ts +23 -0
- package/dist/src/AuthResponse.d.ts.map +1 -0
- package/dist/src/AuthResponse.js +40 -0
- package/dist/src/AuthResponse.js.map +1 -0
- package/dist/src/AuthorizationContext.d.ts +31 -0
- package/dist/src/AuthorizationContext.d.ts.map +1 -0
- package/dist/src/AuthorizationContext.js +87 -0
- package/dist/src/AuthorizationContext.js.map +1 -0
- package/dist/src/AuthorizationManager.d.ts +25 -0
- package/dist/src/AuthorizationManager.d.ts.map +1 -0
- package/dist/src/AuthorizationManager.js +64 -0
- package/dist/src/AuthorizationManager.js.map +1 -0
- package/dist/src/Contracts/Policy.d.ts +4 -0
- package/dist/src/Contracts/Policy.d.ts.map +1 -0
- package/dist/src/Contracts/Policy.js +3 -0
- package/dist/src/Contracts/Policy.js.map +1 -0
- package/dist/src/Exceptions/AuthorizationException.d.ts +6 -0
- package/dist/src/Exceptions/AuthorizationException.d.ts.map +1 -0
- package/dist/src/Exceptions/AuthorizationException.js +13 -0
- package/dist/src/Exceptions/AuthorizationException.js.map +1 -0
- package/dist/src/Gate.d.ts +76 -0
- package/dist/src/Gate.d.ts.map +1 -0
- package/dist/src/Gate.js +189 -0
- package/dist/src/Gate.js.map +1 -0
- package/dist/src/Middleware/Authorize.d.ts +13 -0
- package/dist/src/Middleware/Authorize.d.ts.map +1 -0
- package/dist/src/Middleware/Authorize.js +51 -0
- package/dist/src/Middleware/Authorize.js.map +1 -0
- package/dist/src/PolicyResolver.d.ts +21 -0
- package/dist/src/PolicyResolver.d.ts.map +1 -0
- package/dist/src/PolicyResolver.js +67 -0
- package/dist/src/PolicyResolver.js.map +1 -0
- package/dist/src/RolePermission.d.ts +36 -0
- package/dist/src/RolePermission.d.ts.map +1 -0
- package/dist/src/RolePermission.js +59 -0
- package/dist/src/RolePermission.js.map +1 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +26 -0
- package/dist/src/index.js.map +1 -0
- package/dist/tests/Authorization.test.d.ts +2 -0
- package/dist/tests/Authorization.test.d.ts.map +1 -0
- package/dist/tests/Authorization.test.js +236 -0
- package/dist/tests/Authorization.test.js.map +1 -0
- package/package.json +44 -42
package/README.md
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
## Arika Authorization
|
|
2
2
|
|
|
3
|
-
`@arikajs/authorization` provides authorization (access control) for the ArikaJS framework.
|
|
3
|
+
`@arikajs/authorization` provides a powerful, enterprise-grade authorization (access control) system for the ArikaJS framework.
|
|
4
4
|
|
|
5
|
-
It answers one critical question:
|
|
5
|
+
It answers one critical question: **"Is the authenticated user allowed to perform this action?"**
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
```ts
|
|
8
|
+
// Per-request scoped — safe for concurrent requests
|
|
9
|
+
if (await req.can('edit-post', post)) {
|
|
10
|
+
// authorized
|
|
11
|
+
}
|
|
12
|
+
```
|
|
8
13
|
|
|
9
14
|
---
|
|
10
15
|
|
|
@@ -12,10 +17,14 @@ This package provides a powerful authorization system with Gates and Policies, d
|
|
|
12
17
|
|
|
13
18
|
- **Stage**: Experimental / v0.x
|
|
14
19
|
- **Scope**:
|
|
15
|
-
- Gate-based authorization
|
|
16
|
-
- Policy-based authorization
|
|
17
|
-
-
|
|
18
|
-
-
|
|
20
|
+
- Gate-based authorization with `before()` / `after()` hooks
|
|
21
|
+
- Policy-based authorization with auto-discovery
|
|
22
|
+
- Response-based authorization (custom deny messages)
|
|
23
|
+
- Role & Permission system
|
|
24
|
+
- Bulk ability checks (`any` / `every` / `none`)
|
|
25
|
+
- Request-scoped authorization context (concurrency safe)
|
|
26
|
+
- Authorization middleware with role/permission support
|
|
27
|
+
- Result caching per-request for performance
|
|
19
28
|
- **Design**:
|
|
20
29
|
- Framework-agnostic (usable outside HTTP layer)
|
|
21
30
|
- Decoupled from transport layer
|
|
@@ -36,12 +45,14 @@ This package works on top of `@arikajs/auth` but remains fully decoupled from it
|
|
|
36
45
|
|
|
37
46
|
## 🚀 Features
|
|
38
47
|
|
|
39
|
-
- **Gate-based authorization
|
|
40
|
-
- **Policy-based authorization
|
|
41
|
-
- **
|
|
42
|
-
- **
|
|
43
|
-
- **
|
|
44
|
-
- **
|
|
48
|
+
- **Gate-based authorization** — Simple closure-based checks with before/after hooks.
|
|
49
|
+
- **Policy-based authorization** — Organize logic per model/resource with `before()` bypass.
|
|
50
|
+
- **Response-based authorization** — Return custom deny messages instead of plain booleans.
|
|
51
|
+
- **Role & Permission system** — First-class role and permission checking.
|
|
52
|
+
- **Bulk ability checks** — `Gate.any()`, `Gate.every()`, `Gate.none()` for multi-ability checks.
|
|
53
|
+
- **Request-scoped context** — Memory-safe, per-request isolation with built-in result caching.
|
|
54
|
+
- **Middleware integration** — Protect routes with `can:`, `role:`, and `permission:` prefixes.
|
|
55
|
+
- **Type-safe APIs** — Built for TypeScript.
|
|
45
56
|
|
|
46
57
|
---
|
|
47
58
|
|
|
@@ -49,10 +60,6 @@ This package works on top of `@arikajs/auth` but remains fully decoupled from it
|
|
|
49
60
|
|
|
50
61
|
```bash
|
|
51
62
|
npm install @arikajs/authorization
|
|
52
|
-
# or
|
|
53
|
-
yarn add @arikajs/authorization
|
|
54
|
-
# or
|
|
55
|
-
pnpm add @arikajs/authorization
|
|
56
63
|
```
|
|
57
64
|
|
|
58
65
|
---
|
|
@@ -74,13 +81,8 @@ Gate.define('edit-post', (user, post) => {
|
|
|
74
81
|
**Usage:**
|
|
75
82
|
|
|
76
83
|
```ts
|
|
77
|
-
if (Gate.allows('edit-post', post)) {
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (Gate.denies('edit-post', post)) {
|
|
82
|
-
// ...
|
|
83
|
-
}
|
|
84
|
+
if (await Gate.forUser(user).allows('edit-post', post)) { ... }
|
|
85
|
+
if (await Gate.forUser(user).denies('edit-post', post)) { ... }
|
|
84
86
|
```
|
|
85
87
|
|
|
86
88
|
### 2️⃣ Policies
|
|
@@ -89,101 +91,204 @@ Policies organize authorization logic around a specific model or resource.
|
|
|
89
91
|
|
|
90
92
|
```ts
|
|
91
93
|
class PostPolicy {
|
|
92
|
-
|
|
94
|
+
// Super admin bypass — runs before any check
|
|
95
|
+
before(user: any, ability: string) {
|
|
96
|
+
if (user.isSuperAdmin) return true;
|
|
97
|
+
return null; // continue to actual check
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
view(user: any, post: Post) {
|
|
93
101
|
return true;
|
|
94
102
|
}
|
|
95
103
|
|
|
96
|
-
update(user, post) {
|
|
104
|
+
update(user: any, post: Post) {
|
|
97
105
|
return user.id === post.userId;
|
|
98
106
|
}
|
|
107
|
+
|
|
108
|
+
delete(user: any, post: Post) {
|
|
109
|
+
if (user.id !== post.userId) {
|
|
110
|
+
return AuthResponse.deny('You do not own this post.', 'POST_NOT_OWNED');
|
|
111
|
+
}
|
|
112
|
+
return AuthResponse.allow();
|
|
113
|
+
}
|
|
99
114
|
}
|
|
100
115
|
```
|
|
101
116
|
|
|
102
|
-
**Register
|
|
117
|
+
**Register & Use:**
|
|
103
118
|
|
|
104
119
|
```ts
|
|
105
120
|
Gate.policy(Post, PostPolicy);
|
|
121
|
+
|
|
122
|
+
// Automatically resolves to PostPolicy.update
|
|
123
|
+
await Gate.forUser(user).allows('update', post);
|
|
106
124
|
```
|
|
107
125
|
|
|
108
|
-
|
|
126
|
+
### 3️⃣ Authorization Context (Per-Request)
|
|
127
|
+
|
|
128
|
+
Each request should get its own `AuthorizationContext` for isolation and caching:
|
|
109
129
|
|
|
110
130
|
```ts
|
|
111
|
-
|
|
112
|
-
|
|
131
|
+
const ctx = new AuthorizationContext(user);
|
|
132
|
+
|
|
133
|
+
await ctx.can('edit-post', post); // true
|
|
134
|
+
await ctx.cannot('delete-post', post); // true
|
|
135
|
+
await ctx.authorize('edit-post', post); // throws if denied
|
|
113
136
|
```
|
|
114
137
|
|
|
115
|
-
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 🔑 Role & Permission System
|
|
116
141
|
|
|
117
|
-
|
|
142
|
+
ArikaJS Authorization provides first-class role and permission checking:
|
|
118
143
|
|
|
119
144
|
```ts
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
145
|
+
import { RolePermissionMixin, AuthorizationContext } from '@arikajs/authorization';
|
|
146
|
+
|
|
147
|
+
// Direct usage
|
|
148
|
+
RolePermissionMixin.hasRole(user, 'admin'); // true/false
|
|
149
|
+
RolePermissionMixin.hasAnyRole(user, ['admin', 'editor']);
|
|
150
|
+
RolePermissionMixin.hasPermission(user, 'edit-posts');
|
|
151
|
+
RolePermissionMixin.hasAllPermissions(user, ['view-posts', 'edit-posts']);
|
|
152
|
+
|
|
153
|
+
// Via request-scoped context
|
|
154
|
+
const ctx = new AuthorizationContext(user);
|
|
155
|
+
ctx.hasRole('admin');
|
|
156
|
+
ctx.hasAnyPermission(['edit-posts', 'delete-posts']);
|
|
124
157
|
```
|
|
125
158
|
|
|
159
|
+
User objects should have `roles` and `permissions` arrays (strings or `{name: string}` objects).
|
|
160
|
+
|
|
126
161
|
---
|
|
127
162
|
|
|
128
|
-
##
|
|
163
|
+
## 🛡️ Before / After Hooks
|
|
129
164
|
|
|
130
|
-
|
|
165
|
+
### Global Before Hook (Super Admin Bypass)
|
|
131
166
|
|
|
132
167
|
```ts
|
|
133
|
-
|
|
134
|
-
.
|
|
168
|
+
Gate.before((user, ability) => {
|
|
169
|
+
if (user.isSuperAdmin) return true; // Bypasses ALL checks
|
|
170
|
+
return null; // Continue to actual check
|
|
171
|
+
});
|
|
135
172
|
```
|
|
136
173
|
|
|
137
|
-
|
|
174
|
+
### Global After Hook (Audit Logging)
|
|
138
175
|
|
|
139
176
|
```ts
|
|
140
|
-
.
|
|
177
|
+
Gate.after((user, ability, result) => {
|
|
178
|
+
console.log(`${user.name} → ${ability}: ${result ? 'ALLOWED' : 'DENIED'}`);
|
|
179
|
+
});
|
|
141
180
|
```
|
|
142
181
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
182
|
+
### Policy-Level Before Hook
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
class PostPolicy {
|
|
186
|
+
before(user, ability) {
|
|
187
|
+
if (user.isSuperAdmin) return true;
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## 🔍 Bulk Ability Checks
|
|
196
|
+
|
|
197
|
+
Check multiple abilities in a single call:
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
// Can the user do ANY of these?
|
|
201
|
+
await Gate.forUser(user).any(['edit-post', 'delete-post'], post);
|
|
202
|
+
|
|
203
|
+
// Can the user do ALL of these?
|
|
204
|
+
await Gate.forUser(user).every(['edit-post', 'publish-post'], post);
|
|
205
|
+
|
|
206
|
+
// Can the user do NONE of these?
|
|
207
|
+
await Gate.forUser(user).none(['admin-only', 'super-admin-only']);
|
|
208
|
+
```
|
|
147
209
|
|
|
148
210
|
---
|
|
149
211
|
|
|
150
|
-
##
|
|
212
|
+
## 💬 Response-Based Authorization
|
|
213
|
+
|
|
214
|
+
Instead of returning plain `true`/`false`, return an `AuthResponse` with custom error messages:
|
|
151
215
|
|
|
152
216
|
```ts
|
|
153
|
-
|
|
154
|
-
update(request) {
|
|
155
|
-
Gate.authorize('update', request.post);
|
|
217
|
+
import { AuthResponse } from '@arikajs/authorization';
|
|
156
218
|
|
|
157
|
-
|
|
219
|
+
Gate.define('edit-post', (user, post) => {
|
|
220
|
+
if (user.id !== post.userId) {
|
|
221
|
+
return AuthResponse.deny('You do not own this post.', 'POST_NOT_OWNED');
|
|
158
222
|
}
|
|
159
|
-
|
|
223
|
+
return AuthResponse.allow();
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// Inspect the full response
|
|
227
|
+
const response = await Gate.forUser(user).inspect('edit-post', post);
|
|
228
|
+
response.allowed(); // false
|
|
229
|
+
response.message(); // 'You do not own this post.'
|
|
230
|
+
response.code(); // 'POST_NOT_OWNED'
|
|
160
231
|
```
|
|
161
232
|
|
|
162
|
-
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## 🧩 Middleware Support
|
|
236
|
+
|
|
237
|
+
Protect routes using authorization middleware with multiple strategies:
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
// Gate/Policy check
|
|
241
|
+
Route.get('/posts/:id/edit', controller).middleware('can:edit-post');
|
|
242
|
+
|
|
243
|
+
// Role-based
|
|
244
|
+
Route.get('/admin', controller).middleware('role:admin');
|
|
245
|
+
Route.get('/dashboard', controller).middleware('role:admin,editor');
|
|
246
|
+
|
|
247
|
+
// Permission-based
|
|
248
|
+
Route.get('/settings', controller).middleware('permission:manage-settings');
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Middleware automatically:
|
|
252
|
+
1. Resolves the authenticated user.
|
|
253
|
+
2. Executes the authorization check.
|
|
254
|
+
3. Throws `403 Forbidden` on failure.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## ⚡ Performance
|
|
259
|
+
|
|
260
|
+
- **Per-request caching**: `AuthorizationContext` caches ability results within a single request, preventing redundant gate/policy evaluation.
|
|
261
|
+
- **Policy instance caching**: `PolicyResolver` caches instantiated policy objects instead of creating new ones on every call.
|
|
262
|
+
- **Short-circuit evaluation**: `before()` hooks return immediately without running the actual check.
|
|
263
|
+
- **Bulk checks early-exit**: `any()` returns on first `true`, `none()` returns on first `true`.
|
|
163
264
|
|
|
164
265
|
---
|
|
165
266
|
|
|
166
267
|
## 🏗 Architecture
|
|
167
268
|
|
|
168
|
-
```
|
|
269
|
+
```text
|
|
169
270
|
authorization/
|
|
170
271
|
├── src/
|
|
171
|
-
│ ├──
|
|
172
|
-
│ ├── AuthorizationManager.ts ← Core authorization engine
|
|
173
|
-
│ ├── PolicyResolver.ts ← Maps models to policies
|
|
174
|
-
│ ├── Middleware/
|
|
175
|
-
│ │ └── Authorize.ts ← Route-level protection
|
|
176
|
-
│ ├── Exceptions/
|
|
177
|
-
│ │ └── AuthorizationException.ts
|
|
178
|
-
│ ├── Contracts/
|
|
272
|
+
│ ├── Contracts
|
|
179
273
|
│ │ └── Policy.ts
|
|
180
|
-
│
|
|
274
|
+
│ ├── Exceptions
|
|
275
|
+
│ │ └── AuthorizationException.ts
|
|
276
|
+
│ ├── Middleware
|
|
277
|
+
│ │ └── Authorize.ts
|
|
278
|
+
│ ├── AuthorizationContext.ts
|
|
279
|
+
│ ├── AuthorizationManager.ts
|
|
280
|
+
│ ├── AuthResponse.ts
|
|
281
|
+
│ ├── Gate.ts
|
|
282
|
+
│ ├── index.ts
|
|
283
|
+
│ ├── PolicyResolver.ts
|
|
284
|
+
│ └── RolePermission.ts
|
|
285
|
+
├── tests/
|
|
181
286
|
├── package.json
|
|
182
287
|
├── tsconfig.json
|
|
183
|
-
|
|
184
|
-
└── LICENSE
|
|
288
|
+
└── README.md
|
|
185
289
|
```
|
|
186
290
|
|
|
291
|
+
|
|
187
292
|
---
|
|
188
293
|
|
|
189
294
|
## 🔌 Integration with ArikaJS
|
|
@@ -197,25 +302,15 @@ authorization/
|
|
|
197
302
|
|
|
198
303
|
---
|
|
199
304
|
|
|
200
|
-
## 🧪 Error Handling
|
|
201
|
-
|
|
202
|
-
Unauthorized access throws:
|
|
203
|
-
- `AuthorizationException` (403)
|
|
204
|
-
|
|
205
|
-
Handled automatically by:
|
|
206
|
-
- HTTP kernel
|
|
207
|
-
- Middleware pipeline
|
|
208
|
-
|
|
209
|
-
---
|
|
210
|
-
|
|
211
305
|
## 📌 Design Philosophy
|
|
212
306
|
|
|
213
307
|
- **Explicit over implicit**: Authorization rules should be clear.
|
|
214
308
|
- **Centralized rules**: Keep logic in Gates or Policies, not controllers.
|
|
215
|
-
- **
|
|
309
|
+
- **Rich feedback**: Return custom error messages, not just true/false.
|
|
310
|
+
- **Performance first**: Cache results, short-circuit, instance re-use.
|
|
216
311
|
- **Decoupled from transport layer**: Logic works for HTTP, CLI, etc.
|
|
217
312
|
|
|
218
|
-
>
|
|
313
|
+
> "Authentication identifies the user. Authorization empowers or restricts them."
|
|
219
314
|
|
|
220
315
|
---
|
|
221
316
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents the result of an authorization check.
|
|
3
|
+
* Allows gates/policies to return custom denial messages instead of just true/false.
|
|
4
|
+
*/
|
|
5
|
+
export declare class AuthResponse {
|
|
6
|
+
private _allowed;
|
|
7
|
+
private _message;
|
|
8
|
+
private _code;
|
|
9
|
+
constructor(allowed: boolean, message?: string | null, code?: string | null);
|
|
10
|
+
allowed(): boolean;
|
|
11
|
+
denied(): boolean;
|
|
12
|
+
message(): string | null;
|
|
13
|
+
code(): string | null;
|
|
14
|
+
/**
|
|
15
|
+
* Create an "allow" response.
|
|
16
|
+
*/
|
|
17
|
+
static allow(message?: string | null): AuthResponse;
|
|
18
|
+
/**
|
|
19
|
+
* Create a "deny" response with an optional custom message.
|
|
20
|
+
*/
|
|
21
|
+
static deny(message?: string, code?: string | null): AuthResponse;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=AuthResponse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthResponse.d.ts","sourceRoot":"","sources":["../src/AuthResponse.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,YAAY;IACrB,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,KAAK,CAAgB;gBAEjB,OAAO,EAAE,OAAO,EAAE,OAAO,GAAE,MAAM,GAAG,IAAW,EAAE,IAAI,GAAE,MAAM,GAAG,IAAW;IAMhF,OAAO,IAAI,OAAO;IAIlB,MAAM,IAAI,OAAO;IAIjB,OAAO,IAAI,MAAM,GAAG,IAAI;IAIxB,IAAI,IAAI,MAAM,GAAG,IAAI;IAI5B;;OAEG;WACW,KAAK,CAAC,OAAO,GAAE,MAAM,GAAG,IAAW,GAAG,YAAY;IAIhE;;OAEG;WACW,IAAI,CAAC,OAAO,GAAE,MAAuC,EAAE,IAAI,GAAE,MAAM,GAAG,IAAW,GAAG,YAAY;CAGjH"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AuthResponse = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Represents the result of an authorization check.
|
|
6
|
+
* Allows gates/policies to return custom denial messages instead of just true/false.
|
|
7
|
+
*/
|
|
8
|
+
class AuthResponse {
|
|
9
|
+
constructor(allowed, message = null, code = null) {
|
|
10
|
+
this._allowed = allowed;
|
|
11
|
+
this._message = message;
|
|
12
|
+
this._code = code;
|
|
13
|
+
}
|
|
14
|
+
allowed() {
|
|
15
|
+
return this._allowed;
|
|
16
|
+
}
|
|
17
|
+
denied() {
|
|
18
|
+
return !this._allowed;
|
|
19
|
+
}
|
|
20
|
+
message() {
|
|
21
|
+
return this._message;
|
|
22
|
+
}
|
|
23
|
+
code() {
|
|
24
|
+
return this._code;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Create an "allow" response.
|
|
28
|
+
*/
|
|
29
|
+
static allow(message = null) {
|
|
30
|
+
return new AuthResponse(true, message);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Create a "deny" response with an optional custom message.
|
|
34
|
+
*/
|
|
35
|
+
static deny(message = 'This action is unauthorized.', code = null) {
|
|
36
|
+
return new AuthResponse(false, message, code);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.AuthResponse = AuthResponse;
|
|
40
|
+
//# sourceMappingURL=AuthResponse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthResponse.js","sourceRoot":"","sources":["../src/AuthResponse.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,MAAa,YAAY;IAKrB,YAAY,OAAgB,EAAE,UAAyB,IAAI,EAAE,OAAsB,IAAI;QACnF,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QACxB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QACxB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACtB,CAAC;IAEM,OAAO;QACV,OAAO,IAAI,CAAC,QAAQ,CAAC;IACzB,CAAC;IAEM,MAAM;QACT,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;IAC1B,CAAC;IAEM,OAAO;QACV,OAAO,IAAI,CAAC,QAAQ,CAAC;IACzB,CAAC;IAEM,IAAI;QACP,OAAO,IAAI,CAAC,KAAK,CAAC;IACtB,CAAC;IAED;;OAEG;IACI,MAAM,CAAC,KAAK,CAAC,UAAyB,IAAI;QAC7C,OAAO,IAAI,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED;;OAEG;IACI,MAAM,CAAC,IAAI,CAAC,UAAkB,8BAA8B,EAAE,OAAsB,IAAI;QAC3F,OAAO,IAAI,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAClD,CAAC;CACJ;AAxCD,oCAwCC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { AuthResponse } from './AuthResponse';
|
|
2
|
+
/**
|
|
3
|
+
* Per-request authorization context.
|
|
4
|
+
* Avoids the global mutable `Gate.currentUser` problem under concurrency.
|
|
5
|
+
* Each request gets its own `AuthorizationContext` with an isolated user reference.
|
|
6
|
+
*/
|
|
7
|
+
export declare class AuthorizationContext {
|
|
8
|
+
private user;
|
|
9
|
+
private cache;
|
|
10
|
+
constructor(user: any);
|
|
11
|
+
can(ability: string, ...args: any[]): Promise<boolean>;
|
|
12
|
+
cannot(ability: string, ...args: any[]): Promise<boolean>;
|
|
13
|
+
authorize(ability: string, ...args: any[]): Promise<void>;
|
|
14
|
+
inspect(ability: string, ...args: any[]): Promise<AuthResponse>;
|
|
15
|
+
any(abilities: string[], ...args: any[]): Promise<boolean>;
|
|
16
|
+
every(abilities: string[], ...args: any[]): Promise<boolean>;
|
|
17
|
+
none(abilities: string[], ...args: any[]): Promise<boolean>;
|
|
18
|
+
hasRole(role: string | string[]): boolean;
|
|
19
|
+
hasAnyRole(roles: string[]): boolean;
|
|
20
|
+
hasAllRoles(roles: string[]): boolean;
|
|
21
|
+
hasPermission(permission: string): boolean;
|
|
22
|
+
hasAnyPermission(permissions: string[]): boolean;
|
|
23
|
+
hasAllPermissions(permissions: string[]): boolean;
|
|
24
|
+
getUser(): any;
|
|
25
|
+
/**
|
|
26
|
+
* Clear the authorization cache (e.g. after role change mid-request).
|
|
27
|
+
*/
|
|
28
|
+
flushCache(): void;
|
|
29
|
+
private buildCacheKey;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=AuthorizationContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthorizationContext.d.ts","sourceRoot":"","sources":["../src/AuthorizationContext.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C;;;;GAIG;AACH,qBAAa,oBAAoB;IAC7B,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,KAAK,CAAmC;gBAEpC,IAAI,EAAE,GAAG;IAMR,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAWtD,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAIzD,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzD,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC;IAM/D,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAI1D,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAI5D,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAMjE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO;IAIzC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO;IAIpC,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO;IAIrC,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAI1C,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO;IAIhD,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO;IAMjD,OAAO,IAAI,GAAG;IAIrB;;OAEG;IACI,UAAU,IAAI,IAAI;IAIzB,OAAO,CAAC,aAAa;CASxB"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AuthorizationContext = void 0;
|
|
4
|
+
const Gate_1 = require("./Gate");
|
|
5
|
+
const RolePermission_1 = require("./RolePermission");
|
|
6
|
+
/**
|
|
7
|
+
* Per-request authorization context.
|
|
8
|
+
* Avoids the global mutable `Gate.currentUser` problem under concurrency.
|
|
9
|
+
* Each request gets its own `AuthorizationContext` with an isolated user reference.
|
|
10
|
+
*/
|
|
11
|
+
class AuthorizationContext {
|
|
12
|
+
constructor(user) {
|
|
13
|
+
this.cache = new Map();
|
|
14
|
+
this.user = user;
|
|
15
|
+
}
|
|
16
|
+
// ── Single ability checks ───────────────────────────────────────
|
|
17
|
+
async can(ability, ...args) {
|
|
18
|
+
const cacheKey = this.buildCacheKey(ability, args);
|
|
19
|
+
if (this.cache.has(cacheKey)) {
|
|
20
|
+
return this.cache.get(cacheKey);
|
|
21
|
+
}
|
|
22
|
+
const result = await Gate_1.Gate.forUser(this.user).allows(ability, ...args);
|
|
23
|
+
this.cache.set(cacheKey, result);
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
async cannot(ability, ...args) {
|
|
27
|
+
return !(await this.can(ability, ...args));
|
|
28
|
+
}
|
|
29
|
+
async authorize(ability, ...args) {
|
|
30
|
+
await Gate_1.Gate.forUser(this.user).authorize(ability, ...args);
|
|
31
|
+
}
|
|
32
|
+
async inspect(ability, ...args) {
|
|
33
|
+
return await Gate_1.Gate.forUser(this.user).inspect(ability, ...args);
|
|
34
|
+
}
|
|
35
|
+
// ── Bulk ability checks ─────────────────────────────────────────
|
|
36
|
+
async any(abilities, ...args) {
|
|
37
|
+
return await Gate_1.Gate.forUser(this.user).any(abilities, ...args);
|
|
38
|
+
}
|
|
39
|
+
async every(abilities, ...args) {
|
|
40
|
+
return await Gate_1.Gate.forUser(this.user).every(abilities, ...args);
|
|
41
|
+
}
|
|
42
|
+
async none(abilities, ...args) {
|
|
43
|
+
return await Gate_1.Gate.forUser(this.user).none(abilities, ...args);
|
|
44
|
+
}
|
|
45
|
+
// ── Role & Permission checks (via mixin) ────────────────────────
|
|
46
|
+
hasRole(role) {
|
|
47
|
+
return RolePermission_1.RolePermissionMixin.hasRole(this.user, role);
|
|
48
|
+
}
|
|
49
|
+
hasAnyRole(roles) {
|
|
50
|
+
return RolePermission_1.RolePermissionMixin.hasAnyRole(this.user, roles);
|
|
51
|
+
}
|
|
52
|
+
hasAllRoles(roles) {
|
|
53
|
+
return RolePermission_1.RolePermissionMixin.hasAllRoles(this.user, roles);
|
|
54
|
+
}
|
|
55
|
+
hasPermission(permission) {
|
|
56
|
+
return RolePermission_1.RolePermissionMixin.hasPermission(this.user, permission);
|
|
57
|
+
}
|
|
58
|
+
hasAnyPermission(permissions) {
|
|
59
|
+
return RolePermission_1.RolePermissionMixin.hasAnyPermission(this.user, permissions);
|
|
60
|
+
}
|
|
61
|
+
hasAllPermissions(permissions) {
|
|
62
|
+
return RolePermission_1.RolePermissionMixin.hasAllPermissions(this.user, permissions);
|
|
63
|
+
}
|
|
64
|
+
// ── Internals ───────────────────────────────────────────────────
|
|
65
|
+
getUser() {
|
|
66
|
+
return this.user;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Clear the authorization cache (e.g. after role change mid-request).
|
|
70
|
+
*/
|
|
71
|
+
flushCache() {
|
|
72
|
+
this.cache.clear();
|
|
73
|
+
}
|
|
74
|
+
buildCacheKey(ability, args) {
|
|
75
|
+
// Build a simple cache key from ability + resource IDs
|
|
76
|
+
const argIds = args.map(a => {
|
|
77
|
+
if (a && typeof a === 'object' && a.id !== undefined)
|
|
78
|
+
return `${a.constructor?.name || 'obj'}:${a.id}`;
|
|
79
|
+
if (a && typeof a === 'object' && a.constructor)
|
|
80
|
+
return a.constructor.name;
|
|
81
|
+
return String(a);
|
|
82
|
+
});
|
|
83
|
+
return `${ability}:${argIds.join(',')}`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.AuthorizationContext = AuthorizationContext;
|
|
87
|
+
//# sourceMappingURL=AuthorizationContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthorizationContext.js","sourceRoot":"","sources":["../src/AuthorizationContext.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,17 +1,25 @@
|
|
|
1
|
+
import { AuthorizationContext } from './AuthorizationContext';
|
|
2
|
+
import { AuthResponse } from './AuthResponse';
|
|
1
3
|
export declare class AuthorizationManager {
|
|
2
4
|
private user;
|
|
5
|
+
private context;
|
|
3
6
|
constructor(user: any);
|
|
4
7
|
/**
|
|
5
|
-
*
|
|
8
|
+
* Create a request-scoped authorization context and bind it to the request.
|
|
6
9
|
*/
|
|
10
|
+
static createContext(request: any): AuthorizationContext;
|
|
7
11
|
can(ability: string, ...args: any[]): Promise<boolean>;
|
|
8
|
-
/**
|
|
9
|
-
* Determine if the user cannot perform the given ability.
|
|
10
|
-
*/
|
|
11
12
|
cannot(ability: string, ...args: any[]): Promise<boolean>;
|
|
12
|
-
/**
|
|
13
|
-
* Authorize or throw exception.
|
|
14
|
-
*/
|
|
15
13
|
authorize(ability: string, ...args: any[]): Promise<void>;
|
|
14
|
+
inspect(ability: string, ...args: any[]): Promise<AuthResponse>;
|
|
15
|
+
any(abilities: string[], ...args: any[]): Promise<boolean>;
|
|
16
|
+
every(abilities: string[], ...args: any[]): Promise<boolean>;
|
|
17
|
+
none(abilities: string[], ...args: any[]): Promise<boolean>;
|
|
18
|
+
hasRole(role: string | string[]): boolean;
|
|
19
|
+
hasAnyRole(roles: string[]): boolean;
|
|
20
|
+
hasAllRoles(roles: string[]): boolean;
|
|
21
|
+
hasPermission(permission: string): boolean;
|
|
22
|
+
hasAnyPermission(permissions: string[]): boolean;
|
|
23
|
+
hasAllPermissions(permissions: string[]): boolean;
|
|
16
24
|
}
|
|
17
25
|
//# sourceMappingURL=AuthorizationManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthorizationManager.d.ts","sourceRoot":"","sources":["../src/AuthorizationManager.ts"],"names":[],"mappings":"AAAA,qBAAa,oBAAoB;IAC7B,OAAO,CAAC,IAAI,CAAM;
|
|
1
|
+
{"version":3,"file":"AuthorizationManager.d.ts","sourceRoot":"","sources":["../src/AuthorizationManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,qBAAa,oBAAoB;IAC7B,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,OAAO,CAAuB;gBAE1B,IAAI,EAAE,GAAG;IAKrB;;OAEG;WACW,aAAa,CAAC,OAAO,EAAE,GAAG,GAAG,oBAAoB;IAWlD,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAItD,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAIzD,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzD,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC;IAI/D,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAI1D,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAI5D,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAMjE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO;IAIzC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO;IAIpC,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO;IAIrC,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAI1C,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO;IAIhD,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO;CAG3D"}
|