rbac-shield 0.2.1 → 0.2.2
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 +104 -207
- package/dist/factory.d.mts +7 -2
- package/dist/factory.d.ts +7 -2
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +160 -29
- package/dist/factory.mjs +160 -29
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.mts +3 -0
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,11 +5,12 @@
|
|
|
5
5
|
[]()
|
|
6
6
|
[](https://www.typescriptlang.org/)
|
|
7
7
|
|
|
8
|
-
> [!WARNING]
|
|
8
|
+
> [!WARNING]
|
|
9
|
+
> **Public Beta**: Ensuring strict type safety and performance. API is stable but minor breaking changes might occur before v1.0.
|
|
9
10
|
|
|
10
11
|
**The production-ready, type-safe Role-Based Access Control (RBAC) system for Next.js applications.**
|
|
11
12
|
|
|
12
|
-
Built for modern web development with **React 19**, **TypeScript 5**, and **Next.js App Router** compatibility. RBAC Shield provides a seamless, multi-tenant permission system that
|
|
13
|
+
Built for modern web development with **React 19**, **TypeScript 5**, and **Next.js App Router** compatibility. RBAC Shield provides a seamless, multi-tenant permission system that supports both **Role-based** and **Permission-based** strategies.
|
|
13
14
|
|
|
14
15
|
---
|
|
15
16
|
|
|
@@ -19,10 +20,10 @@ Built for modern web development with **React 19**, **TypeScript 5**, and **Next
|
|
|
19
20
|
- [Quick Setup (CLI)](#-quick-setup-recommended)
|
|
20
21
|
- [Manual Installation](#-manual-installation)
|
|
21
22
|
- [Quick Start](#-quick-start)
|
|
23
|
+
- [Role Management (New)](#-role-management)
|
|
22
24
|
- [Guides & Patterns](#-guides--patterns)
|
|
23
|
-
- [Roles
|
|
25
|
+
- [Roles vs Permissions](#roles-vs-permissions)
|
|
24
26
|
- [Complex Logic (AND/OR)](#complex-logic-andor)
|
|
25
|
-
- [Customizing UX](#customizing-ux-loading--redirects)
|
|
26
27
|
- [SSR & Hydration](#ssr--hydration-eliminate-loading-states)
|
|
27
28
|
- [Logic Switching (Dynamic APIs)](#logic-switching-dynamic-apis)
|
|
28
29
|
- [Server Action Guards](#server-action-guards)
|
|
@@ -35,6 +36,7 @@ Built for modern web development with **React 19**, **TypeScript 5**, and **Next
|
|
|
35
36
|
## ✨ Features
|
|
36
37
|
|
|
37
38
|
- 🎯 **Type-Safe Permissions**: Typescript "Prettify" helpers ensure tooltips show exact prop shapes, amazing IntelliSense.
|
|
39
|
+
- 👑 **First-Class Role Support**: Check for Roles (`admin`), Permissions (`edit:post`), or both simultaneously.
|
|
38
40
|
- 🚀 **High Performance**: Optimized with React Context and memoization. Permission checks are < 1ms.
|
|
39
41
|
- 🏢 **Multi-Tenant Native**: Switch between multiple organizations/roles instantly without page reloads.
|
|
40
42
|
- ⚡ **Zero Loading States**: Support for `initialData` prop allows instant hydration from server-side data.
|
|
@@ -100,7 +102,8 @@ export type Actions = "view" | "create" | "edit" | "delete" | "export";
|
|
|
100
102
|
// 3. Create your instances
|
|
101
103
|
export const {
|
|
102
104
|
RBACProvider,
|
|
103
|
-
useRBAC,
|
|
105
|
+
useRBAC,
|
|
106
|
+
useHasRole, // New!
|
|
104
107
|
useHasPermission,
|
|
105
108
|
useMatch,
|
|
106
109
|
Can,
|
|
@@ -111,7 +114,7 @@ export const {
|
|
|
111
114
|
|
|
112
115
|
### 2. Wrap Your App
|
|
113
116
|
|
|
114
|
-
Add the provider to your root layout
|
|
117
|
+
Add the provider to your root layout.
|
|
115
118
|
|
|
116
119
|
```tsx
|
|
117
120
|
// app/layout.tsx
|
|
@@ -134,36 +137,30 @@ export default function RootLayout({
|
|
|
134
137
|
|
|
135
138
|
### 3. Load Permissions
|
|
136
139
|
|
|
137
|
-
Initialize the system with data from your backend (
|
|
140
|
+
Initialize the system with data from your backend. You can now pass just permissions (strings) or roles and permissions.
|
|
138
141
|
|
|
139
142
|
```tsx
|
|
140
143
|
// components/AuthProvider.tsx
|
|
141
144
|
"use client";
|
|
142
145
|
import { useEffect } from "react";
|
|
143
|
-
import { useRBAC
|
|
146
|
+
import { useRBAC } from "@/lib/rbac";
|
|
144
147
|
|
|
145
|
-
export function AuthProvider({
|
|
146
|
-
|
|
147
|
-
children,
|
|
148
|
-
}: {
|
|
149
|
-
user: any;
|
|
150
|
-
children: React.ReactNode;
|
|
151
|
-
}) {
|
|
152
|
-
const { setAuth, switchTenant } = useRBAC();
|
|
148
|
+
export function AuthProvider({ user, children }) {
|
|
149
|
+
const { setAuth } = useRBAC();
|
|
153
150
|
|
|
154
151
|
useEffect(() => {
|
|
155
152
|
if (user) {
|
|
156
|
-
//
|
|
157
|
-
setAuth([
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
]
|
|
163
|
-
//
|
|
164
|
-
|
|
153
|
+
// Option A: Simple Permissions (Auto-assigned to 'default' tenant)
|
|
154
|
+
setAuth(["projects:view"]);
|
|
155
|
+
|
|
156
|
+
// Option B: Roles + Permissions
|
|
157
|
+
// setAuth([{
|
|
158
|
+
// tenantId: "default",
|
|
159
|
+
// roles: ["admin"],
|
|
160
|
+
// permissions: ["projects:view"]
|
|
161
|
+
// }]);
|
|
165
162
|
}
|
|
166
|
-
}, [user, setAuth
|
|
163
|
+
}, [user, setAuth]);
|
|
167
164
|
|
|
168
165
|
return <>{children}</>;
|
|
169
166
|
}
|
|
@@ -171,26 +168,28 @@ export function AuthProvider({
|
|
|
171
168
|
|
|
172
169
|
### 4. Secure Your App
|
|
173
170
|
|
|
174
|
-
Use the
|
|
171
|
+
Use the components to guard access.
|
|
175
172
|
|
|
176
173
|
```tsx
|
|
177
|
-
import { ProtectedRoute, Can,
|
|
174
|
+
import { ProtectedRoute, Can, useHasRole } from "@/lib/rbac";
|
|
178
175
|
|
|
179
|
-
export default function
|
|
180
|
-
const
|
|
176
|
+
export default function AdminPanel() {
|
|
177
|
+
const isAdmin = useHasRole("admin");
|
|
181
178
|
|
|
182
179
|
return (
|
|
183
|
-
// 1.
|
|
184
|
-
<ProtectedRoute
|
|
185
|
-
<h1>
|
|
180
|
+
// 1. Role-Based Route Protection
|
|
181
|
+
<ProtectedRoute role="admin" fallbackPath="/login">
|
|
182
|
+
<h1>Admin Dashboard</h1>
|
|
186
183
|
|
|
187
|
-
{/* 2.
|
|
188
|
-
<Can permission="billing:view"
|
|
184
|
+
{/* 2. Permission Check */}
|
|
185
|
+
<Can permission="billing:view">
|
|
189
186
|
<BillingWidget />
|
|
190
187
|
</Can>
|
|
191
188
|
|
|
192
|
-
{/* 3.
|
|
193
|
-
<
|
|
189
|
+
{/* 3. Combined Logic (Role AND Permission) */}
|
|
190
|
+
<Can role="manager" permission="users:delete">
|
|
191
|
+
<DeleteUserButton />
|
|
192
|
+
</Can>
|
|
194
193
|
</ProtectedRoute>
|
|
195
194
|
);
|
|
196
195
|
}
|
|
@@ -198,28 +197,49 @@ export default function ProjectSettings() {
|
|
|
198
197
|
|
|
199
198
|
---
|
|
200
199
|
|
|
201
|
-
##
|
|
200
|
+
## 👑 Role Management
|
|
202
201
|
|
|
203
|
-
|
|
202
|
+
RBAC Shield now supports **Dynamic Logic** for access control. You can check for Roles, Permissions, or Both.
|
|
204
203
|
|
|
205
|
-
|
|
206
|
-
Assign a "identity permission" to your roles, e.g., `role:admin`, `role:manager`.
|
|
204
|
+
### Logic Matrix
|
|
207
205
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
206
|
+
| Props Provided | Logic Applied | Example |
|
|
207
|
+
| :------------------ | :--------------------------------------------------- | :------------------------------------- |
|
|
208
|
+
| **Role Only** | User has `role` | `<Can role="admin">` |
|
|
209
|
+
| **Permission Only** | User has `permission` | `<Can permission="edit">` |
|
|
210
|
+
| **Both** | **STRICT AND**: User has `role` **AND** `permission` | `<Can role="admin" permission="edit">` |
|
|
211
|
+
| **Neither** | Deny Access | `<Can />` (Renders nothing) |
|
|
212
|
+
|
|
213
|
+
### Wildcards
|
|
214
|
+
|
|
215
|
+
- **Roles**: If the user has the role `*`, they pass ALL role checks.
|
|
216
|
+
- **Permissions**: If the user has permission `*`, they pass ALL permission checks.
|
|
211
217
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
218
|
+
### Array Inputs (OR Logic)
|
|
219
|
+
|
|
220
|
+
If you provide an array to `role` or `permission`, by default it checks if the user has **ANY** of them (OR logic).
|
|
221
|
+
|
|
222
|
+
```tsx
|
|
223
|
+
// User is EITHER 'admin' OR 'manager'
|
|
224
|
+
<Can role={["admin", "manager"]}>
|
|
225
|
+
<ManagementPanel />
|
|
216
226
|
</Can>
|
|
217
227
|
```
|
|
218
228
|
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## 📖 Guides & Patterns
|
|
232
|
+
|
|
233
|
+
### Roles vs Permissions
|
|
234
|
+
|
|
235
|
+
- **Roles**: Use for high-level identity or persona checks (e.g., "Is this an Admin?").
|
|
236
|
+
- **Permissions**: Use for granular capability checks (e.g., "Can they delete this post?").
|
|
237
|
+
|
|
238
|
+
**Best Practice**: Combine them! Use `<ProtectedRoute role="admin">` for the page layout, and `<Can permission="settings:edit">` for specific buttons.
|
|
239
|
+
|
|
219
240
|
### Complex Logic (AND/OR)
|
|
220
241
|
|
|
221
|
-
|
|
222
|
-
Use the array format with `requireAll`.
|
|
242
|
+
Use `requireAll` to enforce strict requirements on arrays.
|
|
223
243
|
|
|
224
244
|
```tsx
|
|
225
245
|
// User must be 'admin' AND have 'post:delete' permission
|
|
@@ -228,27 +248,6 @@ Use the array format with `requireAll`.
|
|
|
228
248
|
</Can>
|
|
229
249
|
```
|
|
230
250
|
|
|
231
|
-
### Customizing UX (Loading & Redirects)
|
|
232
|
-
|
|
233
|
-
You have full control over the loading and fallback states. Pass `null` to hide them completely.
|
|
234
|
-
|
|
235
|
-
```tsx
|
|
236
|
-
// 1. Silent Loading (No Spinner)
|
|
237
|
-
<ProtectedRoute permission="admin:view" loadingComponent={null} >
|
|
238
|
-
<Dashboard />
|
|
239
|
-
</ProtectedRoute>
|
|
240
|
-
|
|
241
|
-
// 2. Instant Redirect (No "Access Denied" screen)
|
|
242
|
-
<ProtectedRoute permission="admin:view" fallback={null} fallbackPath="/login" >
|
|
243
|
-
<Dashboard />
|
|
244
|
-
</ProtectedRoute>
|
|
245
|
-
|
|
246
|
-
// 3. No Redirect (Show Custom 403 Page)
|
|
247
|
-
<ProtectedRoute permission="admin:view" redirect={false} fallback={<AccessDeniedPage />} >
|
|
248
|
-
<Dashboard />
|
|
249
|
-
</ProtectedRoute>
|
|
250
|
-
```
|
|
251
|
-
|
|
252
251
|
### SSR & Hydration (Eliminate Loading States)
|
|
253
252
|
|
|
254
253
|
Prevent the "flicker" of loading states by passing server-side permissions directly to the provider.
|
|
@@ -256,24 +255,18 @@ Prevent the "flicker" of loading states by passing server-side permissions direc
|
|
|
256
255
|
```tsx
|
|
257
256
|
// app/layout.tsx (Server Component)
|
|
258
257
|
import { RBACProvider } from "@/lib/rbac";
|
|
259
|
-
import { getSession } from "@/lib/auth";
|
|
258
|
+
import { getSession } from "@/lib/auth";
|
|
260
259
|
|
|
261
260
|
export default async function RootLayout({ children }) {
|
|
262
261
|
const session = await getSession();
|
|
263
262
|
|
|
264
|
-
//
|
|
265
|
-
const initialData = session
|
|
266
|
-
? [
|
|
267
|
-
{
|
|
268
|
-
tenantId: session.orgId,
|
|
269
|
-
permissions: session.permissions, // string[] from DB is fine!
|
|
270
|
-
},
|
|
271
|
-
]
|
|
272
|
-
: [];
|
|
263
|
+
// server-side: just pass the string array of permissions!
|
|
264
|
+
const initialData = session?.permissions || [];
|
|
273
265
|
|
|
274
266
|
return (
|
|
275
267
|
<html>
|
|
276
268
|
<body>
|
|
269
|
+
{/* Hydrates instantly! */}
|
|
277
270
|
<RBACProvider initialData={initialData}>{children}</RBACProvider>
|
|
278
271
|
</body>
|
|
279
272
|
</html>
|
|
@@ -283,51 +276,19 @@ export default async function RootLayout({ children }) {
|
|
|
283
276
|
|
|
284
277
|
### Logic Switching (Dynamic APIs)
|
|
285
278
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
#### Option 1: Top-Level Hook (Recommended)
|
|
289
|
-
|
|
290
|
-
Use this when you want to resolve the handler _during render_ but call it later (e.g., on click).
|
|
279
|
+
Use `useMatch` to execute different logic based on permissions or roles.
|
|
291
280
|
|
|
292
281
|
```tsx
|
|
293
|
-
import {
|
|
282
|
+
import { useMatch } from "@/lib/rbac";
|
|
294
283
|
|
|
295
284
|
export default function Dashboard() {
|
|
296
|
-
// 1. Resolve the handler at the top level
|
|
297
|
-
// Note: We return a FUNCTION () => ... so it doesn't run immediately!
|
|
298
285
|
const getData = useMatch({
|
|
299
|
-
"admin:view": () =>
|
|
300
|
-
"manager:view": () =>
|
|
301
|
-
default: () =>
|
|
286
|
+
"admin:view": () => api.getAdminStats(),
|
|
287
|
+
"manager:view": () => api.getManagerStats(),
|
|
288
|
+
default: () => api.getUserStats(),
|
|
302
289
|
});
|
|
303
290
|
|
|
304
|
-
|
|
305
|
-
// 2. Call the resolved function
|
|
306
|
-
if (getData) getData();
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
return <button onClick={handleRefresh}>Refresh Data</button>;
|
|
310
|
-
}
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
#### Option 2: Event Handler Utility
|
|
314
|
-
|
|
315
|
-
Use the `match` utility (not the hook) if you want to keep all logic inside the event handler.
|
|
316
|
-
|
|
317
|
-
```tsx
|
|
318
|
-
import { usePermissions, match } from "@/lib/rbac";
|
|
319
|
-
|
|
320
|
-
export default function Dashboard() {
|
|
321
|
-
const permissions = usePermissions(); // Hooks must be top-level
|
|
322
|
-
|
|
323
|
-
const handleRefresh = () => {
|
|
324
|
-
// 'match' is a plain function, safe to use here!
|
|
325
|
-
match(permissions, {
|
|
326
|
-
"admin:view": () => api.getAdminStats(),
|
|
327
|
-
"manager:view": () => api.getManagerStats(),
|
|
328
|
-
default: () => api.getUserStats(),
|
|
329
|
-
});
|
|
330
|
-
};
|
|
291
|
+
return <button onClick={getData}>Refresh Data</button>;
|
|
331
292
|
}
|
|
332
293
|
```
|
|
333
294
|
|
|
@@ -343,17 +304,16 @@ import { getSession } from "@/lib/auth";
|
|
|
343
304
|
export async function deleteProject(id: string) {
|
|
344
305
|
const session = await getSession();
|
|
345
306
|
|
|
346
|
-
// Wrap your delicate logic
|
|
347
307
|
const safeAction = guard(
|
|
348
308
|
session.permissions, // User's permissions
|
|
349
309
|
"project:delete", // Required permission
|
|
350
310
|
async () => {
|
|
351
311
|
await db.project.delete(id);
|
|
352
312
|
return "Deleted!";
|
|
353
|
-
}
|
|
313
|
+
},
|
|
354
314
|
);
|
|
355
315
|
|
|
356
|
-
return safeAction();
|
|
316
|
+
return safeAction();
|
|
357
317
|
}
|
|
358
318
|
```
|
|
359
319
|
|
|
@@ -363,119 +323,56 @@ export async function deleteProject(id: string) {
|
|
|
363
323
|
|
|
364
324
|
### Components
|
|
365
325
|
|
|
366
|
-
#### `<RBACProvider>`
|
|
367
|
-
|
|
368
|
-
Top-level provider component.
|
|
369
|
-
|
|
370
|
-
- **initialData**: (Optional) `TenantAuthInput[]`. Hydrate state immediately.
|
|
371
|
-
- **debug**: (Optional) `boolean`. Log permission checks to console.
|
|
372
|
-
|
|
373
326
|
#### `<ProtectedRoute>`
|
|
374
327
|
|
|
375
328
|
A wrapper component that guards an entire route or section.
|
|
376
329
|
|
|
377
|
-
- **permission**:
|
|
378
|
-
- **
|
|
379
|
-
- **
|
|
380
|
-
- **
|
|
381
|
-
- **
|
|
382
|
-
- **
|
|
330
|
+
- **permission**: string | string[] (Optional)
|
|
331
|
+
- **role**: string | string[] (Optional)
|
|
332
|
+
- **requireAll**: boolean (Default: `false`)
|
|
333
|
+
- **redirect**: boolean (Default: `true`)
|
|
334
|
+
- **fallbackPath**: string (Default: `/`)
|
|
335
|
+
- **fallback**: ReactNode (UI while redirecting/denied)
|
|
383
336
|
|
|
384
337
|
#### `<Can>`
|
|
385
338
|
|
|
386
339
|
Structural component for conditional rendering.
|
|
387
340
|
|
|
388
|
-
- **permission**:
|
|
389
|
-
- **
|
|
390
|
-
- **
|
|
341
|
+
- **permission**: string | string[] (Optional)
|
|
342
|
+
- **role**: string | string[] (Optional)
|
|
343
|
+
- **requireAll**: boolean
|
|
344
|
+
- **fallback**: ReactNode
|
|
391
345
|
|
|
392
346
|
### Hooks
|
|
393
347
|
|
|
394
|
-
#### `
|
|
395
|
-
|
|
396
|
-
Access raw state and actions.
|
|
397
|
-
|
|
398
|
-
- `setAuth(authData)`: Accepts simple `string[]`.
|
|
399
|
-
- `switchTenant(id)`: Change active context.
|
|
400
|
-
- `isLoading`: `boolean`.
|
|
401
|
-
- `activeTenantId`: `string | null`.
|
|
402
|
-
|
|
403
|
-
#### `useMatch(handlers)`
|
|
348
|
+
#### `useHasRole(role: string)`
|
|
404
349
|
|
|
405
|
-
|
|
350
|
+
Returns `boolean`. Checks if user has the specific role (or `*`).
|
|
406
351
|
|
|
407
|
-
|
|
408
|
-
- **default**: Fallback function.
|
|
409
|
-
|
|
410
|
-
#### `useHasPermission(permission)`
|
|
352
|
+
#### `useHasPermission(permission: string)`
|
|
411
353
|
|
|
412
354
|
Returns `boolean`. Checks for exact permission match or wildcard `*`.
|
|
413
355
|
|
|
414
|
-
#### `
|
|
415
|
-
|
|
416
|
-
Returns `boolean`. True if user has **at least one** permission.
|
|
417
|
-
|
|
418
|
-
#### `useHasAllPermissions(permissions[])`
|
|
419
|
-
|
|
420
|
-
Returns `boolean`. True only if user has **every single** permission.
|
|
421
|
-
|
|
422
|
-
### Utilities
|
|
423
|
-
|
|
424
|
-
#### `guard(userPerms, requiredPerm, function)`
|
|
425
|
-
|
|
426
|
-
Higher-order function that wraps and protects a function execution.
|
|
356
|
+
#### `useRBAC()`
|
|
427
357
|
|
|
428
|
-
|
|
358
|
+
Access raw state.
|
|
429
359
|
|
|
430
|
-
|
|
360
|
+
- `setAuth(authData)`: Valid inputs:
|
|
361
|
+
- `string[]` (Permissions only)
|
|
362
|
+
- `TenantAuthInput[]` (Full Multi-tenant data)
|
|
363
|
+
- `switchTenant(id)`: Change active context.
|
|
431
364
|
|
|
432
365
|
---
|
|
433
366
|
|
|
434
367
|
## 🛡️ Security & Best Practices
|
|
435
368
|
|
|
436
|
-
> [!IMPORTANT]
|
|
369
|
+
> [!IMPORTANT]
|
|
370
|
+
> **Client-side checks are for User Experience (UX) only.**
|
|
437
371
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
### 1. Server-Side Verification (Universal)
|
|
441
|
-
|
|
442
|
-
Use the universal `checkPermission` helper in Middleware, Server Actions, or API Routes:
|
|
443
|
-
|
|
444
|
-
```tsx
|
|
445
|
-
import { checkPermission } from "rbac-shield";
|
|
446
|
-
// 1. Middleware Example
|
|
447
|
-
export function middleware(req) {
|
|
448
|
-
const permissions = getPermissionsFromCookie(req);
|
|
449
|
-
if (!checkPermission(permissions, "admin:access")) {
|
|
450
|
-
return NextResponse.redirect(new URL("/403", req.url));
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
### 2. Debug Mode
|
|
456
|
-
|
|
457
|
-
Enable debug mode to see permission logs in the console:
|
|
458
|
-
|
|
459
|
-
```tsx
|
|
460
|
-
<RBACProvider debug={true}>{children}</RBACProvider>
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
---
|
|
464
|
-
|
|
465
|
-
## 🐛 Troubleshooting
|
|
466
|
-
|
|
467
|
-
| Issue | Solution |
|
|
468
|
-
| ---------------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
469
|
-
| **Infinite Loading** | Ensure `RBACProvider` wraps your app and `setAuth` is called with valid data. |
|
|
470
|
-
| **Type Errors** | Verify your `Resources` and `Actions` types in `lib/rbac.ts` are exported. |
|
|
471
|
-
| **Hydration Mismatch** | `ProtectedRoute` and `Can` are client components; ensure they are used in client contexts or wrapped properly. |
|
|
372
|
+
Always verify permissions on the server (API Routes, Server Actions, Middleware) using `checkPermission` or `guard`.
|
|
472
373
|
|
|
473
374
|
---
|
|
474
375
|
|
|
475
|
-
## 🤝 Contributing
|
|
476
|
-
|
|
477
|
-
We welcome contributions! Please open an issue or submit a PR on our [GitHub repository](https://github.com/your-repo/rbac-shield).
|
|
478
|
-
|
|
479
376
|
## 📄 License
|
|
480
377
|
|
|
481
378
|
MIT © [Arif Hossain Roman](https://github.com/devnuckles)
|
package/dist/factory.d.mts
CHANGED
|
@@ -5,7 +5,9 @@ type Prettify<T> = {
|
|
|
5
5
|
} & {};
|
|
6
6
|
export interface CanProps<R extends string, A extends string> {
|
|
7
7
|
/** Single permission or array of permissions to check */
|
|
8
|
-
permission
|
|
8
|
+
permission?: PermissionString<R, A> | PermissionString<R, A>[];
|
|
9
|
+
/** Single role or array of roles to check */
|
|
10
|
+
role?: string | string[];
|
|
9
11
|
children: React.ReactNode;
|
|
10
12
|
/** Content to render if permission is denied */
|
|
11
13
|
fallback?: React.ReactNode;
|
|
@@ -15,7 +17,9 @@ export interface CanProps<R extends string, A extends string> {
|
|
|
15
17
|
export interface ProtectedRouteProps<R extends string, A extends string> {
|
|
16
18
|
children: React.ReactNode;
|
|
17
19
|
/** Single permission or array of permissions to check */
|
|
18
|
-
permission
|
|
20
|
+
permission?: PermissionString<R, A> | PermissionString<R, A>[];
|
|
21
|
+
/** Single role or array of roles to check */
|
|
22
|
+
role?: string | string[];
|
|
19
23
|
/** Path to redirect to if access denied (if redirect is true) */
|
|
20
24
|
fallbackPath?: string;
|
|
21
25
|
/** Content to render if access denied (or while redirecting) */
|
|
@@ -40,6 +44,7 @@ export interface RBACFactory<R extends string, A extends string> {
|
|
|
40
44
|
switchTenant: (tenantId: string) => void;
|
|
41
45
|
reset: () => void;
|
|
42
46
|
};
|
|
47
|
+
useHasRole: (role: string) => boolean;
|
|
43
48
|
useHasPermission: (permission: PermissionString<R, A>) => boolean;
|
|
44
49
|
useHasAnyPermission: (permissions: PermissionString<R, A>[]) => boolean;
|
|
45
50
|
useHasAllPermissions: (permissions: PermissionString<R, A>[]) => boolean;
|
package/dist/factory.d.ts
CHANGED
|
@@ -5,7 +5,9 @@ type Prettify<T> = {
|
|
|
5
5
|
} & {};
|
|
6
6
|
export interface CanProps<R extends string, A extends string> {
|
|
7
7
|
/** Single permission or array of permissions to check */
|
|
8
|
-
permission
|
|
8
|
+
permission?: PermissionString<R, A> | PermissionString<R, A>[];
|
|
9
|
+
/** Single role or array of roles to check */
|
|
10
|
+
role?: string | string[];
|
|
9
11
|
children: React.ReactNode;
|
|
10
12
|
/** Content to render if permission is denied */
|
|
11
13
|
fallback?: React.ReactNode;
|
|
@@ -15,7 +17,9 @@ export interface CanProps<R extends string, A extends string> {
|
|
|
15
17
|
export interface ProtectedRouteProps<R extends string, A extends string> {
|
|
16
18
|
children: React.ReactNode;
|
|
17
19
|
/** Single permission or array of permissions to check */
|
|
18
|
-
permission
|
|
20
|
+
permission?: PermissionString<R, A> | PermissionString<R, A>[];
|
|
21
|
+
/** Single role or array of roles to check */
|
|
22
|
+
role?: string | string[];
|
|
19
23
|
/** Path to redirect to if access denied (if redirect is true) */
|
|
20
24
|
fallbackPath?: string;
|
|
21
25
|
/** Content to render if access denied (or while redirecting) */
|
|
@@ -40,6 +44,7 @@ export interface RBACFactory<R extends string, A extends string> {
|
|
|
40
44
|
switchTenant: (tenantId: string) => void;
|
|
41
45
|
reset: () => void;
|
|
42
46
|
};
|
|
47
|
+
useHasRole: (role: string) => boolean;
|
|
43
48
|
useHasPermission: (permission: PermissionString<R, A>) => boolean;
|
|
44
49
|
useHasAnyPermission: (permissions: PermissionString<R, A>[]) => boolean;
|
|
45
50
|
useHasAllPermissions: (permissions: PermissionString<R, A>[]) => boolean;
|
package/dist/factory.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.tsx"],"names":[],"mappings":"AAEA,OAAO,KAON,MAAM,OAAO,CAAC;AAEf,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAavE,KAAK,QAAQ,CAAC,CAAC,IAAI;KAChB,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CACrB,GAAG,EAAE,CAAC;AAEP,MAAM,WAAW,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM;IAC1D,yDAAyD;IACzD,UAAU,EAAE,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.tsx"],"names":[],"mappings":"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"}
|