@krutai/rbac 0.1.1

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.
@@ -0,0 +1,140 @@
1
+ # AI Reference — @krutai/rbac
2
+
3
+ ## Package Overview
4
+ - **Name**: `@krutai/rbac`
5
+ - **Version**: `0.1.1`
6
+ - **Purpose**: Role-Based Access Control (RBAC) library for KrutAI
7
+ - **Entry**: `src/index.ts` → `dist/index.{js,mjs,d.ts}`
8
+ - **Build**: `tsup` (CJS + ESM dual output, `krutai` is external peer dep)
9
+
10
+ ## Dependency Architecture
11
+
12
+ ```
13
+ @krutai/rbac@0.1.1
14
+ ├── peerDependency: krutai >=0.1.2 ← auto-installed, provides API validation
15
+ └── peerDependency: @krutai/auth >=0.1.0 ← optional
16
+ ```
17
+
18
+ > **Important for AI**: The validator (`validateApiKeyFormat`, `ApiKeyValidationError`, etc.) is NOT defined in this package. It is imported from `krutai` and re-exported. Do NOT add a local `validator.ts` here.
19
+
20
+ ## File Structure
21
+ ```
22
+ packages/rbac/
23
+ ├── src/
24
+ │ ├── index.ts # Barrel export (all public API)
25
+ │ ├── types.ts # Core TypeScript interfaces and types
26
+ │ ├── errors.ts # Custom error classes
27
+ │ ├── role.ts # Role/permission helpers + pre-built roles
28
+ │ ├── rbac.ts # RBACManager class (core engine)
29
+ │ └── guards.ts # Guard factories + Express/Next.js middleware
30
+ ├── package.json
31
+ ├── tsconfig.json
32
+ └── tsup.config.ts
33
+ ```
34
+
35
+ ## Key Exports
36
+
37
+ ### Classes
38
+ - `RBACManager` — main RBAC engine
39
+
40
+ ### Types
41
+ - `Permission` — `string` alias for `"resource:action"` format
42
+ - `Role` — `{ name, permissions, inherits?, description? }`
43
+ - `RBACConfig` — `{ roles, defaultRole? }`
44
+ - `RBACContext` — `{ userId?, roles, metadata? }`
45
+ - `CheckOptions` — `{ requireAll?: boolean }`
46
+ - `PermissionCheckResult` — `{ granted, permissions, roles, missing? }`
47
+ - `GuardFn` — `(context: RBACContext) => boolean`
48
+
49
+ ### Helpers
50
+ - `defineRole(config)` — type-safe role definition
51
+ - `definePermission(resource, action)` — creates `"resource:action"` string
52
+ - `crudPermissions(resource)` — creates `[create, read, update, delete]` permissions
53
+ - `wildcardPermission(resource)` — creates `"resource:*"`
54
+
55
+ ### Pre-built Roles
56
+ - `GUEST_ROLE`, `USER_ROLE`, `MODERATOR_ROLE`, `ADMIN_ROLE`, `SUPER_ADMIN_ROLE`
57
+ - `DEFAULT_ROLES` — array of all five in hierarchy order
58
+
59
+ ### Guard Factories
60
+ - `createPermissionGuard(rbac, permission)` → `GuardFn`
61
+ - `createRoleGuard(rbac, roleName)` → `GuardFn`
62
+ - `createAllPermissionsGuard(rbac, permissions[])` → `GuardFn`
63
+ - `createAnyPermissionGuard(rbac, permissions[])` → `GuardFn`
64
+
65
+ ### Middleware
66
+ - `requirePermission(rbac, permission)` — Express/Next.js middleware
67
+ - `requireRole(rbac, roleName)` — Express/Next.js middleware
68
+ - `withPermission(rbac, permission, handler, onDenied?)` — handler wrapper
69
+
70
+ ### Errors
71
+ - `RBACError` — base class
72
+ - `PermissionDeniedError(permission, roles)` — access denied
73
+ - `RoleNotFoundError(roleName)` — role not in registry
74
+ - `CircularInheritanceError(chain)` — circular role inheritance
75
+
76
+ ### Validator Re-exports (from `krutai`)
77
+ ```typescript
78
+ // These are re-exported from krutai — NOT defined here
79
+ export { validateApiKeyFormat, validateApiKeyWithService, createApiKeyChecker, ApiKeyValidationError } from 'krutai';
80
+ ```
81
+
82
+ ## RBACManager API Summary
83
+
84
+ ```typescript
85
+ // Construction
86
+ new RBACManager({ roles: Role[], defaultRole?: string })
87
+
88
+ // Role management
89
+ .addRole(role: Role): void
90
+ .removeRole(name: string): void // throws RoleNotFoundError
91
+ .getRole(name: string): Role | undefined
92
+ .getAllRoles(): Role[]
93
+
94
+ // Permission resolution
95
+ .getPermissionsForRole(name: string): Set<Permission> // throws RoleNotFoundError
96
+ .getPermissionsForRoles(names: string[]): Set<Permission>
97
+
98
+ // Checks
99
+ .can(ctx, permission): boolean
100
+ .cannot(ctx, permission): boolean
101
+ .hasPermission(ctx, permission, opts?): boolean
102
+ .hasAnyPermission(ctx, permissions[]): boolean
103
+ .hasAllPermissions(ctx, permissions[]): boolean
104
+ .hasRole(ctx, roleName): boolean
105
+ .hasAnyRole(ctx, roleNames[]): boolean
106
+ .check(ctx, permissions[], opts?): PermissionCheckResult
107
+ ```
108
+
109
+ ## Wildcard Permission Rules
110
+ - `*:*` or `*` → matches any permission
111
+ - `posts:*` → matches any action on `posts` resource
112
+ - `*:read` → matches `read` on any resource
113
+
114
+ ## Role Inheritance
115
+ Permissions are resolved recursively. Cycles throw `CircularInheritanceError`.
116
+ Results are cached per role name for performance.
117
+
118
+ ## Middleware Contract
119
+ `requirePermission` / `requireRole` expect `req.rbacContext: RBACContext` to be set
120
+ by a preceding auth middleware. Returns 401 if missing, 403 if denied.
121
+
122
+ ## tsup Configuration Notes
123
+ - `krutai` → external (peer dep, NOT bundled — do NOT add to `noExternal`)
124
+ - No other special external/noExternal rules
125
+
126
+ ## Important Notes
127
+
128
+ 1. **Validator lives in `krutai`**: Never add a local `validator.ts` — import from `krutai`
129
+ 2. **`krutai` must be external in tsup**: Do NOT add it to `noExternal`
130
+ 3. **`krutai` in devDependencies**: Needed for local TypeScript compilation during development
131
+
132
+ ## Related Packages
133
+
134
+ - `krutai` — Core utilities and API validation (peer dep)
135
+ - `@krutai/auth` — Authentication with Better Auth (optional peer dep)
136
+
137
+ ## Links
138
+
139
+ - GitHub: https://github.com/AccountantAIOrg/krut_packages
140
+ - npm: https://www.npmjs.com/package/@krutai/rbac
package/README.md ADDED
@@ -0,0 +1,216 @@
1
+ # @krutai/rbac
2
+
3
+ > Role-Based Access Control (RBAC) for KrutAI — type-safe, inheritance-aware, framework-agnostic.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@krutai/rbac)](https://www.npmjs.com/package/@krutai/rbac)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Features
9
+
10
+ - ✅ **Type-safe** — full TypeScript with strict types
11
+ - 🔗 **Role inheritance** — roles can inherit permissions from parent roles
12
+ - 🃏 **Wildcard permissions** — `posts:*` or `*:*` for broad grants
13
+ - 🏗️ **Pre-built roles** — `guest`, `user`, `moderator`, `admin`, `super_admin`
14
+ - 🛡️ **Guard helpers** — framework-agnostic guard factories
15
+ - 🔌 **Middleware** — Express / Next.js-compatible middleware factories
16
+ - 🚨 **Descriptive errors** — `PermissionDeniedError`, `RoleNotFoundError`, `CircularInheritanceError`
17
+ - 🔑 **API Key Validation** — re-exported from `krutai` (auto-installed as peer dep)
18
+
19
+ ---
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install @krutai/rbac
25
+ # or
26
+ bun add @krutai/rbac
27
+ ```
28
+
29
+ > **Note:** `krutai` is automatically installed as a peer dependency — no extra steps needed.
30
+
31
+ ---
32
+
33
+ ## Quick Start
34
+
35
+ ```typescript
36
+ import { RBACManager, defineRole, definePermission } from '@krutai/rbac';
37
+
38
+ // 1. Define your roles
39
+ const rbac = new RBACManager({
40
+ roles: [
41
+ defineRole({
42
+ name: 'user',
43
+ permissions: ['posts:read', 'posts:create'],
44
+ }),
45
+ defineRole({
46
+ name: 'admin',
47
+ permissions: ['posts:delete', 'users:manage'],
48
+ inherits: ['user'], // inherits all user permissions
49
+ }),
50
+ ],
51
+ defaultRole: 'user',
52
+ });
53
+
54
+ // 2. Build a context from your auth session
55
+ const ctx = { userId: 'u_123', roles: ['admin'] };
56
+
57
+ // 3. Check permissions
58
+ rbac.can(ctx, 'posts:read'); // true (inherited from user)
59
+ rbac.can(ctx, 'posts:delete'); // true
60
+ rbac.can(ctx, 'billing:read'); // false
61
+ rbac.cannot(ctx, 'billing:read'); // true
62
+ ```
63
+
64
+ ---
65
+
66
+ ## Core API
67
+
68
+ ### `RBACManager`
69
+
70
+ ```typescript
71
+ const rbac = new RBACManager(config: RBACConfig);
72
+ ```
73
+
74
+ #### Role Management
75
+
76
+ | Method | Description |
77
+ |--------|-------------|
78
+ | `addRole(role)` | Register a new role |
79
+ | `removeRole(name)` | Remove a role by name |
80
+ | `getRole(name)` | Get a role definition |
81
+ | `getAllRoles()` | List all registered roles |
82
+
83
+ #### Permission Resolution
84
+
85
+ | Method | Description |
86
+ |--------|-------------|
87
+ | `getPermissionsForRole(name)` | Resolved `Set<Permission>` for a role (includes inherited) |
88
+ | `getPermissionsForRoles(names[])` | Union of permissions across multiple roles |
89
+
90
+ #### Permission Checks
91
+
92
+ | Method | Description |
93
+ |--------|-------------|
94
+ | `can(ctx, permission)` | Returns `true` if context has the permission |
95
+ | `cannot(ctx, permission)` | Inverse of `can` |
96
+ | `hasPermission(ctx, permission)` | Same as `can` |
97
+ | `hasAnyPermission(ctx, permissions[])` | True if context has ≥1 permission |
98
+ | `hasAllPermissions(ctx, permissions[])` | True if context has all permissions |
99
+ | `hasRole(ctx, roleName)` | True if context has the role |
100
+ | `hasAnyRole(ctx, roleNames[])` | True if context has ≥1 role |
101
+ | `check(ctx, permissions[], opts?)` | Detailed result with `granted`, `missing` |
102
+
103
+ ---
104
+
105
+ ## Permission Strings
106
+
107
+ Permissions follow the `resource:action` convention:
108
+
109
+ ```typescript
110
+ import { definePermission, crudPermissions, wildcardPermission } from '@krutai/rbac';
111
+
112
+ definePermission('posts', 'read') // "posts:read"
113
+ crudPermissions('posts') // ["posts:create", "posts:read", "posts:update", "posts:delete"]
114
+ wildcardPermission('posts') // "posts:*"
115
+ wildcardPermission('*') // "*:*" — grants everything
116
+ ```
117
+
118
+ ---
119
+
120
+ ## Role Inheritance
121
+
122
+ ```typescript
123
+ const rbac = new RBACManager({
124
+ roles: [
125
+ { name: 'guest', permissions: ['public:read'] },
126
+ { name: 'user', permissions: ['profile:read'], inherits: ['guest'] },
127
+ { name: 'moderator', permissions: ['posts:delete'], inherits: ['user'] },
128
+ { name: 'admin', permissions: ['users:manage'], inherits: ['moderator'] },
129
+ ],
130
+ });
131
+
132
+ const ctx = { roles: ['moderator'] };
133
+ rbac.can(ctx, 'public:read'); // true (guest → user → moderator)
134
+ rbac.can(ctx, 'profile:read'); // true (user → moderator)
135
+ rbac.can(ctx, 'posts:delete'); // true
136
+ rbac.can(ctx, 'users:manage'); // false (admin only)
137
+ ```
138
+
139
+ ---
140
+
141
+ ## Pre-built Roles
142
+
143
+ ```typescript
144
+ import { DEFAULT_ROLES, ADMIN_ROLE, SUPER_ADMIN_ROLE } from '@krutai/rbac';
145
+
146
+ const rbac = new RBACManager({ roles: DEFAULT_ROLES });
147
+ // Includes: guest, user, moderator, admin, super_admin
148
+ ```
149
+
150
+ ---
151
+
152
+ ## Guard Helpers
153
+
154
+ ```typescript
155
+ import { createPermissionGuard, createRoleGuard } from '@krutai/rbac';
156
+
157
+ const canDeletePosts = createPermissionGuard(rbac, 'posts:delete');
158
+ const isAdmin = createRoleGuard(rbac, 'admin');
159
+
160
+ canDeletePosts(ctx); // boolean
161
+ isAdmin(ctx); // boolean
162
+ ```
163
+
164
+ ---
165
+
166
+ ## Express / Next.js Middleware
167
+
168
+ ```typescript
169
+ import { requirePermission, requireRole } from '@krutai/rbac';
170
+
171
+ // Attach rbacContext in your auth middleware first:
172
+ app.use((req, res, next) => {
173
+ req.rbacContext = { userId: req.user.id, roles: req.user.roles };
174
+ next();
175
+ });
176
+
177
+ // Then protect routes:
178
+ app.delete('/posts/:id', requirePermission(rbac, 'posts:delete'), deleteHandler);
179
+ app.get('/admin', requireRole(rbac, 'admin'), adminHandler);
180
+ ```
181
+
182
+ ---
183
+
184
+ ## Error Handling
185
+
186
+ ```typescript
187
+ import { PermissionDeniedError, RoleNotFoundError } from '@krutai/rbac';
188
+
189
+ try {
190
+ if (rbac.cannot(ctx, 'posts:delete')) {
191
+ throw new PermissionDeniedError('posts:delete', ctx.roles);
192
+ }
193
+ } catch (err) {
194
+ if (err instanceof PermissionDeniedError) {
195
+ console.error(err.message); // "Permission denied: ..."
196
+ console.error(err.permission); // "posts:delete"
197
+ console.error(err.roles); // ["user"]
198
+ }
199
+ }
200
+ ```
201
+
202
+ ---
203
+
204
+ ## API Key Validation
205
+
206
+ `@krutai/rbac` re-exports the API key validator from `krutai`:
207
+
208
+ ```typescript
209
+ import { validateApiKeyFormat, ApiKeyValidationError } from '@krutai/rbac';
210
+ ```
211
+
212
+ ---
213
+
214
+ ## License
215
+
216
+ MIT © KrutAI