secure-role-guard 1.0.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.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +813 -0
  3. package/dist/adapters/express.d.mts +109 -0
  4. package/dist/adapters/express.d.ts +109 -0
  5. package/dist/adapters/express.js +122 -0
  6. package/dist/adapters/express.js.map +1 -0
  7. package/dist/adapters/express.mjs +118 -0
  8. package/dist/adapters/express.mjs.map +1 -0
  9. package/dist/adapters/index.d.mts +3 -0
  10. package/dist/adapters/index.d.ts +3 -0
  11. package/dist/adapters/index.js +181 -0
  12. package/dist/adapters/index.js.map +1 -0
  13. package/dist/adapters/index.mjs +171 -0
  14. package/dist/adapters/index.mjs.map +1 -0
  15. package/dist/adapters/nextjs.d.mts +140 -0
  16. package/dist/adapters/nextjs.d.ts +140 -0
  17. package/dist/adapters/nextjs.js +138 -0
  18. package/dist/adapters/nextjs.js.map +1 -0
  19. package/dist/adapters/nextjs.mjs +131 -0
  20. package/dist/adapters/nextjs.mjs.map +1 -0
  21. package/dist/core/index.d.mts +100 -0
  22. package/dist/core/index.d.ts +100 -0
  23. package/dist/core/index.js +132 -0
  24. package/dist/core/index.js.map +1 -0
  25. package/dist/core/index.mjs +125 -0
  26. package/dist/core/index.mjs.map +1 -0
  27. package/dist/index.d.mts +4 -0
  28. package/dist/index.d.ts +4 -0
  29. package/dist/index.js +238 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/index.mjs +222 -0
  32. package/dist/index.mjs.map +1 -0
  33. package/dist/react/index.d.mts +237 -0
  34. package/dist/react/index.d.ts +237 -0
  35. package/dist/react/index.js +177 -0
  36. package/dist/react/index.js.map +1 -0
  37. package/dist/react/index.mjs +167 -0
  38. package/dist/react/index.mjs.map +1 -0
  39. package/dist/types-CSUpaGsY.d.mts +76 -0
  40. package/dist/types-CSUpaGsY.d.ts +76 -0
  41. package/package.json +99 -0
@@ -0,0 +1,181 @@
1
+ 'use strict';
2
+
3
+ // src/core/permission-engine.ts
4
+ var WILDCARD_PERMISSION = "*";
5
+ function collectUserPermissions(user, registry) {
6
+ const permissions = /* @__PURE__ */ new Set();
7
+ if (user.permissions !== void 0) {
8
+ for (const permission of user.permissions) {
9
+ permissions.add(permission);
10
+ }
11
+ }
12
+ if (user.roles !== void 0) {
13
+ for (const role of user.roles) {
14
+ const rolePermissions = registry.getPermissions(role);
15
+ for (const permission of rolePermissions) {
16
+ permissions.add(permission);
17
+ }
18
+ }
19
+ }
20
+ return permissions;
21
+ }
22
+ function hasWildcardAccess(permissions, requested) {
23
+ if (permissions.has(WILDCARD_PERMISSION)) {
24
+ return true;
25
+ }
26
+ const parts = requested.split(".");
27
+ let namespace = "";
28
+ for (let i = 0; i < parts.length - 1; i++) {
29
+ const part = parts[i];
30
+ if (part !== void 0) {
31
+ namespace = namespace === "" ? part : `${namespace}.${part}`;
32
+ if (permissions.has(`${namespace}.*`)) {
33
+ return true;
34
+ }
35
+ }
36
+ }
37
+ return false;
38
+ }
39
+ function canUser(user, permission, registry) {
40
+ if (user === null || user === void 0) {
41
+ return false;
42
+ }
43
+ if (permission === "" || permission.trim() === "") {
44
+ return false;
45
+ }
46
+ const userPermissions = collectUserPermissions(user, registry);
47
+ if (userPermissions.has(permission)) {
48
+ return true;
49
+ }
50
+ if (hasWildcardAccess(userPermissions, permission)) {
51
+ return true;
52
+ }
53
+ return false;
54
+ }
55
+ function canUserAll(user, permissions, registry) {
56
+ if (permissions.length === 0) {
57
+ return false;
58
+ }
59
+ for (const permission of permissions) {
60
+ if (!canUser(user, permission, registry)) {
61
+ return false;
62
+ }
63
+ }
64
+ return true;
65
+ }
66
+ function canUserAny(user, permissions, registry) {
67
+ if (permissions.length === 0) {
68
+ return false;
69
+ }
70
+ for (const permission of permissions) {
71
+ if (canUser(user, permission, registry)) {
72
+ return true;
73
+ }
74
+ }
75
+ return false;
76
+ }
77
+
78
+ // src/adapters/express.ts
79
+ var DEFAULT_OPTIONS = {
80
+ statusCode: 403,
81
+ message: "Forbidden",
82
+ getUser: (req) => req.user
83
+ };
84
+ function requirePermission(permission, registry, options = {}) {
85
+ const opts = { ...DEFAULT_OPTIONS, ...options };
86
+ return (req, res, next) => {
87
+ const user = opts.getUser(req);
88
+ if (canUser(user, permission, registry)) {
89
+ next();
90
+ } else {
91
+ res.status(opts.statusCode).json({ error: opts.message });
92
+ }
93
+ };
94
+ }
95
+ function requireAllPermissions(permissions, registry, options = {}) {
96
+ const opts = { ...DEFAULT_OPTIONS, ...options };
97
+ return (req, res, next) => {
98
+ const user = opts.getUser(req);
99
+ if (canUserAll(user, permissions, registry)) {
100
+ next();
101
+ } else {
102
+ res.status(opts.statusCode).json({ error: opts.message });
103
+ }
104
+ };
105
+ }
106
+ function requireAnyPermission(permissions, registry, options = {}) {
107
+ const opts = { ...DEFAULT_OPTIONS, ...options };
108
+ return (req, res, next) => {
109
+ const user = opts.getUser(req);
110
+ if (canUserAny(user, permissions, registry)) {
111
+ next();
112
+ } else {
113
+ res.status(opts.statusCode).json({ error: opts.message });
114
+ }
115
+ };
116
+ }
117
+
118
+ // src/adapters/nextjs.ts
119
+ function checkNextPermission(user, permission, registry) {
120
+ return {
121
+ allowed: canUser(user, permission, registry),
122
+ user: user ?? null
123
+ };
124
+ }
125
+ function checkNextPermissionAll(user, permissions, registry) {
126
+ return {
127
+ allowed: canUserAll(user, permissions, registry),
128
+ user: user ?? null
129
+ };
130
+ }
131
+ function checkNextPermissionAny(user, permissions, registry) {
132
+ return {
133
+ allowed: canUserAny(user, permissions, registry),
134
+ user: user ?? null
135
+ };
136
+ }
137
+ function withPermission(permission, registry, options, handler) {
138
+ const statusCode = options.statusCode ?? 403;
139
+ const message = options.message ?? "Forbidden";
140
+ return async (request) => {
141
+ const user = await options.getUser(request);
142
+ if (user === null || !canUser(user, permission, registry)) {
143
+ return Response.json({ error: message }, { status: statusCode });
144
+ }
145
+ return handler(request, user);
146
+ };
147
+ }
148
+ function withAllPermissions(permissions, registry, options, handler) {
149
+ const statusCode = options.statusCode ?? 403;
150
+ const message = options.message ?? "Forbidden";
151
+ return async (request) => {
152
+ const user = await options.getUser(request);
153
+ if (user === null || !canUserAll(user, permissions, registry)) {
154
+ return Response.json({ error: message }, { status: statusCode });
155
+ }
156
+ return handler(request, user);
157
+ };
158
+ }
159
+ function withAnyPermission(permissions, registry, options, handler) {
160
+ const statusCode = options.statusCode ?? 403;
161
+ const message = options.message ?? "Forbidden";
162
+ return async (request) => {
163
+ const user = await options.getUser(request);
164
+ if (user === null || !canUserAny(user, permissions, registry)) {
165
+ return Response.json({ error: message }, { status: statusCode });
166
+ }
167
+ return handler(request, user);
168
+ };
169
+ }
170
+
171
+ exports.checkNextPermission = checkNextPermission;
172
+ exports.checkNextPermissionAll = checkNextPermissionAll;
173
+ exports.checkNextPermissionAny = checkNextPermissionAny;
174
+ exports.requireAllPermissions = requireAllPermissions;
175
+ exports.requireAnyPermission = requireAnyPermission;
176
+ exports.requirePermission = requirePermission;
177
+ exports.withAllPermissions = withAllPermissions;
178
+ exports.withAnyPermission = withAnyPermission;
179
+ exports.withPermission = withPermission;
180
+ //# sourceMappingURL=index.js.map
181
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/permission-engine.ts","../../src/adapters/express.ts","../../src/adapters/nextjs.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * Secure Role Guard - Permission Engine\r\n *\r\n * Pure functions for permission checking.\r\n * Zero side effects, zero dependencies, zero mutations.\r\n */\r\n\r\nimport type { UserContext, RoleRegistry, PermissionCheckResult } from \"./types\";\r\n\r\n/** Wildcard permission that grants all access */\r\nconst WILDCARD_PERMISSION = \"*\";\r\n\r\n/**\r\n * Collects all permissions for a user from their roles and direct permissions.\r\n *\r\n * @param user - The user context\r\n * @param registry - The role registry to resolve role permissions\r\n * @returns Set of all permissions (for efficient lookup)\r\n */\r\nfunction collectUserPermissions(\r\n user: UserContext,\r\n registry: RoleRegistry\r\n): ReadonlySet<string> {\r\n const permissions = new Set<string>();\r\n\r\n // Add direct user permissions\r\n if (user.permissions !== undefined) {\r\n for (const permission of user.permissions) {\r\n permissions.add(permission);\r\n }\r\n }\r\n\r\n // Add permissions from all user roles\r\n if (user.roles !== undefined) {\r\n for (const role of user.roles) {\r\n const rolePermissions = registry.getPermissions(role);\r\n for (const permission of rolePermissions) {\r\n permissions.add(permission);\r\n }\r\n }\r\n }\r\n\r\n return permissions;\r\n}\r\n\r\n/**\r\n * Checks if a permission set contains a wildcard that grants the requested permission.\r\n *\r\n * Supports:\r\n * - Exact wildcard (*) - grants everything\r\n * - Namespace wildcards (user.*) - grants all under namespace\r\n *\r\n * @param permissions - Set of permissions to check\r\n * @param requested - The permission being requested\r\n * @returns True if wildcard grants access\r\n */\r\nfunction hasWildcardAccess(\r\n permissions: ReadonlySet<string>,\r\n requested: string\r\n): boolean {\r\n // Check for global wildcard\r\n if (permissions.has(WILDCARD_PERMISSION)) {\r\n return true;\r\n }\r\n\r\n // Check for namespace wildcards (e.g., user.* grants user.read)\r\n const parts = requested.split(\".\");\r\n let namespace = \"\";\r\n\r\n for (let i = 0; i < parts.length - 1; i++) {\r\n const part = parts[i];\r\n if (part !== undefined) {\r\n namespace = namespace === \"\" ? part : `${namespace}.${part}`;\r\n if (permissions.has(`${namespace}.*`)) {\r\n return true;\r\n }\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Checks if a user has a specific permission.\r\n *\r\n * SECURITY GUARANTEES:\r\n * - Deny by default: undefined/null/empty always returns false\r\n * - Pure function: no side effects, no mutations\r\n * - Deterministic: same inputs always produce same output\r\n *\r\n * @param user - The user context to check\r\n * @param permission - The permission required\r\n * @param registry - The role registry for resolving roles\r\n * @returns True if user has the permission, false otherwise\r\n *\r\n * @example\r\n * const canEdit = canUser(currentUser, 'user.update', roleRegistry);\r\n */\r\nexport function canUser(\r\n user: UserContext | null | undefined,\r\n permission: string,\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: No user context means no access\r\n if (user === null || user === undefined) {\r\n return false;\r\n }\r\n\r\n // DENY BY DEFAULT: Empty permission request is invalid\r\n if (permission === \"\" || permission.trim() === \"\") {\r\n return false;\r\n }\r\n\r\n const userPermissions = collectUserPermissions(user, registry);\r\n\r\n // Check for exact permission match\r\n if (userPermissions.has(permission)) {\r\n return true;\r\n }\r\n\r\n // Check for wildcard access\r\n if (hasWildcardAccess(userPermissions, permission)) {\r\n return true;\r\n }\r\n\r\n // DENY BY DEFAULT\r\n return false;\r\n}\r\n\r\n/**\r\n * Checks if a user has ALL of the specified permissions.\r\n *\r\n * @param user - The user context to check\r\n * @param permissions - Array of permissions required\r\n * @param registry - The role registry\r\n * @returns True if user has ALL permissions\r\n *\r\n * @example\r\n * const canManage = canUserAll(user, ['user.read', 'user.update'], registry);\r\n */\r\nexport function canUserAll(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: Empty permissions array\r\n if (permissions.length === 0) {\r\n return false;\r\n }\r\n\r\n for (const permission of permissions) {\r\n if (!canUser(user, permission, registry)) {\r\n return false;\r\n }\r\n }\r\n\r\n return true;\r\n}\r\n\r\n/**\r\n * Checks if a user has ANY of the specified permissions.\r\n *\r\n * @param user - The user context to check\r\n * @param permissions - Array of permissions to check\r\n * @param registry - The role registry\r\n * @returns True if user has at least one permission\r\n *\r\n * @example\r\n * const canView = canUserAny(user, ['report.view', 'report.admin'], registry);\r\n */\r\nexport function canUserAny(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: Empty permissions array\r\n if (permissions.length === 0) {\r\n return false;\r\n }\r\n\r\n for (const permission of permissions) {\r\n if (canUser(user, permission, registry)) {\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Extended permission check with detailed result.\r\n * Useful for debugging and logging.\r\n *\r\n * @param user - The user context\r\n * @param permission - Permission to check\r\n * @param registry - The role registry\r\n * @returns PermissionCheckResult with allowed status and reason\r\n */\r\nexport function checkPermission(\r\n user: UserContext | null | undefined,\r\n permission: string,\r\n registry: RoleRegistry\r\n): PermissionCheckResult {\r\n if (user === null || user === undefined) {\r\n return Object.freeze({\r\n allowed: false,\r\n reason: \"No user context provided\",\r\n });\r\n }\r\n\r\n if (permission === \"\" || permission.trim() === \"\") {\r\n return Object.freeze({\r\n allowed: false,\r\n reason: \"Empty permission requested\",\r\n });\r\n }\r\n\r\n const allowed = canUser(user, permission, registry);\r\n\r\n return Object.freeze({\r\n allowed,\r\n reason: allowed\r\n ? `Permission \"${permission}\" granted`\r\n : `Permission \"${permission}\" denied`,\r\n });\r\n}\r\n","/**\r\n * Secure Role Guard - Express Adapter\r\n *\r\n * Thin middleware wrapper for Express.js applications.\r\n * Delegates all authorization logic to the core permission engine.\r\n *\r\n * IMPORTANT: This adapter does NOT handle authentication.\r\n * You must provide the user context from your auth middleware.\r\n */\r\n\r\nimport type { UserContext, RoleRegistry } from \"../core/types\";\r\nimport { canUser, canUserAll, canUserAny } from \"../core/permission-engine\";\r\n\r\n/**\r\n * Express-compatible Request type (minimal interface).\r\n * We use a minimal interface to avoid requiring express as a dependency.\r\n */\r\nexport interface ExpressRequest {\r\n /** User context attached by your auth middleware */\r\n user?: UserContext;\r\n}\r\n\r\n/**\r\n * Express-compatible Response type (minimal interface).\r\n */\r\nexport interface ExpressResponse {\r\n status(code: number): ExpressResponse;\r\n json(body: unknown): ExpressResponse;\r\n}\r\n\r\n/**\r\n * Express-compatible NextFunction type.\r\n */\r\nexport type ExpressNextFunction = (error?: unknown) => void;\r\n\r\n/**\r\n * Express middleware signature.\r\n */\r\nexport type ExpressMiddleware = (\r\n req: ExpressRequest,\r\n res: ExpressResponse,\r\n next: ExpressNextFunction\r\n) => void;\r\n\r\n/**\r\n * Options for the permission middleware.\r\n */\r\nexport type PermissionMiddlewareOptions = {\r\n /** HTTP status code to return on denial (default: 403) */\r\n readonly statusCode?: number;\r\n /** Custom error message (default: 'Forbidden') */\r\n readonly message?: string;\r\n /** Custom function to extract user from request */\r\n readonly getUser?: (req: ExpressRequest) => UserContext | null | undefined;\r\n};\r\n\r\nconst DEFAULT_OPTIONS: Required<PermissionMiddlewareOptions> = {\r\n statusCode: 403,\r\n message: \"Forbidden\",\r\n getUser: (req) => req.user,\r\n};\r\n\r\n/**\r\n * Creates Express middleware that requires a specific permission.\r\n *\r\n * SECURITY: Denies by default if user is missing or permission not granted.\r\n *\r\n * @param permission - Required permission\r\n * @param registry - Role registry\r\n * @param options - Optional configuration\r\n * @returns Express middleware function\r\n *\r\n * @example\r\n * ```ts\r\n * import { defineRoles } from 'secure-role-guard/core';\r\n * import { requirePermission } from 'secure-role-guard/adapters/express';\r\n *\r\n * const registry = defineRoles({ admin: ['user.update'] });\r\n *\r\n * app.put('/users/:id',\r\n * authMiddleware, // Your auth middleware sets req.user\r\n * requirePermission('user.update', registry),\r\n * updateUserHandler\r\n * );\r\n * ```\r\n */\r\nexport function requirePermission(\r\n permission: string,\r\n registry: RoleRegistry,\r\n options: PermissionMiddlewareOptions = {}\r\n): ExpressMiddleware {\r\n const opts = { ...DEFAULT_OPTIONS, ...options };\r\n\r\n return (req, res, next) => {\r\n const user = opts.getUser(req);\r\n\r\n if (canUser(user, permission, registry)) {\r\n next();\r\n } else {\r\n res.status(opts.statusCode).json({ error: opts.message });\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Creates Express middleware that requires ALL specified permissions.\r\n *\r\n * @param permissions - Array of required permissions\r\n * @param registry - Role registry\r\n * @param options - Optional configuration\r\n * @returns Express middleware function\r\n *\r\n * @example\r\n * ```ts\r\n * app.delete('/users/:id',\r\n * authMiddleware,\r\n * requireAllPermissions(['user.read', 'user.delete'], registry),\r\n * deleteUserHandler\r\n * );\r\n * ```\r\n */\r\nexport function requireAllPermissions(\r\n permissions: readonly string[],\r\n registry: RoleRegistry,\r\n options: PermissionMiddlewareOptions = {}\r\n): ExpressMiddleware {\r\n const opts = { ...DEFAULT_OPTIONS, ...options };\r\n\r\n return (req, res, next) => {\r\n const user = opts.getUser(req);\r\n\r\n if (canUserAll(user, permissions, registry)) {\r\n next();\r\n } else {\r\n res.status(opts.statusCode).json({ error: opts.message });\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Creates Express middleware that requires ANY of the specified permissions.\r\n *\r\n * @param permissions - Array of permissions (user needs at least one)\r\n * @param registry - Role registry\r\n * @param options - Optional configuration\r\n * @returns Express middleware function\r\n *\r\n * @example\r\n * ```ts\r\n * app.get('/reports',\r\n * authMiddleware,\r\n * requireAnyPermission(['report.admin', 'report.viewer'], registry),\r\n * getReportsHandler\r\n * );\r\n * ```\r\n */\r\nexport function requireAnyPermission(\r\n permissions: readonly string[],\r\n registry: RoleRegistry,\r\n options: PermissionMiddlewareOptions = {}\r\n): ExpressMiddleware {\r\n const opts = { ...DEFAULT_OPTIONS, ...options };\r\n\r\n return (req, res, next) => {\r\n const user = opts.getUser(req);\r\n\r\n if (canUserAny(user, permissions, registry)) {\r\n next();\r\n } else {\r\n res.status(opts.statusCode).json({ error: opts.message });\r\n }\r\n };\r\n}\r\n","/**\r\n * Secure Role Guard - Next.js Adapter\r\n *\r\n * Thin wrapper for Next.js API routes and Route Handlers.\r\n * Delegates all authorization logic to the core permission engine.\r\n *\r\n * IMPORTANT: This adapter does NOT handle authentication.\r\n * You must provide the user context from your auth system.\r\n */\r\n\r\nimport type { UserContext, RoleRegistry } from \"../core/types\";\r\nimport { canUser, canUserAll, canUserAny } from \"../core/permission-engine\";\r\n\r\n/**\r\n * Next.js Request type (minimal interface for App Router).\r\n * Using minimal interface to avoid requiring next as a dependency.\r\n */\r\nexport interface NextRequest {\r\n /** Headers from the request */\r\n headers: {\r\n get(name: string): string | null;\r\n };\r\n}\r\n\r\n/**\r\n * Next.js Response helper for App Router.\r\n */\r\nexport interface NextResponse {\r\n json(body: unknown, init?: { status?: number }): Response;\r\n}\r\n\r\n/**\r\n * Result of a permission check in Next.js context.\r\n */\r\nexport type PermissionResult = {\r\n readonly allowed: boolean;\r\n readonly user: UserContext | null;\r\n};\r\n\r\n/**\r\n * Options for Next.js permission wrappers.\r\n */\r\nexport type NextPermissionOptions = {\r\n /** Function to extract user context from request */\r\n readonly getUser: (\r\n request: NextRequest\r\n ) => UserContext | null | Promise<UserContext | null>;\r\n /** HTTP status code on denial (default: 403) */\r\n readonly statusCode?: number;\r\n /** Error message on denial (default: 'Forbidden') */\r\n readonly message?: string;\r\n};\r\n\r\n/**\r\n * Checks if a user has permission in a Next.js API context.\r\n *\r\n * This is a pure check function - you handle the response.\r\n *\r\n * @param user - User context\r\n * @param permission - Required permission\r\n * @param registry - Role registry\r\n * @returns PermissionResult\r\n *\r\n * @example\r\n * ```ts\r\n * // app/api/users/route.ts\r\n * import { defineRoles } from 'secure-role-guard/core';\r\n * import { checkNextPermission } from 'secure-role-guard/adapters/nextjs';\r\n *\r\n * const registry = defineRoles({ admin: ['user.update'] });\r\n *\r\n * export async function PUT(request: NextRequest) {\r\n * const user = await getUser(request); // Your auth function\r\n * const result = checkNextPermission(user, 'user.update', registry);\r\n *\r\n * if (!result.allowed) {\r\n * return NextResponse.json({ error: 'Forbidden' }, { status: 403 });\r\n * }\r\n *\r\n * // Handle the request...\r\n * }\r\n * ```\r\n */\r\nexport function checkNextPermission(\r\n user: UserContext | null | undefined,\r\n permission: string,\r\n registry: RoleRegistry\r\n): PermissionResult {\r\n return {\r\n allowed: canUser(user, permission, registry),\r\n user: user ?? null,\r\n };\r\n}\r\n\r\n/**\r\n * Checks if a user has ALL specified permissions.\r\n *\r\n * @param user - User context\r\n * @param permissions - Required permissions\r\n * @param registry - Role registry\r\n * @returns PermissionResult\r\n */\r\nexport function checkNextPermissionAll(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): PermissionResult {\r\n return {\r\n allowed: canUserAll(user, permissions, registry),\r\n user: user ?? null,\r\n };\r\n}\r\n\r\n/**\r\n * Checks if a user has ANY of the specified permissions.\r\n *\r\n * @param user - User context\r\n * @param permissions - Permissions to check\r\n * @param registry - Role registry\r\n * @returns PermissionResult\r\n */\r\nexport function checkNextPermissionAny(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): PermissionResult {\r\n return {\r\n allowed: canUserAny(user, permissions, registry),\r\n user: user ?? null,\r\n };\r\n}\r\n\r\n/**\r\n * Higher-order function that wraps a Next.js Route Handler with permission check.\r\n *\r\n * @param permission - Required permission\r\n * @param registry - Role registry\r\n * @param options - Configuration options\r\n * @param handler - The route handler to wrap\r\n * @returns Wrapped route handler\r\n *\r\n * @example\r\n * ```ts\r\n * // app/api/admin/route.ts\r\n * import { defineRoles } from 'secure-role-guard/core';\r\n * import { withPermission } from 'secure-role-guard/adapters/nextjs';\r\n *\r\n * const registry = defineRoles({ admin: ['admin.access'] });\r\n *\r\n * async function getUser(req: NextRequest) {\r\n * // Your auth logic to extract user from session/token\r\n * return { roles: ['admin'] };\r\n * }\r\n *\r\n * export const GET = withPermission(\r\n * 'admin.access',\r\n * registry,\r\n * { getUser },\r\n * async (request, user) => {\r\n * return Response.json({ message: 'Welcome, admin!' });\r\n * }\r\n * );\r\n * ```\r\n */\r\nexport function withPermission<T extends NextRequest>(\r\n permission: string,\r\n registry: RoleRegistry,\r\n options: NextPermissionOptions,\r\n handler: (request: T, user: UserContext) => Response | Promise<Response>\r\n): (request: T) => Promise<Response> {\r\n const statusCode = options.statusCode ?? 403;\r\n const message = options.message ?? \"Forbidden\";\r\n\r\n return async (request: T): Promise<Response> => {\r\n const user = await options.getUser(request);\r\n\r\n if (user === null || !canUser(user, permission, registry)) {\r\n return Response.json({ error: message }, { status: statusCode });\r\n }\r\n\r\n return handler(request, user);\r\n };\r\n}\r\n\r\n/**\r\n * Higher-order function that wraps a handler requiring ALL permissions.\r\n */\r\nexport function withAllPermissions<T extends NextRequest>(\r\n permissions: readonly string[],\r\n registry: RoleRegistry,\r\n options: NextPermissionOptions,\r\n handler: (request: T, user: UserContext) => Response | Promise<Response>\r\n): (request: T) => Promise<Response> {\r\n const statusCode = options.statusCode ?? 403;\r\n const message = options.message ?? \"Forbidden\";\r\n\r\n return async (request: T): Promise<Response> => {\r\n const user = await options.getUser(request);\r\n\r\n if (user === null || !canUserAll(user, permissions, registry)) {\r\n return Response.json({ error: message }, { status: statusCode });\r\n }\r\n\r\n return handler(request, user);\r\n };\r\n}\r\n\r\n/**\r\n * Higher-order function that wraps a handler requiring ANY permission.\r\n */\r\nexport function withAnyPermission<T extends NextRequest>(\r\n permissions: readonly string[],\r\n registry: RoleRegistry,\r\n options: NextPermissionOptions,\r\n handler: (request: T, user: UserContext) => Response | Promise<Response>\r\n): (request: T) => Promise<Response> {\r\n const statusCode = options.statusCode ?? 403;\r\n const message = options.message ?? \"Forbidden\";\r\n\r\n return async (request: T): Promise<Response> => {\r\n const user = await options.getUser(request);\r\n\r\n if (user === null || !canUserAny(user, permissions, registry)) {\r\n return Response.json({ error: message }, { status: statusCode });\r\n }\r\n\r\n return handler(request, user);\r\n };\r\n}\r\n"]}
@@ -0,0 +1,171 @@
1
+ // src/core/permission-engine.ts
2
+ var WILDCARD_PERMISSION = "*";
3
+ function collectUserPermissions(user, registry) {
4
+ const permissions = /* @__PURE__ */ new Set();
5
+ if (user.permissions !== void 0) {
6
+ for (const permission of user.permissions) {
7
+ permissions.add(permission);
8
+ }
9
+ }
10
+ if (user.roles !== void 0) {
11
+ for (const role of user.roles) {
12
+ const rolePermissions = registry.getPermissions(role);
13
+ for (const permission of rolePermissions) {
14
+ permissions.add(permission);
15
+ }
16
+ }
17
+ }
18
+ return permissions;
19
+ }
20
+ function hasWildcardAccess(permissions, requested) {
21
+ if (permissions.has(WILDCARD_PERMISSION)) {
22
+ return true;
23
+ }
24
+ const parts = requested.split(".");
25
+ let namespace = "";
26
+ for (let i = 0; i < parts.length - 1; i++) {
27
+ const part = parts[i];
28
+ if (part !== void 0) {
29
+ namespace = namespace === "" ? part : `${namespace}.${part}`;
30
+ if (permissions.has(`${namespace}.*`)) {
31
+ return true;
32
+ }
33
+ }
34
+ }
35
+ return false;
36
+ }
37
+ function canUser(user, permission, registry) {
38
+ if (user === null || user === void 0) {
39
+ return false;
40
+ }
41
+ if (permission === "" || permission.trim() === "") {
42
+ return false;
43
+ }
44
+ const userPermissions = collectUserPermissions(user, registry);
45
+ if (userPermissions.has(permission)) {
46
+ return true;
47
+ }
48
+ if (hasWildcardAccess(userPermissions, permission)) {
49
+ return true;
50
+ }
51
+ return false;
52
+ }
53
+ function canUserAll(user, permissions, registry) {
54
+ if (permissions.length === 0) {
55
+ return false;
56
+ }
57
+ for (const permission of permissions) {
58
+ if (!canUser(user, permission, registry)) {
59
+ return false;
60
+ }
61
+ }
62
+ return true;
63
+ }
64
+ function canUserAny(user, permissions, registry) {
65
+ if (permissions.length === 0) {
66
+ return false;
67
+ }
68
+ for (const permission of permissions) {
69
+ if (canUser(user, permission, registry)) {
70
+ return true;
71
+ }
72
+ }
73
+ return false;
74
+ }
75
+
76
+ // src/adapters/express.ts
77
+ var DEFAULT_OPTIONS = {
78
+ statusCode: 403,
79
+ message: "Forbidden",
80
+ getUser: (req) => req.user
81
+ };
82
+ function requirePermission(permission, registry, options = {}) {
83
+ const opts = { ...DEFAULT_OPTIONS, ...options };
84
+ return (req, res, next) => {
85
+ const user = opts.getUser(req);
86
+ if (canUser(user, permission, registry)) {
87
+ next();
88
+ } else {
89
+ res.status(opts.statusCode).json({ error: opts.message });
90
+ }
91
+ };
92
+ }
93
+ function requireAllPermissions(permissions, registry, options = {}) {
94
+ const opts = { ...DEFAULT_OPTIONS, ...options };
95
+ return (req, res, next) => {
96
+ const user = opts.getUser(req);
97
+ if (canUserAll(user, permissions, registry)) {
98
+ next();
99
+ } else {
100
+ res.status(opts.statusCode).json({ error: opts.message });
101
+ }
102
+ };
103
+ }
104
+ function requireAnyPermission(permissions, registry, options = {}) {
105
+ const opts = { ...DEFAULT_OPTIONS, ...options };
106
+ return (req, res, next) => {
107
+ const user = opts.getUser(req);
108
+ if (canUserAny(user, permissions, registry)) {
109
+ next();
110
+ } else {
111
+ res.status(opts.statusCode).json({ error: opts.message });
112
+ }
113
+ };
114
+ }
115
+
116
+ // src/adapters/nextjs.ts
117
+ function checkNextPermission(user, permission, registry) {
118
+ return {
119
+ allowed: canUser(user, permission, registry),
120
+ user: user ?? null
121
+ };
122
+ }
123
+ function checkNextPermissionAll(user, permissions, registry) {
124
+ return {
125
+ allowed: canUserAll(user, permissions, registry),
126
+ user: user ?? null
127
+ };
128
+ }
129
+ function checkNextPermissionAny(user, permissions, registry) {
130
+ return {
131
+ allowed: canUserAny(user, permissions, registry),
132
+ user: user ?? null
133
+ };
134
+ }
135
+ function withPermission(permission, registry, options, handler) {
136
+ const statusCode = options.statusCode ?? 403;
137
+ const message = options.message ?? "Forbidden";
138
+ return async (request) => {
139
+ const user = await options.getUser(request);
140
+ if (user === null || !canUser(user, permission, registry)) {
141
+ return Response.json({ error: message }, { status: statusCode });
142
+ }
143
+ return handler(request, user);
144
+ };
145
+ }
146
+ function withAllPermissions(permissions, registry, options, handler) {
147
+ const statusCode = options.statusCode ?? 403;
148
+ const message = options.message ?? "Forbidden";
149
+ return async (request) => {
150
+ const user = await options.getUser(request);
151
+ if (user === null || !canUserAll(user, permissions, registry)) {
152
+ return Response.json({ error: message }, { status: statusCode });
153
+ }
154
+ return handler(request, user);
155
+ };
156
+ }
157
+ function withAnyPermission(permissions, registry, options, handler) {
158
+ const statusCode = options.statusCode ?? 403;
159
+ const message = options.message ?? "Forbidden";
160
+ return async (request) => {
161
+ const user = await options.getUser(request);
162
+ if (user === null || !canUserAny(user, permissions, registry)) {
163
+ return Response.json({ error: message }, { status: statusCode });
164
+ }
165
+ return handler(request, user);
166
+ };
167
+ }
168
+
169
+ export { checkNextPermission, checkNextPermissionAll, checkNextPermissionAny, requireAllPermissions, requireAnyPermission, requirePermission, withAllPermissions, withAnyPermission, withPermission };
170
+ //# sourceMappingURL=index.mjs.map
171
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/permission-engine.ts","../../src/adapters/express.ts","../../src/adapters/nextjs.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\r\n * Secure Role Guard - Permission Engine\r\n *\r\n * Pure functions for permission checking.\r\n * Zero side effects, zero dependencies, zero mutations.\r\n */\r\n\r\nimport type { UserContext, RoleRegistry, PermissionCheckResult } from \"./types\";\r\n\r\n/** Wildcard permission that grants all access */\r\nconst WILDCARD_PERMISSION = \"*\";\r\n\r\n/**\r\n * Collects all permissions for a user from their roles and direct permissions.\r\n *\r\n * @param user - The user context\r\n * @param registry - The role registry to resolve role permissions\r\n * @returns Set of all permissions (for efficient lookup)\r\n */\r\nfunction collectUserPermissions(\r\n user: UserContext,\r\n registry: RoleRegistry\r\n): ReadonlySet<string> {\r\n const permissions = new Set<string>();\r\n\r\n // Add direct user permissions\r\n if (user.permissions !== undefined) {\r\n for (const permission of user.permissions) {\r\n permissions.add(permission);\r\n }\r\n }\r\n\r\n // Add permissions from all user roles\r\n if (user.roles !== undefined) {\r\n for (const role of user.roles) {\r\n const rolePermissions = registry.getPermissions(role);\r\n for (const permission of rolePermissions) {\r\n permissions.add(permission);\r\n }\r\n }\r\n }\r\n\r\n return permissions;\r\n}\r\n\r\n/**\r\n * Checks if a permission set contains a wildcard that grants the requested permission.\r\n *\r\n * Supports:\r\n * - Exact wildcard (*) - grants everything\r\n * - Namespace wildcards (user.*) - grants all under namespace\r\n *\r\n * @param permissions - Set of permissions to check\r\n * @param requested - The permission being requested\r\n * @returns True if wildcard grants access\r\n */\r\nfunction hasWildcardAccess(\r\n permissions: ReadonlySet<string>,\r\n requested: string\r\n): boolean {\r\n // Check for global wildcard\r\n if (permissions.has(WILDCARD_PERMISSION)) {\r\n return true;\r\n }\r\n\r\n // Check for namespace wildcards (e.g., user.* grants user.read)\r\n const parts = requested.split(\".\");\r\n let namespace = \"\";\r\n\r\n for (let i = 0; i < parts.length - 1; i++) {\r\n const part = parts[i];\r\n if (part !== undefined) {\r\n namespace = namespace === \"\" ? part : `${namespace}.${part}`;\r\n if (permissions.has(`${namespace}.*`)) {\r\n return true;\r\n }\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Checks if a user has a specific permission.\r\n *\r\n * SECURITY GUARANTEES:\r\n * - Deny by default: undefined/null/empty always returns false\r\n * - Pure function: no side effects, no mutations\r\n * - Deterministic: same inputs always produce same output\r\n *\r\n * @param user - The user context to check\r\n * @param permission - The permission required\r\n * @param registry - The role registry for resolving roles\r\n * @returns True if user has the permission, false otherwise\r\n *\r\n * @example\r\n * const canEdit = canUser(currentUser, 'user.update', roleRegistry);\r\n */\r\nexport function canUser(\r\n user: UserContext | null | undefined,\r\n permission: string,\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: No user context means no access\r\n if (user === null || user === undefined) {\r\n return false;\r\n }\r\n\r\n // DENY BY DEFAULT: Empty permission request is invalid\r\n if (permission === \"\" || permission.trim() === \"\") {\r\n return false;\r\n }\r\n\r\n const userPermissions = collectUserPermissions(user, registry);\r\n\r\n // Check for exact permission match\r\n if (userPermissions.has(permission)) {\r\n return true;\r\n }\r\n\r\n // Check for wildcard access\r\n if (hasWildcardAccess(userPermissions, permission)) {\r\n return true;\r\n }\r\n\r\n // DENY BY DEFAULT\r\n return false;\r\n}\r\n\r\n/**\r\n * Checks if a user has ALL of the specified permissions.\r\n *\r\n * @param user - The user context to check\r\n * @param permissions - Array of permissions required\r\n * @param registry - The role registry\r\n * @returns True if user has ALL permissions\r\n *\r\n * @example\r\n * const canManage = canUserAll(user, ['user.read', 'user.update'], registry);\r\n */\r\nexport function canUserAll(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: Empty permissions array\r\n if (permissions.length === 0) {\r\n return false;\r\n }\r\n\r\n for (const permission of permissions) {\r\n if (!canUser(user, permission, registry)) {\r\n return false;\r\n }\r\n }\r\n\r\n return true;\r\n}\r\n\r\n/**\r\n * Checks if a user has ANY of the specified permissions.\r\n *\r\n * @param user - The user context to check\r\n * @param permissions - Array of permissions to check\r\n * @param registry - The role registry\r\n * @returns True if user has at least one permission\r\n *\r\n * @example\r\n * const canView = canUserAny(user, ['report.view', 'report.admin'], registry);\r\n */\r\nexport function canUserAny(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: Empty permissions array\r\n if (permissions.length === 0) {\r\n return false;\r\n }\r\n\r\n for (const permission of permissions) {\r\n if (canUser(user, permission, registry)) {\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Extended permission check with detailed result.\r\n * Useful for debugging and logging.\r\n *\r\n * @param user - The user context\r\n * @param permission - Permission to check\r\n * @param registry - The role registry\r\n * @returns PermissionCheckResult with allowed status and reason\r\n */\r\nexport function checkPermission(\r\n user: UserContext | null | undefined,\r\n permission: string,\r\n registry: RoleRegistry\r\n): PermissionCheckResult {\r\n if (user === null || user === undefined) {\r\n return Object.freeze({\r\n allowed: false,\r\n reason: \"No user context provided\",\r\n });\r\n }\r\n\r\n if (permission === \"\" || permission.trim() === \"\") {\r\n return Object.freeze({\r\n allowed: false,\r\n reason: \"Empty permission requested\",\r\n });\r\n }\r\n\r\n const allowed = canUser(user, permission, registry);\r\n\r\n return Object.freeze({\r\n allowed,\r\n reason: allowed\r\n ? `Permission \"${permission}\" granted`\r\n : `Permission \"${permission}\" denied`,\r\n });\r\n}\r\n","/**\r\n * Secure Role Guard - Express Adapter\r\n *\r\n * Thin middleware wrapper for Express.js applications.\r\n * Delegates all authorization logic to the core permission engine.\r\n *\r\n * IMPORTANT: This adapter does NOT handle authentication.\r\n * You must provide the user context from your auth middleware.\r\n */\r\n\r\nimport type { UserContext, RoleRegistry } from \"../core/types\";\r\nimport { canUser, canUserAll, canUserAny } from \"../core/permission-engine\";\r\n\r\n/**\r\n * Express-compatible Request type (minimal interface).\r\n * We use a minimal interface to avoid requiring express as a dependency.\r\n */\r\nexport interface ExpressRequest {\r\n /** User context attached by your auth middleware */\r\n user?: UserContext;\r\n}\r\n\r\n/**\r\n * Express-compatible Response type (minimal interface).\r\n */\r\nexport interface ExpressResponse {\r\n status(code: number): ExpressResponse;\r\n json(body: unknown): ExpressResponse;\r\n}\r\n\r\n/**\r\n * Express-compatible NextFunction type.\r\n */\r\nexport type ExpressNextFunction = (error?: unknown) => void;\r\n\r\n/**\r\n * Express middleware signature.\r\n */\r\nexport type ExpressMiddleware = (\r\n req: ExpressRequest,\r\n res: ExpressResponse,\r\n next: ExpressNextFunction\r\n) => void;\r\n\r\n/**\r\n * Options for the permission middleware.\r\n */\r\nexport type PermissionMiddlewareOptions = {\r\n /** HTTP status code to return on denial (default: 403) */\r\n readonly statusCode?: number;\r\n /** Custom error message (default: 'Forbidden') */\r\n readonly message?: string;\r\n /** Custom function to extract user from request */\r\n readonly getUser?: (req: ExpressRequest) => UserContext | null | undefined;\r\n};\r\n\r\nconst DEFAULT_OPTIONS: Required<PermissionMiddlewareOptions> = {\r\n statusCode: 403,\r\n message: \"Forbidden\",\r\n getUser: (req) => req.user,\r\n};\r\n\r\n/**\r\n * Creates Express middleware that requires a specific permission.\r\n *\r\n * SECURITY: Denies by default if user is missing or permission not granted.\r\n *\r\n * @param permission - Required permission\r\n * @param registry - Role registry\r\n * @param options - Optional configuration\r\n * @returns Express middleware function\r\n *\r\n * @example\r\n * ```ts\r\n * import { defineRoles } from 'secure-role-guard/core';\r\n * import { requirePermission } from 'secure-role-guard/adapters/express';\r\n *\r\n * const registry = defineRoles({ admin: ['user.update'] });\r\n *\r\n * app.put('/users/:id',\r\n * authMiddleware, // Your auth middleware sets req.user\r\n * requirePermission('user.update', registry),\r\n * updateUserHandler\r\n * );\r\n * ```\r\n */\r\nexport function requirePermission(\r\n permission: string,\r\n registry: RoleRegistry,\r\n options: PermissionMiddlewareOptions = {}\r\n): ExpressMiddleware {\r\n const opts = { ...DEFAULT_OPTIONS, ...options };\r\n\r\n return (req, res, next) => {\r\n const user = opts.getUser(req);\r\n\r\n if (canUser(user, permission, registry)) {\r\n next();\r\n } else {\r\n res.status(opts.statusCode).json({ error: opts.message });\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Creates Express middleware that requires ALL specified permissions.\r\n *\r\n * @param permissions - Array of required permissions\r\n * @param registry - Role registry\r\n * @param options - Optional configuration\r\n * @returns Express middleware function\r\n *\r\n * @example\r\n * ```ts\r\n * app.delete('/users/:id',\r\n * authMiddleware,\r\n * requireAllPermissions(['user.read', 'user.delete'], registry),\r\n * deleteUserHandler\r\n * );\r\n * ```\r\n */\r\nexport function requireAllPermissions(\r\n permissions: readonly string[],\r\n registry: RoleRegistry,\r\n options: PermissionMiddlewareOptions = {}\r\n): ExpressMiddleware {\r\n const opts = { ...DEFAULT_OPTIONS, ...options };\r\n\r\n return (req, res, next) => {\r\n const user = opts.getUser(req);\r\n\r\n if (canUserAll(user, permissions, registry)) {\r\n next();\r\n } else {\r\n res.status(opts.statusCode).json({ error: opts.message });\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Creates Express middleware that requires ANY of the specified permissions.\r\n *\r\n * @param permissions - Array of permissions (user needs at least one)\r\n * @param registry - Role registry\r\n * @param options - Optional configuration\r\n * @returns Express middleware function\r\n *\r\n * @example\r\n * ```ts\r\n * app.get('/reports',\r\n * authMiddleware,\r\n * requireAnyPermission(['report.admin', 'report.viewer'], registry),\r\n * getReportsHandler\r\n * );\r\n * ```\r\n */\r\nexport function requireAnyPermission(\r\n permissions: readonly string[],\r\n registry: RoleRegistry,\r\n options: PermissionMiddlewareOptions = {}\r\n): ExpressMiddleware {\r\n const opts = { ...DEFAULT_OPTIONS, ...options };\r\n\r\n return (req, res, next) => {\r\n const user = opts.getUser(req);\r\n\r\n if (canUserAny(user, permissions, registry)) {\r\n next();\r\n } else {\r\n res.status(opts.statusCode).json({ error: opts.message });\r\n }\r\n };\r\n}\r\n","/**\r\n * Secure Role Guard - Next.js Adapter\r\n *\r\n * Thin wrapper for Next.js API routes and Route Handlers.\r\n * Delegates all authorization logic to the core permission engine.\r\n *\r\n * IMPORTANT: This adapter does NOT handle authentication.\r\n * You must provide the user context from your auth system.\r\n */\r\n\r\nimport type { UserContext, RoleRegistry } from \"../core/types\";\r\nimport { canUser, canUserAll, canUserAny } from \"../core/permission-engine\";\r\n\r\n/**\r\n * Next.js Request type (minimal interface for App Router).\r\n * Using minimal interface to avoid requiring next as a dependency.\r\n */\r\nexport interface NextRequest {\r\n /** Headers from the request */\r\n headers: {\r\n get(name: string): string | null;\r\n };\r\n}\r\n\r\n/**\r\n * Next.js Response helper for App Router.\r\n */\r\nexport interface NextResponse {\r\n json(body: unknown, init?: { status?: number }): Response;\r\n}\r\n\r\n/**\r\n * Result of a permission check in Next.js context.\r\n */\r\nexport type PermissionResult = {\r\n readonly allowed: boolean;\r\n readonly user: UserContext | null;\r\n};\r\n\r\n/**\r\n * Options for Next.js permission wrappers.\r\n */\r\nexport type NextPermissionOptions = {\r\n /** Function to extract user context from request */\r\n readonly getUser: (\r\n request: NextRequest\r\n ) => UserContext | null | Promise<UserContext | null>;\r\n /** HTTP status code on denial (default: 403) */\r\n readonly statusCode?: number;\r\n /** Error message on denial (default: 'Forbidden') */\r\n readonly message?: string;\r\n};\r\n\r\n/**\r\n * Checks if a user has permission in a Next.js API context.\r\n *\r\n * This is a pure check function - you handle the response.\r\n *\r\n * @param user - User context\r\n * @param permission - Required permission\r\n * @param registry - Role registry\r\n * @returns PermissionResult\r\n *\r\n * @example\r\n * ```ts\r\n * // app/api/users/route.ts\r\n * import { defineRoles } from 'secure-role-guard/core';\r\n * import { checkNextPermission } from 'secure-role-guard/adapters/nextjs';\r\n *\r\n * const registry = defineRoles({ admin: ['user.update'] });\r\n *\r\n * export async function PUT(request: NextRequest) {\r\n * const user = await getUser(request); // Your auth function\r\n * const result = checkNextPermission(user, 'user.update', registry);\r\n *\r\n * if (!result.allowed) {\r\n * return NextResponse.json({ error: 'Forbidden' }, { status: 403 });\r\n * }\r\n *\r\n * // Handle the request...\r\n * }\r\n * ```\r\n */\r\nexport function checkNextPermission(\r\n user: UserContext | null | undefined,\r\n permission: string,\r\n registry: RoleRegistry\r\n): PermissionResult {\r\n return {\r\n allowed: canUser(user, permission, registry),\r\n user: user ?? null,\r\n };\r\n}\r\n\r\n/**\r\n * Checks if a user has ALL specified permissions.\r\n *\r\n * @param user - User context\r\n * @param permissions - Required permissions\r\n * @param registry - Role registry\r\n * @returns PermissionResult\r\n */\r\nexport function checkNextPermissionAll(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): PermissionResult {\r\n return {\r\n allowed: canUserAll(user, permissions, registry),\r\n user: user ?? null,\r\n };\r\n}\r\n\r\n/**\r\n * Checks if a user has ANY of the specified permissions.\r\n *\r\n * @param user - User context\r\n * @param permissions - Permissions to check\r\n * @param registry - Role registry\r\n * @returns PermissionResult\r\n */\r\nexport function checkNextPermissionAny(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): PermissionResult {\r\n return {\r\n allowed: canUserAny(user, permissions, registry),\r\n user: user ?? null,\r\n };\r\n}\r\n\r\n/**\r\n * Higher-order function that wraps a Next.js Route Handler with permission check.\r\n *\r\n * @param permission - Required permission\r\n * @param registry - Role registry\r\n * @param options - Configuration options\r\n * @param handler - The route handler to wrap\r\n * @returns Wrapped route handler\r\n *\r\n * @example\r\n * ```ts\r\n * // app/api/admin/route.ts\r\n * import { defineRoles } from 'secure-role-guard/core';\r\n * import { withPermission } from 'secure-role-guard/adapters/nextjs';\r\n *\r\n * const registry = defineRoles({ admin: ['admin.access'] });\r\n *\r\n * async function getUser(req: NextRequest) {\r\n * // Your auth logic to extract user from session/token\r\n * return { roles: ['admin'] };\r\n * }\r\n *\r\n * export const GET = withPermission(\r\n * 'admin.access',\r\n * registry,\r\n * { getUser },\r\n * async (request, user) => {\r\n * return Response.json({ message: 'Welcome, admin!' });\r\n * }\r\n * );\r\n * ```\r\n */\r\nexport function withPermission<T extends NextRequest>(\r\n permission: string,\r\n registry: RoleRegistry,\r\n options: NextPermissionOptions,\r\n handler: (request: T, user: UserContext) => Response | Promise<Response>\r\n): (request: T) => Promise<Response> {\r\n const statusCode = options.statusCode ?? 403;\r\n const message = options.message ?? \"Forbidden\";\r\n\r\n return async (request: T): Promise<Response> => {\r\n const user = await options.getUser(request);\r\n\r\n if (user === null || !canUser(user, permission, registry)) {\r\n return Response.json({ error: message }, { status: statusCode });\r\n }\r\n\r\n return handler(request, user);\r\n };\r\n}\r\n\r\n/**\r\n * Higher-order function that wraps a handler requiring ALL permissions.\r\n */\r\nexport function withAllPermissions<T extends NextRequest>(\r\n permissions: readonly string[],\r\n registry: RoleRegistry,\r\n options: NextPermissionOptions,\r\n handler: (request: T, user: UserContext) => Response | Promise<Response>\r\n): (request: T) => Promise<Response> {\r\n const statusCode = options.statusCode ?? 403;\r\n const message = options.message ?? \"Forbidden\";\r\n\r\n return async (request: T): Promise<Response> => {\r\n const user = await options.getUser(request);\r\n\r\n if (user === null || !canUserAll(user, permissions, registry)) {\r\n return Response.json({ error: message }, { status: statusCode });\r\n }\r\n\r\n return handler(request, user);\r\n };\r\n}\r\n\r\n/**\r\n * Higher-order function that wraps a handler requiring ANY permission.\r\n */\r\nexport function withAnyPermission<T extends NextRequest>(\r\n permissions: readonly string[],\r\n registry: RoleRegistry,\r\n options: NextPermissionOptions,\r\n handler: (request: T, user: UserContext) => Response | Promise<Response>\r\n): (request: T) => Promise<Response> {\r\n const statusCode = options.statusCode ?? 403;\r\n const message = options.message ?? \"Forbidden\";\r\n\r\n return async (request: T): Promise<Response> => {\r\n const user = await options.getUser(request);\r\n\r\n if (user === null || !canUserAny(user, permissions, registry)) {\r\n return Response.json({ error: message }, { status: statusCode });\r\n }\r\n\r\n return handler(request, user);\r\n };\r\n}\r\n"]}
@@ -0,0 +1,140 @@
1
+ import { U as UserContext, a as RoleRegistry } from '../types-CSUpaGsY.mjs';
2
+
3
+ /**
4
+ * Secure Role Guard - Next.js Adapter
5
+ *
6
+ * Thin wrapper for Next.js API routes and Route Handlers.
7
+ * Delegates all authorization logic to the core permission engine.
8
+ *
9
+ * IMPORTANT: This adapter does NOT handle authentication.
10
+ * You must provide the user context from your auth system.
11
+ */
12
+
13
+ /**
14
+ * Next.js Request type (minimal interface for App Router).
15
+ * Using minimal interface to avoid requiring next as a dependency.
16
+ */
17
+ interface NextRequest {
18
+ /** Headers from the request */
19
+ headers: {
20
+ get(name: string): string | null;
21
+ };
22
+ }
23
+ /**
24
+ * Next.js Response helper for App Router.
25
+ */
26
+ interface NextResponse {
27
+ json(body: unknown, init?: {
28
+ status?: number;
29
+ }): Response;
30
+ }
31
+ /**
32
+ * Result of a permission check in Next.js context.
33
+ */
34
+ type PermissionResult = {
35
+ readonly allowed: boolean;
36
+ readonly user: UserContext | null;
37
+ };
38
+ /**
39
+ * Options for Next.js permission wrappers.
40
+ */
41
+ type NextPermissionOptions = {
42
+ /** Function to extract user context from request */
43
+ readonly getUser: (request: NextRequest) => UserContext | null | Promise<UserContext | null>;
44
+ /** HTTP status code on denial (default: 403) */
45
+ readonly statusCode?: number;
46
+ /** Error message on denial (default: 'Forbidden') */
47
+ readonly message?: string;
48
+ };
49
+ /**
50
+ * Checks if a user has permission in a Next.js API context.
51
+ *
52
+ * This is a pure check function - you handle the response.
53
+ *
54
+ * @param user - User context
55
+ * @param permission - Required permission
56
+ * @param registry - Role registry
57
+ * @returns PermissionResult
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * // app/api/users/route.ts
62
+ * import { defineRoles } from 'secure-role-guard/core';
63
+ * import { checkNextPermission } from 'secure-role-guard/adapters/nextjs';
64
+ *
65
+ * const registry = defineRoles({ admin: ['user.update'] });
66
+ *
67
+ * export async function PUT(request: NextRequest) {
68
+ * const user = await getUser(request); // Your auth function
69
+ * const result = checkNextPermission(user, 'user.update', registry);
70
+ *
71
+ * if (!result.allowed) {
72
+ * return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
73
+ * }
74
+ *
75
+ * // Handle the request...
76
+ * }
77
+ * ```
78
+ */
79
+ declare function checkNextPermission(user: UserContext | null | undefined, permission: string, registry: RoleRegistry): PermissionResult;
80
+ /**
81
+ * Checks if a user has ALL specified permissions.
82
+ *
83
+ * @param user - User context
84
+ * @param permissions - Required permissions
85
+ * @param registry - Role registry
86
+ * @returns PermissionResult
87
+ */
88
+ declare function checkNextPermissionAll(user: UserContext | null | undefined, permissions: readonly string[], registry: RoleRegistry): PermissionResult;
89
+ /**
90
+ * Checks if a user has ANY of the specified permissions.
91
+ *
92
+ * @param user - User context
93
+ * @param permissions - Permissions to check
94
+ * @param registry - Role registry
95
+ * @returns PermissionResult
96
+ */
97
+ declare function checkNextPermissionAny(user: UserContext | null | undefined, permissions: readonly string[], registry: RoleRegistry): PermissionResult;
98
+ /**
99
+ * Higher-order function that wraps a Next.js Route Handler with permission check.
100
+ *
101
+ * @param permission - Required permission
102
+ * @param registry - Role registry
103
+ * @param options - Configuration options
104
+ * @param handler - The route handler to wrap
105
+ * @returns Wrapped route handler
106
+ *
107
+ * @example
108
+ * ```ts
109
+ * // app/api/admin/route.ts
110
+ * import { defineRoles } from 'secure-role-guard/core';
111
+ * import { withPermission } from 'secure-role-guard/adapters/nextjs';
112
+ *
113
+ * const registry = defineRoles({ admin: ['admin.access'] });
114
+ *
115
+ * async function getUser(req: NextRequest) {
116
+ * // Your auth logic to extract user from session/token
117
+ * return { roles: ['admin'] };
118
+ * }
119
+ *
120
+ * export const GET = withPermission(
121
+ * 'admin.access',
122
+ * registry,
123
+ * { getUser },
124
+ * async (request, user) => {
125
+ * return Response.json({ message: 'Welcome, admin!' });
126
+ * }
127
+ * );
128
+ * ```
129
+ */
130
+ declare function withPermission<T extends NextRequest>(permission: string, registry: RoleRegistry, options: NextPermissionOptions, handler: (request: T, user: UserContext) => Response | Promise<Response>): (request: T) => Promise<Response>;
131
+ /**
132
+ * Higher-order function that wraps a handler requiring ALL permissions.
133
+ */
134
+ declare function withAllPermissions<T extends NextRequest>(permissions: readonly string[], registry: RoleRegistry, options: NextPermissionOptions, handler: (request: T, user: UserContext) => Response | Promise<Response>): (request: T) => Promise<Response>;
135
+ /**
136
+ * Higher-order function that wraps a handler requiring ANY permission.
137
+ */
138
+ declare function withAnyPermission<T extends NextRequest>(permissions: readonly string[], registry: RoleRegistry, options: NextPermissionOptions, handler: (request: T, user: UserContext) => Response | Promise<Response>): (request: T) => Promise<Response>;
139
+
140
+ export { type NextPermissionOptions, type NextRequest, type NextResponse, type PermissionResult, checkNextPermission, checkNextPermissionAll, checkNextPermissionAny, withAllPermissions, withAnyPermission, withPermission };