@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,390 @@
1
+ export { ApiKeyValidationError, createApiKeyChecker, validateApiKeyFormat, validateApiKeyWithService } from 'krutai';
2
+
3
+ /**
4
+ * Core type definitions for @krutai/rbac
5
+ */
6
+ /**
7
+ * A permission string in the format "resource:action"
8
+ * Examples: "posts:read", "users:delete", "admin:manage"
9
+ */
10
+ type Permission = string;
11
+ /**
12
+ * A role definition with its associated permissions and optional inheritance
13
+ */
14
+ interface Role {
15
+ /** Unique name for this role */
16
+ name: string;
17
+ /** List of permissions granted to this role */
18
+ permissions: Permission[];
19
+ /** Names of roles whose permissions this role inherits */
20
+ inherits?: string[];
21
+ /** Human-readable description of this role */
22
+ description?: string;
23
+ }
24
+ /**
25
+ * Configuration for the RBACManager
26
+ */
27
+ interface RBACConfig {
28
+ /** Initial set of roles to register */
29
+ roles: Role[];
30
+ /** Name of the role assigned to users with no explicit role */
31
+ defaultRole?: string;
32
+ }
33
+ /**
34
+ * Options for permission checks
35
+ */
36
+ interface CheckOptions {
37
+ /**
38
+ * When checking multiple permissions:
39
+ * - true → user must have ALL permissions (AND)
40
+ * - false → user must have AT LEAST ONE permission (OR)
41
+ * @default false
42
+ */
43
+ requireAll?: boolean;
44
+ }
45
+ /**
46
+ * The context object representing the current user/request being checked
47
+ */
48
+ interface RBACContext {
49
+ /** Optional identifier for the user */
50
+ userId?: string;
51
+ /** List of role names assigned to this user */
52
+ roles: string[];
53
+ /** Optional arbitrary metadata for custom logic */
54
+ metadata?: Record<string, unknown>;
55
+ }
56
+ /**
57
+ * Result of a permission check with additional details
58
+ */
59
+ interface PermissionCheckResult {
60
+ /** Whether the check passed */
61
+ granted: boolean;
62
+ /** The permission(s) that were checked */
63
+ permissions: Permission[];
64
+ /** The roles that were evaluated */
65
+ roles: string[];
66
+ /** Which permissions were missing (if denied) */
67
+ missing?: Permission[];
68
+ }
69
+ /**
70
+ * A guard function that takes a context and returns whether access is granted
71
+ */
72
+ type GuardFn = (context: RBACContext) => boolean;
73
+ /**
74
+ * A middleware-style handler function
75
+ */
76
+ type HandlerFn<TContext = unknown, TResult = unknown> = (context: TContext) => TResult | Promise<TResult>;
77
+ /**
78
+ * A middleware-style deny handler
79
+ */
80
+ type DenyHandlerFn<TContext = unknown, TResult = unknown> = (context: TContext, permission: Permission) => TResult | Promise<TResult>;
81
+
82
+ /**
83
+ * RBACManager — core class for role-based access control
84
+ */
85
+
86
+ /**
87
+ * The main RBAC engine. Manages roles, resolves inheritance chains,
88
+ * and evaluates permission checks against a user context.
89
+ *
90
+ * @example
91
+ * const rbac = new RBACManager({
92
+ * roles: [
93
+ * { name: 'user', permissions: ['posts:read'] },
94
+ * { name: 'admin', permissions: ['posts:delete'], inherits: ['user'] },
95
+ * ],
96
+ * });
97
+ *
98
+ * const ctx = { roles: ['admin'] };
99
+ * rbac.can(ctx, 'posts:read'); // true (inherited from user)
100
+ * rbac.can(ctx, 'posts:delete'); // true
101
+ * rbac.can(ctx, 'users:manage'); // false
102
+ */
103
+ declare class RBACManager {
104
+ private readonly roles;
105
+ private readonly defaultRole?;
106
+ /** Cache of resolved permissions per role to avoid repeated traversal */
107
+ private readonly permissionCache;
108
+ constructor(config: RBACConfig);
109
+ /**
110
+ * Register a new role. Clears the permission cache.
111
+ */
112
+ addRole(role: Role): void;
113
+ /**
114
+ * Remove a role by name. Clears the permission cache.
115
+ * @throws {RoleNotFoundError} if the role does not exist
116
+ */
117
+ removeRole(name: string): void;
118
+ /**
119
+ * Retrieve a role definition by name.
120
+ */
121
+ getRole(name: string): Role | undefined;
122
+ /**
123
+ * Returns all registered roles as an array.
124
+ */
125
+ getAllRoles(): Role[];
126
+ /**
127
+ * Resolves the full set of permissions for a single role,
128
+ * including all inherited permissions (with cycle detection).
129
+ *
130
+ * @throws {RoleNotFoundError} if the role does not exist
131
+ * @throws {CircularInheritanceError} if a circular inheritance chain is detected
132
+ */
133
+ getPermissionsForRole(roleName: string): Set<Permission>;
134
+ /**
135
+ * Resolves the union of all permissions across multiple roles.
136
+ */
137
+ getPermissionsForRoles(roleNames: string[]): Set<Permission>;
138
+ /**
139
+ * Check whether the context has a specific permission.
140
+ * Supports wildcard permissions (e.g. "*:*" or "posts:*").
141
+ */
142
+ hasPermission(context: RBACContext, permission: Permission, _opts?: CheckOptions): boolean;
143
+ /**
144
+ * Check whether the context has AT LEAST ONE of the given permissions.
145
+ */
146
+ hasAnyPermission(context: RBACContext, permissions: Permission[]): boolean;
147
+ /**
148
+ * Check whether the context has ALL of the given permissions.
149
+ */
150
+ hasAllPermissions(context: RBACContext, permissions: Permission[]): boolean;
151
+ /**
152
+ * Check whether the context has a specific role assigned.
153
+ */
154
+ hasRole(context: RBACContext, roleName: string): boolean;
155
+ /**
156
+ * Check whether the context has AT LEAST ONE of the given roles.
157
+ */
158
+ hasAnyRole(context: RBACContext, roleNames: string[]): boolean;
159
+ /**
160
+ * Alias for `hasPermission`. Reads naturally in conditional expressions.
161
+ *
162
+ * @example
163
+ * if (rbac.can(ctx, 'posts:delete')) { ... }
164
+ */
165
+ can(context: RBACContext, permission: Permission): boolean;
166
+ /**
167
+ * Inverse of `can`. Reads naturally in guard expressions.
168
+ *
169
+ * @example
170
+ * if (rbac.cannot(ctx, 'posts:delete')) throw new PermissionDeniedError(...);
171
+ */
172
+ cannot(context: RBACContext, permission: Permission): boolean;
173
+ /**
174
+ * Detailed permission check that returns a result object with context.
175
+ */
176
+ check(context: RBACContext, permissions: Permission[], opts?: CheckOptions): PermissionCheckResult;
177
+ private resolveContextRoles;
178
+ private resolvePermissions;
179
+ /**
180
+ * Matches a required permission against the user's permission set,
181
+ * supporting wildcard segments ("*").
182
+ *
183
+ * Wildcard rules:
184
+ * - "*:*" matches everything
185
+ * - "posts:*" matches any action on "posts"
186
+ * - "*:read" matches "read" on any resource
187
+ */
188
+ private matchPermission;
189
+ }
190
+
191
+ /**
192
+ * Role and permission definition helpers for @krutai/rbac
193
+ */
194
+
195
+ /**
196
+ * Type-safe helper to define a role.
197
+ * Useful for getting autocomplete and validation when building role configs.
198
+ *
199
+ * @example
200
+ * const editorRole = defineRole({
201
+ * name: 'editor',
202
+ * permissions: ['posts:read', 'posts:write'],
203
+ * inherits: ['user'],
204
+ * });
205
+ */
206
+ declare function defineRole(config: Role): Role;
207
+ /**
208
+ * Creates a typed permission string in the format "resource:action".
209
+ *
210
+ * @example
211
+ * const canReadPosts = definePermission('posts', 'read'); // "posts:read"
212
+ * const canDeleteUsers = definePermission('users', 'delete'); // "users:delete"
213
+ */
214
+ declare function definePermission(resource: string, action: string): Permission;
215
+ /**
216
+ * Creates a set of CRUD permissions for a given resource.
217
+ *
218
+ * @example
219
+ * const postPermissions = crudPermissions('posts');
220
+ * // ["posts:create", "posts:read", "posts:update", "posts:delete"]
221
+ */
222
+ declare function crudPermissions(resource: string): Permission[];
223
+ /**
224
+ * Creates a wildcard permission for a resource (grants all actions).
225
+ *
226
+ * @example
227
+ * wildcardPermission('posts') // "posts:*"
228
+ */
229
+ declare function wildcardPermission(resource: string): Permission;
230
+ /**
231
+ * Guest role — read-only access to public resources
232
+ */
233
+ declare const GUEST_ROLE: Role;
234
+ /**
235
+ * User role — standard authenticated user
236
+ */
237
+ declare const USER_ROLE: Role;
238
+ /**
239
+ * Moderator role — can manage user-generated content
240
+ */
241
+ declare const MODERATOR_ROLE: Role;
242
+ /**
243
+ * Admin role — full control over application resources
244
+ */
245
+ declare const ADMIN_ROLE: Role;
246
+ /**
247
+ * Super Admin role — unrestricted access to everything
248
+ */
249
+ declare const SUPER_ADMIN_ROLE: Role;
250
+ /**
251
+ * All pre-built roles in hierarchy order (least → most privileged)
252
+ */
253
+ declare const DEFAULT_ROLES: Role[];
254
+
255
+ /**
256
+ * Guard and middleware helpers for @krutai/rbac
257
+ *
258
+ * These utilities make it easy to integrate RBAC checks into
259
+ * request handlers, middleware chains, and framework-agnostic guards.
260
+ */
261
+
262
+ /**
263
+ * Creates a guard function that checks a single permission.
264
+ *
265
+ * @example
266
+ * const canDeletePosts = createPermissionGuard(rbac, 'posts:delete');
267
+ * if (!canDeletePosts(ctx)) throw new PermissionDeniedError('posts:delete', ctx.roles);
268
+ */
269
+ declare function createPermissionGuard(rbac: RBACManager, permission: Permission): GuardFn;
270
+ /**
271
+ * Creates a guard function that checks whether the context has a specific role.
272
+ *
273
+ * @example
274
+ * const isAdmin = createRoleGuard(rbac, 'admin');
275
+ * if (!isAdmin(ctx)) throw new PermissionDeniedError('admin role', ctx.roles);
276
+ */
277
+ declare function createRoleGuard(rbac: RBACManager, roleName: string): GuardFn;
278
+ /**
279
+ * Creates a guard that requires ALL of the given permissions.
280
+ *
281
+ * @example
282
+ * const canManagePosts = createAllPermissionsGuard(rbac, ['posts:read', 'posts:delete']);
283
+ */
284
+ declare function createAllPermissionsGuard(rbac: RBACManager, permissions: Permission[]): GuardFn;
285
+ /**
286
+ * Creates a guard that requires AT LEAST ONE of the given permissions.
287
+ *
288
+ * @example
289
+ * const canViewContent = createAnyPermissionGuard(rbac, ['posts:read', 'drafts:read']);
290
+ */
291
+ declare function createAnyPermissionGuard(rbac: RBACManager, permissions: Permission[]): GuardFn;
292
+ /**
293
+ * Wraps a handler function with a permission check.
294
+ * Calls `onDenied` (or throws `PermissionDeniedError`) if the check fails.
295
+ *
296
+ * @example
297
+ * const deletePost = withPermission(
298
+ * rbac,
299
+ * 'posts:delete',
300
+ * async (ctx) => { await db.posts.delete(ctx.postId); },
301
+ * (ctx, perm) => { throw new Error(`Forbidden: ${perm}`); }
302
+ * );
303
+ */
304
+ declare function withPermission<TContext extends {
305
+ rbac: RBACContext;
306
+ }, TResult>(rbac: RBACManager, permission: Permission, handler: HandlerFn<TContext, TResult>, onDenied?: DenyHandlerFn<TContext, TResult>): HandlerFn<TContext, TResult>;
307
+ /**
308
+ * Represents a minimal Express/Next.js-style request with an `rbacContext` property.
309
+ * Attach an `RBACContext` to your request object before using this middleware.
310
+ */
311
+ interface RBACRequest {
312
+ rbacContext?: RBACContext;
313
+ }
314
+ /**
315
+ * Express/Next.js-compatible middleware factory.
316
+ * Expects `req.rbacContext` to be populated by a preceding auth middleware.
317
+ *
318
+ * @example
319
+ * // Express
320
+ * app.delete('/posts/:id', requirePermission(rbac, 'posts:delete'), deletePostHandler);
321
+ *
322
+ * @example
323
+ * // Next.js API route (pages router)
324
+ * export default requirePermission(rbac, 'posts:delete')(handler);
325
+ */
326
+ declare function requirePermission(rbac: RBACManager, permission: Permission): (req: RBACRequest, res: {
327
+ status: (code: number) => {
328
+ json: (body: unknown) => void;
329
+ };
330
+ }, next: () => void) => void;
331
+ /**
332
+ * Express/Next.js-compatible middleware factory for role checks.
333
+ *
334
+ * @example
335
+ * app.get('/admin', requireRole(rbac, 'admin'), adminHandler);
336
+ */
337
+ declare function requireRole(rbac: RBACManager, roleName: string): (req: RBACRequest, res: {
338
+ status: (code: number) => {
339
+ json: (body: unknown) => void;
340
+ };
341
+ }, next: () => void) => void;
342
+
343
+ /**
344
+ * Custom error classes for @krutai/rbac
345
+ */
346
+ /**
347
+ * Base error class for all RBAC-related errors
348
+ */
349
+ declare class RBACError extends Error {
350
+ constructor(message: string);
351
+ }
352
+ /**
353
+ * Thrown when a user lacks the required permission to perform an action
354
+ */
355
+ declare class PermissionDeniedError extends RBACError {
356
+ /** The permission that was required but not held */
357
+ readonly permission: string;
358
+ /** The roles that were evaluated */
359
+ readonly roles: string[];
360
+ constructor(permission: string, roles: string[]);
361
+ }
362
+ /**
363
+ * Thrown when a referenced role does not exist in the registry
364
+ */
365
+ declare class RoleNotFoundError extends RBACError {
366
+ /** The name of the role that was not found */
367
+ readonly roleName: string;
368
+ constructor(roleName: string);
369
+ }
370
+ /**
371
+ * Thrown when a circular inheritance chain is detected in role definitions
372
+ */
373
+ declare class CircularInheritanceError extends RBACError {
374
+ /** The chain of roles that form the cycle */
375
+ readonly chain: string[];
376
+ constructor(chain: string[]);
377
+ }
378
+
379
+ /**
380
+ * @krutai/rbac — Role-Based Access Control for KrutAI
381
+ *
382
+ * A flexible, type-safe RBAC library with role inheritance,
383
+ * wildcard permissions, and framework-agnostic guard helpers.
384
+ *
385
+ * @packageDocumentation
386
+ */
387
+
388
+ declare const VERSION = "0.1.0";
389
+
390
+ export { ADMIN_ROLE, type CheckOptions, CircularInheritanceError, DEFAULT_ROLES, type DenyHandlerFn, GUEST_ROLE, type GuardFn, type HandlerFn, MODERATOR_ROLE, type Permission, type PermissionCheckResult, PermissionDeniedError, type RBACConfig, type RBACContext, RBACError, RBACManager, type RBACRequest, type Role, RoleNotFoundError, SUPER_ADMIN_ROLE, USER_ROLE, VERSION, createAllPermissionsGuard, createAnyPermissionGuard, createPermissionGuard, createRoleGuard, crudPermissions, definePermission, defineRole, requirePermission, requireRole, wildcardPermission, withPermission };