rbac-shield 0.1.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/LICENSE +21 -0
- package/README.md +299 -0
- package/bin/cli.js +30 -0
- package/bin/init.js +176 -0
- package/bin/templates.js +68 -0
- package/bin/utils.js +117 -0
- package/dist/ProtectedRoute.js +52 -0
- package/dist/ProtectedRoute.mjs +49 -0
- package/dist/checkPermission.js +26 -0
- package/dist/checkPermission.mjs +23 -0
- package/dist/index.js +271 -0
- package/dist/index.mjs +233 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types.js +3 -0
- package/dist/types.mjs +2 -0
- package/package.json +84 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 RBAC Shield Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# π RBAC Shield
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/rbac-shield)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
6
|
+
[](https://www.npmjs.com/package/rbac-shield)
|
|
7
|
+
|
|
8
|
+
**The production-ready, type-safe Role-Based Access Control (RBAC) system for Next.js applications.**
|
|
9
|
+
|
|
10
|
+
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 just works.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## π Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Features](#-features)
|
|
17
|
+
- [Quick Setup (CLI)](#-quick-setup-recommended)
|
|
18
|
+
- [Manual Installation](#-manual-installation)
|
|
19
|
+
- [Quick Start](#-quick-start)
|
|
20
|
+
- [API Reference](#-api-reference)
|
|
21
|
+
- [Advanced Usage](#-advanced-usage)
|
|
22
|
+
- [Security & Best Practices](#-security--best-practices)
|
|
23
|
+
- [Troubleshooting](#-troubleshooting)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## β¨ Features
|
|
28
|
+
|
|
29
|
+
- π― **Type-Safe Permissions**: Full TypeScript support with intelligent autocomplete for your specific resources and actions.
|
|
30
|
+
- π **High Performance**: Optimized with React Context and memoization. Permission checks are < 1ms.
|
|
31
|
+
- π’ **Multi-Tenant Native**: Switch between multiple organizations/roles instantly without page reloads.
|
|
32
|
+
- π‘οΈ **Route Protection**: Declarative client-side guards with automatic handling of loading states and redirects.
|
|
33
|
+
- π **SSR & Hydration Safe**: Built from the ground up for Next.js App Routerβno hydration mismatches.
|
|
34
|
+
- π **Universal Support**: Works seamlessly in **Client Components**, **Server Components**, and **Middleware**.
|
|
35
|
+
- π¦ **Zero Dependencies**: Lightweight (~35KB) and built entirely on standard React APIs.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## π Quick Setup (Recommended)
|
|
40
|
+
|
|
41
|
+
The fastest way to integrate RBAC Shield is with our interactive CLI. It initializes your configuration and handles all boilerplate.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx rbac-shield init
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The CLI will:
|
|
48
|
+
|
|
49
|
+
1. Detect your project type (Next.js/React, TS/JS)
|
|
50
|
+
2. Help you define your resources (e.g., `projects`) and actions (e.g., `create`)
|
|
51
|
+
3. Generate a clean, type-safe `lib/rbac.ts` file configured for your app
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## π¦ Manual Installation
|
|
56
|
+
|
|
57
|
+
If you prefer to set things up yourself:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm install rbac-shield
|
|
61
|
+
# or
|
|
62
|
+
yarn add rbac-shield
|
|
63
|
+
# or
|
|
64
|
+
pnpm add rbac-shield
|
|
65
|
+
# or
|
|
66
|
+
bun add rbac-shield
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Peer Dependencies:**
|
|
70
|
+
Ensure you have peer dependencies installed (standard in Next.js apps):
|
|
71
|
+
`react >= 18.0.0`, `react-dom >= 18.0.0`, `next >= 13.0.0`
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## π Quick Start
|
|
76
|
+
|
|
77
|
+
### 1. Define Your Schema
|
|
78
|
+
|
|
79
|
+
Create a single source of truth for your permissions in `lib/rbac.ts` (or `config/rbac.ts`).
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// lib/rbac.ts
|
|
83
|
+
import { createRBAC } from "rbac-shield";
|
|
84
|
+
|
|
85
|
+
// 1. Define resources (things you secure)
|
|
86
|
+
export type Resources = "projects" | "billing" | "users" | "marketing";
|
|
87
|
+
|
|
88
|
+
// 2. Define actions (what users can do)
|
|
89
|
+
export type Actions = "view" | "create" | "edit" | "delete" | "export";
|
|
90
|
+
|
|
91
|
+
// 3. Create your instances
|
|
92
|
+
export const {
|
|
93
|
+
RBACProvider,
|
|
94
|
+
useStore,
|
|
95
|
+
useHasPermission,
|
|
96
|
+
useHasAnyPermission,
|
|
97
|
+
useHasAllPermissions,
|
|
98
|
+
usePermissions,
|
|
99
|
+
Can,
|
|
100
|
+
ProtectedRoute,
|
|
101
|
+
} = createRBAC<Resources, Actions>();
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 2. Wrap Your App
|
|
105
|
+
|
|
106
|
+
Add the provider to your root layout to enable state management.
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
// app/layout.tsx
|
|
110
|
+
import { RBACProvider } from "@/lib/rbac";
|
|
111
|
+
|
|
112
|
+
export default function RootLayout({
|
|
113
|
+
children,
|
|
114
|
+
}: {
|
|
115
|
+
children: React.ReactNode;
|
|
116
|
+
}) {
|
|
117
|
+
return (
|
|
118
|
+
<html lang="en">
|
|
119
|
+
<body>
|
|
120
|
+
<RBACProvider>{children}</RBACProvider>
|
|
121
|
+
</body>
|
|
122
|
+
</html>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 3. Load Permissions
|
|
128
|
+
|
|
129
|
+
Initialize the system with data from your backend (e.g., after login).
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
// components/AuthProvider.tsx
|
|
133
|
+
"use client";
|
|
134
|
+
import { useEffect } from "react";
|
|
135
|
+
import { useStore } from "@/lib/rbac";
|
|
136
|
+
|
|
137
|
+
export function AuthProvider({
|
|
138
|
+
user,
|
|
139
|
+
children,
|
|
140
|
+
}: {
|
|
141
|
+
user: any;
|
|
142
|
+
children: React.ReactNode;
|
|
143
|
+
}) {
|
|
144
|
+
const { setAuth, switchTenant } = useStore();
|
|
145
|
+
|
|
146
|
+
useEffect(() => {
|
|
147
|
+
if (user) {
|
|
148
|
+
// Load permissions into RBAC Shield
|
|
149
|
+
setAuth([
|
|
150
|
+
{
|
|
151
|
+
tenantId: "team-123",
|
|
152
|
+
permissions: ["projects:view", "projects:edit"],
|
|
153
|
+
},
|
|
154
|
+
]);
|
|
155
|
+
// Activate the context
|
|
156
|
+
switchTenant("team-123");
|
|
157
|
+
}
|
|
158
|
+
}, [user, setAuth, switchTenant]);
|
|
159
|
+
|
|
160
|
+
return <>{children}</>;
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 4. Secure Your App
|
|
165
|
+
|
|
166
|
+
Use the generated hooks and components anywhere.
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
import { ProtectedRoute, Can, useHasPermission } from "@/lib/rbac";
|
|
170
|
+
|
|
171
|
+
export default function ProjectSettings() {
|
|
172
|
+
const canDelete = useHasPermission("projects:delete");
|
|
173
|
+
|
|
174
|
+
return (
|
|
175
|
+
// 1. Protect Example: Redirects to / if missing permission
|
|
176
|
+
<ProtectedRoute requiredPermission="projects:edit" fallbackPath="/">
|
|
177
|
+
<h1>Project Settings</h1>
|
|
178
|
+
|
|
179
|
+
{/* 2. Conditional Render Example */}
|
|
180
|
+
<Can permission="billing:view" fallback={<p>Upgrade to see billing</p>}>
|
|
181
|
+
<BillingWidget />
|
|
182
|
+
</Can>
|
|
183
|
+
|
|
184
|
+
{/* 3. Hook Logic Example */}
|
|
185
|
+
<button disabled={!canDelete}>Delete Project</button>
|
|
186
|
+
</ProtectedRoute>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## π API Reference
|
|
194
|
+
|
|
195
|
+
### `<ProtectedRoute>`
|
|
196
|
+
|
|
197
|
+
A wrapper component that guards an entire route or section.
|
|
198
|
+
|
|
199
|
+
- **requiredPermission**: `Resources:Actions` string (e.g., `billing:view`).
|
|
200
|
+
- **fallbackPath**: (Optional) URL to redirect to if unauthorized. Default: `/`.
|
|
201
|
+
- **fallback**: (Optional) React Node to render instead of redirecting.
|
|
202
|
+
|
|
203
|
+
### `<Can>`
|
|
204
|
+
|
|
205
|
+
A structural component for showing/hiding UI elements.
|
|
206
|
+
|
|
207
|
+
- **permission**: The required permission string.
|
|
208
|
+
- **fallback**: (Optional) UI to show when permission is denied.
|
|
209
|
+
|
|
210
|
+
### `useHasPermission(permission)`
|
|
211
|
+
|
|
212
|
+
Returns `boolean`. Checks for exact permission match or wildcard `*`.
|
|
213
|
+
|
|
214
|
+
### `useHasAnyPermission([perm1, perm2])`
|
|
215
|
+
|
|
216
|
+
Returns `boolean`. True if user has **at least one** of the listed permissions.
|
|
217
|
+
|
|
218
|
+
### `useHasAllPermissions([perm1, perm2])`
|
|
219
|
+
|
|
220
|
+
Returns `boolean`. True only if user has **every single** listed permission.
|
|
221
|
+
|
|
222
|
+
### `useStore()`
|
|
223
|
+
|
|
224
|
+
Access raw state and actions:
|
|
225
|
+
|
|
226
|
+
- `isLoading`: `boolean`
|
|
227
|
+
- `activeTenantId`: `string | null`
|
|
228
|
+
- `switchTenant(id)`: Change active context
|
|
229
|
+
- `setAuth(authData)`: Load permission data
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## οΏ½ Security & Best Practices
|
|
234
|
+
|
|
235
|
+
> [!IMPORTANT] > **Client-side checks are for User Experience (UX) only.**
|
|
236
|
+
|
|
237
|
+
RBAC Shield controls what the user _sees_ in the browser, but it cannot stop a determined attacker from crafting API requests manually.
|
|
238
|
+
|
|
239
|
+
### 1. Server-Side Verification (Universal)
|
|
240
|
+
|
|
241
|
+
Use the universal `checkPermission` helper in Middleware, Server Actions, or API Routes:
|
|
242
|
+
|
|
243
|
+
```tsx
|
|
244
|
+
import { checkPermission } from "rbac-shield";
|
|
245
|
+
// 1. Middleware Example
|
|
246
|
+
export function middleware(req) {
|
|
247
|
+
const permissions = getPermissionsFromCookie(req);
|
|
248
|
+
if (!checkPermission(permissions, "admin:access")) {
|
|
249
|
+
return NextResponse.redirect(new URL("/403", req.url));
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// 2. Server Action Example
|
|
254
|
+
export async function deleteProject() {
|
|
255
|
+
const user = await getCurrentUser();
|
|
256
|
+
if (!checkPermission(user.permissions, "projects:delete")) {
|
|
257
|
+
throw new Error("Unauthorized");
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### 2. Debug Mode
|
|
263
|
+
|
|
264
|
+
Troubleshooting permissions is easier with debug mode enabled.
|
|
265
|
+
|
|
266
|
+
```tsx
|
|
267
|
+
<RBACProvider debug={true}>{children}</RBACProvider>
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
This logs helpful messages to the console:
|
|
271
|
+
|
|
272
|
+
- `[RBAC Shield] Permissions loaded for tenants: ['org_1']`
|
|
273
|
+
- `[RBAC Shield] Denied: Required 'billing:edit', User has ['projects:view']`
|
|
274
|
+
|
|
275
|
+
### 3. Production Checklist
|
|
276
|
+
|
|
277
|
+
1. **API Validation**: Every API route and Server Action MUST verify permissions independently on the server using `checkPermission()`.
|
|
278
|
+
2. **Middleware**: Use Next.js Middleware to protect route segments (`/admin/*`) before they even render.
|
|
279
|
+
3. **RBAC Shield**: Use this library's components (Client-Side) to hide UI elements for a smooth experience.
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## π Troubleshooting
|
|
284
|
+
|
|
285
|
+
| Issue | Solution |
|
|
286
|
+
| ---------------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
287
|
+
| **Infinite Loading** | Ensure `RBACProvider` wraps your app and `setAuth` is called with valid data. |
|
|
288
|
+
| **Type Errors** | Verify your `Resources` and `Actions` types in `lib/rbac.ts` are exported. |
|
|
289
|
+
| **Hydration Mismatch** | `ProtectedRoute` and `Can` are client components; ensure they are used in client contexts or wrapped properly. |
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## π€ Contributing
|
|
294
|
+
|
|
295
|
+
We welcome contributions! Please open an issue or submit a PR on our [GitHub repository](https://github.com/your-repo/rbac-shield).
|
|
296
|
+
|
|
297
|
+
## π License
|
|
298
|
+
|
|
299
|
+
MIT Β© [Arif Hossain Roman](https://github.com/your-username)
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { program } = require('commander');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const packageJson = require('../package.json');
|
|
6
|
+
|
|
7
|
+
program
|
|
8
|
+
.name('rbac-shield')
|
|
9
|
+
.description('Interactive CLI for RBAC Shield setup')
|
|
10
|
+
.version(packageJson.version);
|
|
11
|
+
|
|
12
|
+
program
|
|
13
|
+
.command('init')
|
|
14
|
+
.description('Initialize RBAC Shield in your project')
|
|
15
|
+
.action(async () => {
|
|
16
|
+
try {
|
|
17
|
+
const init = require('./init');
|
|
18
|
+
await init();
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.error(chalk.red('Error:'), error.message);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Show help if no command provided
|
|
26
|
+
if (!process.argv.slice(2).length) {
|
|
27
|
+
program.outputHelp();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
program.parse(process.argv);
|
package/bin/init.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
const prompts = require('prompts');
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
const { getTemplate, TEMPLATES } = require('./templates');
|
|
7
|
+
const {
|
|
8
|
+
detectFramework,
|
|
9
|
+
isTypeScriptProject,
|
|
10
|
+
ensureDirectory,
|
|
11
|
+
fileExists,
|
|
12
|
+
showNextSteps
|
|
13
|
+
} = require('./utils');
|
|
14
|
+
|
|
15
|
+
async function init() {
|
|
16
|
+
console.log(chalk.cyan.bold('\nπ RBAC Shield - Interactive Setup'));
|
|
17
|
+
console.log(chalk.cyan('β'.repeat(40)) + '\n');
|
|
18
|
+
|
|
19
|
+
// Detect project type
|
|
20
|
+
const framework = detectFramework();
|
|
21
|
+
const hasTypeScript = isTypeScriptProject();
|
|
22
|
+
|
|
23
|
+
if (framework) {
|
|
24
|
+
console.log(chalk.green(`β Detected ${framework} project\n`));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Check if rbac-shield is already installed
|
|
28
|
+
const isInstalled = fileExists(path.join(process.cwd(), 'node_modules', 'rbac-shield'));
|
|
29
|
+
|
|
30
|
+
// Prompt for language
|
|
31
|
+
const { language } = await prompts({
|
|
32
|
+
type: 'select',
|
|
33
|
+
name: 'language',
|
|
34
|
+
message: 'Use TypeScript or JavaScript?',
|
|
35
|
+
choices: [
|
|
36
|
+
{ title: 'TypeScript', value: 'typescript', selected: hasTypeScript },
|
|
37
|
+
{ title: 'JavaScript', value: 'javascript' }
|
|
38
|
+
],
|
|
39
|
+
initial: hasTypeScript ? 0 : 1
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (!language) {
|
|
43
|
+
console.log(chalk.yellow('\nβ Setup cancelled'));
|
|
44
|
+
process.exit(0);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Prompt for template
|
|
48
|
+
const { template } = await prompts({
|
|
49
|
+
type: 'select',
|
|
50
|
+
name: 'template',
|
|
51
|
+
message: 'Choose a template:',
|
|
52
|
+
choices: [
|
|
53
|
+
{
|
|
54
|
+
title: 'Basic (projects, users, settings)',
|
|
55
|
+
value: 'basic',
|
|
56
|
+
description: 'General purpose RBAC setup'
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
title: 'E-commerce (products, orders, customers)',
|
|
60
|
+
value: 'ecommerce',
|
|
61
|
+
description: 'For online stores and marketplaces'
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
title: 'SaaS (workspaces, billing, teams)',
|
|
65
|
+
value: 'saas',
|
|
66
|
+
description: 'For multi-tenant SaaS applications'
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
title: 'Custom (define your own)',
|
|
70
|
+
value: 'custom',
|
|
71
|
+
description: 'Specify your own resources and actions'
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
initial: 0
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
if (!template) {
|
|
78
|
+
console.log(chalk.yellow('\nβ Setup cancelled'));
|
|
79
|
+
process.exit(0);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let resources, actions;
|
|
83
|
+
|
|
84
|
+
if (template === 'custom') {
|
|
85
|
+
// Prompt for custom resources and actions
|
|
86
|
+
const customConfig = await prompts([
|
|
87
|
+
{
|
|
88
|
+
type: 'text',
|
|
89
|
+
name: 'resources',
|
|
90
|
+
message: 'Enter resource types (comma-separated):',
|
|
91
|
+
initial: 'projects, users, settings',
|
|
92
|
+
validate: value => value.trim().length > 0 || 'Please enter at least one resource'
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
type: 'text',
|
|
96
|
+
name: 'actions',
|
|
97
|
+
message: 'Enter action types (comma-separated):',
|
|
98
|
+
initial: 'view, create, edit, delete',
|
|
99
|
+
validate: value => value.trim().length > 0 || 'Please enter at least one action'
|
|
100
|
+
}
|
|
101
|
+
]);
|
|
102
|
+
|
|
103
|
+
if (!customConfig.resources || !customConfig.actions) {
|
|
104
|
+
console.log(chalk.yellow('\nβ Setup cancelled'));
|
|
105
|
+
process.exit(0);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
resources = customConfig.resources.split(',').map(r => r.trim()).filter(Boolean);
|
|
109
|
+
actions = customConfig.actions.split(',').map(a => a.trim()).filter(Boolean);
|
|
110
|
+
} else {
|
|
111
|
+
const templateData = TEMPLATES[template];
|
|
112
|
+
resources = templateData.resources;
|
|
113
|
+
actions = templateData.actions;
|
|
114
|
+
|
|
115
|
+
// Confirm template
|
|
116
|
+
const { confirmed } = await prompts({
|
|
117
|
+
type: 'confirm',
|
|
118
|
+
name: 'confirmed',
|
|
119
|
+
message: `Use resources: ${resources.join(', ')} and actions: ${actions.join(', ')}?`,
|
|
120
|
+
initial: true
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
if (!confirmed) {
|
|
124
|
+
console.log(chalk.yellow('\nβ Setup cancelled'));
|
|
125
|
+
process.exit(0);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log('');
|
|
130
|
+
|
|
131
|
+
// Install rbac-shield if not installed
|
|
132
|
+
if (!isInstalled) {
|
|
133
|
+
console.log(chalk.blue('π¦ Installing rbac-shield...'));
|
|
134
|
+
try {
|
|
135
|
+
execSync('npm install rbac-shield', { stdio: 'inherit' });
|
|
136
|
+
console.log(chalk.green('β rbac-shield installed\n'));
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.error(chalk.red('β Failed to install rbac-shield'));
|
|
139
|
+
console.error(chalk.yellow('Please install manually: npm install rbac-shield'));
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Create lib directory
|
|
145
|
+
const libDir = path.join(process.cwd(), 'lib');
|
|
146
|
+
ensureDirectory(libDir);
|
|
147
|
+
|
|
148
|
+
// Generate config file
|
|
149
|
+
const ext = language === 'typescript' ? 'ts' : 'js';
|
|
150
|
+
const configPath = path.join(libDir, `rbac.${ext}`);
|
|
151
|
+
|
|
152
|
+
if (fileExists(configPath)) {
|
|
153
|
+
const { overwrite } = await prompts({
|
|
154
|
+
type: 'confirm',
|
|
155
|
+
name: 'overwrite',
|
|
156
|
+
message: `${configPath} already exists. Overwrite?`,
|
|
157
|
+
initial: false
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
if (!overwrite) {
|
|
161
|
+
console.log(chalk.yellow('\nβ Setup cancelled'));
|
|
162
|
+
process.exit(0);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const configContent = getTemplate(template, language, resources, actions);
|
|
167
|
+
fs.writeFileSync(configPath, configContent, 'utf8');
|
|
168
|
+
|
|
169
|
+
console.log(chalk.green(`β Created lib/rbac.${ext}`));
|
|
170
|
+
console.log(chalk.green('β Setup complete!\n'));
|
|
171
|
+
|
|
172
|
+
// Show next steps
|
|
173
|
+
showNextSteps(framework, language);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
module.exports = init;
|
package/bin/templates.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const TEMPLATES = {
|
|
2
|
+
basic: {
|
|
3
|
+
resources: ['projects', 'users', 'settings'],
|
|
4
|
+
actions: ['view', 'create', 'edit', 'delete']
|
|
5
|
+
},
|
|
6
|
+
ecommerce: {
|
|
7
|
+
resources: ['products', 'orders', 'customers', 'inventory'],
|
|
8
|
+
actions: ['view', 'create', 'edit', 'delete', 'manage']
|
|
9
|
+
},
|
|
10
|
+
saas: {
|
|
11
|
+
resources: ['workspaces', 'billing', 'teams', 'api-keys'],
|
|
12
|
+
actions: ['view', 'create', 'edit', 'delete', 'manage']
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function getTemplate(templateName, language, resources, actions) {
|
|
17
|
+
const isTypeScript = language === 'typescript';
|
|
18
|
+
|
|
19
|
+
const resourcesType = resources.map(r => `'${r}'`).join(' | ');
|
|
20
|
+
const actionsType = actions.map(a => `'${a}'`).join(' | ');
|
|
21
|
+
|
|
22
|
+
if (isTypeScript) {
|
|
23
|
+
return `import { createRBAC } from 'rbac-shield';
|
|
24
|
+
|
|
25
|
+
// Define your application's resources
|
|
26
|
+
export type Resources = ${resourcesType};
|
|
27
|
+
|
|
28
|
+
// Define your application's actions
|
|
29
|
+
export type Actions = ${actionsType};
|
|
30
|
+
|
|
31
|
+
// Initialize RBAC with your types
|
|
32
|
+
export const {
|
|
33
|
+
RBACProvider,
|
|
34
|
+
useStore,
|
|
35
|
+
useHasPermission,
|
|
36
|
+
useHasAnyPermission,
|
|
37
|
+
useHasAllPermissions,
|
|
38
|
+
usePermissions,
|
|
39
|
+
Can,
|
|
40
|
+
RBACErrorBoundary,
|
|
41
|
+
ProtectedRoute,
|
|
42
|
+
} = createRBAC<Resources, Actions>();
|
|
43
|
+
`;
|
|
44
|
+
} else {
|
|
45
|
+
return `import { createRBAC } from 'rbac-shield';
|
|
46
|
+
|
|
47
|
+
// Initialize RBAC
|
|
48
|
+
// Resources: ${resources.join(', ')}
|
|
49
|
+
// Actions: ${actions.join(', ')}
|
|
50
|
+
export const {
|
|
51
|
+
RBACProvider,
|
|
52
|
+
useStore,
|
|
53
|
+
useHasPermission,
|
|
54
|
+
useHasAnyPermission,
|
|
55
|
+
useHasAllPermissions,
|
|
56
|
+
usePermissions,
|
|
57
|
+
Can,
|
|
58
|
+
RBACErrorBoundary,
|
|
59
|
+
ProtectedRoute,
|
|
60
|
+
} = createRBAC();
|
|
61
|
+
`;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = {
|
|
66
|
+
TEMPLATES,
|
|
67
|
+
getTemplate
|
|
68
|
+
};
|