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.
- package/LICENSE +21 -0
- package/README.md +813 -0
- package/dist/adapters/express.d.mts +109 -0
- package/dist/adapters/express.d.ts +109 -0
- package/dist/adapters/express.js +122 -0
- package/dist/adapters/express.js.map +1 -0
- package/dist/adapters/express.mjs +118 -0
- package/dist/adapters/express.mjs.map +1 -0
- package/dist/adapters/index.d.mts +3 -0
- package/dist/adapters/index.d.ts +3 -0
- package/dist/adapters/index.js +181 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/index.mjs +171 -0
- package/dist/adapters/index.mjs.map +1 -0
- package/dist/adapters/nextjs.d.mts +140 -0
- package/dist/adapters/nextjs.d.ts +140 -0
- package/dist/adapters/nextjs.js +138 -0
- package/dist/adapters/nextjs.js.map +1 -0
- package/dist/adapters/nextjs.mjs +131 -0
- package/dist/adapters/nextjs.mjs.map +1 -0
- package/dist/core/index.d.mts +100 -0
- package/dist/core/index.d.ts +100 -0
- package/dist/core/index.js +132 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/index.mjs +125 -0
- package/dist/core/index.mjs.map +1 -0
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +238 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +222 -0
- package/dist/index.mjs.map +1 -0
- package/dist/react/index.d.mts +237 -0
- package/dist/react/index.d.ts +237 -0
- package/dist/react/index.js +177 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/index.mjs +167 -0
- package/dist/react/index.mjs.map +1 -0
- package/dist/types-CSUpaGsY.d.mts +76 -0
- package/dist/types-CSUpaGsY.d.ts +76 -0
- package/package.json +99 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { U as UserContext, a as RoleRegistry } from '../types-CSUpaGsY.js';
|
|
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 };
|
|
@@ -0,0 +1,138 @@
|
|
|
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/nextjs.ts
|
|
79
|
+
function checkNextPermission(user, permission, registry) {
|
|
80
|
+
return {
|
|
81
|
+
allowed: canUser(user, permission, registry),
|
|
82
|
+
user: user ?? null
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function checkNextPermissionAll(user, permissions, registry) {
|
|
86
|
+
return {
|
|
87
|
+
allowed: canUserAll(user, permissions, registry),
|
|
88
|
+
user: user ?? null
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function checkNextPermissionAny(user, permissions, registry) {
|
|
92
|
+
return {
|
|
93
|
+
allowed: canUserAny(user, permissions, registry),
|
|
94
|
+
user: user ?? null
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function withPermission(permission, registry, options, handler) {
|
|
98
|
+
const statusCode = options.statusCode ?? 403;
|
|
99
|
+
const message = options.message ?? "Forbidden";
|
|
100
|
+
return async (request) => {
|
|
101
|
+
const user = await options.getUser(request);
|
|
102
|
+
if (user === null || !canUser(user, permission, registry)) {
|
|
103
|
+
return Response.json({ error: message }, { status: statusCode });
|
|
104
|
+
}
|
|
105
|
+
return handler(request, user);
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function withAllPermissions(permissions, registry, options, handler) {
|
|
109
|
+
const statusCode = options.statusCode ?? 403;
|
|
110
|
+
const message = options.message ?? "Forbidden";
|
|
111
|
+
return async (request) => {
|
|
112
|
+
const user = await options.getUser(request);
|
|
113
|
+
if (user === null || !canUserAll(user, permissions, registry)) {
|
|
114
|
+
return Response.json({ error: message }, { status: statusCode });
|
|
115
|
+
}
|
|
116
|
+
return handler(request, user);
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function withAnyPermission(permissions, registry, options, handler) {
|
|
120
|
+
const statusCode = options.statusCode ?? 403;
|
|
121
|
+
const message = options.message ?? "Forbidden";
|
|
122
|
+
return async (request) => {
|
|
123
|
+
const user = await options.getUser(request);
|
|
124
|
+
if (user === null || !canUserAny(user, permissions, registry)) {
|
|
125
|
+
return Response.json({ error: message }, { status: statusCode });
|
|
126
|
+
}
|
|
127
|
+
return handler(request, user);
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
exports.checkNextPermission = checkNextPermission;
|
|
132
|
+
exports.checkNextPermissionAll = checkNextPermissionAll;
|
|
133
|
+
exports.checkNextPermissionAny = checkNextPermissionAny;
|
|
134
|
+
exports.withAllPermissions = withAllPermissions;
|
|
135
|
+
exports.withAnyPermission = withAnyPermission;
|
|
136
|
+
exports.withPermission = withPermission;
|
|
137
|
+
//# sourceMappingURL=nextjs.js.map
|
|
138
|
+
//# sourceMappingURL=nextjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/permission-engine.ts","../../src/adapters/nextjs.ts"],"names":[],"mappings":"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","file":"nextjs.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 - 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,131 @@
|
|
|
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/nextjs.ts
|
|
77
|
+
function checkNextPermission(user, permission, registry) {
|
|
78
|
+
return {
|
|
79
|
+
allowed: canUser(user, permission, registry),
|
|
80
|
+
user: user ?? null
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function checkNextPermissionAll(user, permissions, registry) {
|
|
84
|
+
return {
|
|
85
|
+
allowed: canUserAll(user, permissions, registry),
|
|
86
|
+
user: user ?? null
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function checkNextPermissionAny(user, permissions, registry) {
|
|
90
|
+
return {
|
|
91
|
+
allowed: canUserAny(user, permissions, registry),
|
|
92
|
+
user: user ?? null
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function withPermission(permission, registry, options, handler) {
|
|
96
|
+
const statusCode = options.statusCode ?? 403;
|
|
97
|
+
const message = options.message ?? "Forbidden";
|
|
98
|
+
return async (request) => {
|
|
99
|
+
const user = await options.getUser(request);
|
|
100
|
+
if (user === null || !canUser(user, permission, registry)) {
|
|
101
|
+
return Response.json({ error: message }, { status: statusCode });
|
|
102
|
+
}
|
|
103
|
+
return handler(request, user);
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function withAllPermissions(permissions, registry, options, handler) {
|
|
107
|
+
const statusCode = options.statusCode ?? 403;
|
|
108
|
+
const message = options.message ?? "Forbidden";
|
|
109
|
+
return async (request) => {
|
|
110
|
+
const user = await options.getUser(request);
|
|
111
|
+
if (user === null || !canUserAll(user, permissions, registry)) {
|
|
112
|
+
return Response.json({ error: message }, { status: statusCode });
|
|
113
|
+
}
|
|
114
|
+
return handler(request, user);
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function withAnyPermission(permissions, registry, options, handler) {
|
|
118
|
+
const statusCode = options.statusCode ?? 403;
|
|
119
|
+
const message = options.message ?? "Forbidden";
|
|
120
|
+
return async (request) => {
|
|
121
|
+
const user = await options.getUser(request);
|
|
122
|
+
if (user === null || !canUserAny(user, permissions, registry)) {
|
|
123
|
+
return Response.json({ error: message }, { status: statusCode });
|
|
124
|
+
}
|
|
125
|
+
return handler(request, user);
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export { checkNextPermission, checkNextPermissionAll, checkNextPermissionAny, withAllPermissions, withAnyPermission, withPermission };
|
|
130
|
+
//# sourceMappingURL=nextjs.mjs.map
|
|
131
|
+
//# sourceMappingURL=nextjs.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/permission-engine.ts","../../src/adapters/nextjs.ts"],"names":[],"mappings":"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","file":"nextjs.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 - 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,100 @@
|
|
|
1
|
+
import { R as RoleDefinition, a as RoleRegistry, U as UserContext, b as PermissionCheckResult } from '../types-CSUpaGsY.mjs';
|
|
2
|
+
export { c as PermissionCheckOptions, P as PermissionEngineConfig } from '../types-CSUpaGsY.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Secure Role Guard - Role Registry
|
|
6
|
+
*
|
|
7
|
+
* Creates an immutable registry of role definitions.
|
|
8
|
+
* Pure functions only, zero side effects.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Creates an immutable role registry from role definitions.
|
|
13
|
+
*
|
|
14
|
+
* Security guarantees:
|
|
15
|
+
* - Returns a frozen, immutable object
|
|
16
|
+
* - No mutations possible after creation
|
|
17
|
+
* - Unknown roles return empty permissions (deny by default)
|
|
18
|
+
*
|
|
19
|
+
* @param definitions - Object mapping role names to permission arrays
|
|
20
|
+
* @returns Frozen RoleRegistry object
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* const registry = defineRoles({
|
|
24
|
+
* superadmin: ['*'],
|
|
25
|
+
* admin: ['user.read', 'user.update'],
|
|
26
|
+
* support: ['ticket.read', 'ticket.reply']
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* registry.getPermissions('admin'); // ['user.read', 'user.update']
|
|
30
|
+
* registry.getPermissions('unknown'); // []
|
|
31
|
+
*/
|
|
32
|
+
declare function defineRoles(definitions: RoleDefinition): RoleRegistry;
|
|
33
|
+
/**
|
|
34
|
+
* Creates an empty role registry.
|
|
35
|
+
* Useful for testing or when no roles are defined.
|
|
36
|
+
*
|
|
37
|
+
* @returns Empty frozen RoleRegistry
|
|
38
|
+
*/
|
|
39
|
+
declare function createEmptyRegistry(): RoleRegistry;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Secure Role Guard - Permission Engine
|
|
43
|
+
*
|
|
44
|
+
* Pure functions for permission checking.
|
|
45
|
+
* Zero side effects, zero dependencies, zero mutations.
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Checks if a user has a specific permission.
|
|
50
|
+
*
|
|
51
|
+
* SECURITY GUARANTEES:
|
|
52
|
+
* - Deny by default: undefined/null/empty always returns false
|
|
53
|
+
* - Pure function: no side effects, no mutations
|
|
54
|
+
* - Deterministic: same inputs always produce same output
|
|
55
|
+
*
|
|
56
|
+
* @param user - The user context to check
|
|
57
|
+
* @param permission - The permission required
|
|
58
|
+
* @param registry - The role registry for resolving roles
|
|
59
|
+
* @returns True if user has the permission, false otherwise
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* const canEdit = canUser(currentUser, 'user.update', roleRegistry);
|
|
63
|
+
*/
|
|
64
|
+
declare function canUser(user: UserContext | null | undefined, permission: string, registry: RoleRegistry): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Checks if a user has ALL of the specified permissions.
|
|
67
|
+
*
|
|
68
|
+
* @param user - The user context to check
|
|
69
|
+
* @param permissions - Array of permissions required
|
|
70
|
+
* @param registry - The role registry
|
|
71
|
+
* @returns True if user has ALL permissions
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* const canManage = canUserAll(user, ['user.read', 'user.update'], registry);
|
|
75
|
+
*/
|
|
76
|
+
declare function canUserAll(user: UserContext | null | undefined, permissions: readonly string[], registry: RoleRegistry): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Checks if a user has ANY of the specified permissions.
|
|
79
|
+
*
|
|
80
|
+
* @param user - The user context to check
|
|
81
|
+
* @param permissions - Array of permissions to check
|
|
82
|
+
* @param registry - The role registry
|
|
83
|
+
* @returns True if user has at least one permission
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* const canView = canUserAny(user, ['report.view', 'report.admin'], registry);
|
|
87
|
+
*/
|
|
88
|
+
declare function canUserAny(user: UserContext | null | undefined, permissions: readonly string[], registry: RoleRegistry): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Extended permission check with detailed result.
|
|
91
|
+
* Useful for debugging and logging.
|
|
92
|
+
*
|
|
93
|
+
* @param user - The user context
|
|
94
|
+
* @param permission - Permission to check
|
|
95
|
+
* @param registry - The role registry
|
|
96
|
+
* @returns PermissionCheckResult with allowed status and reason
|
|
97
|
+
*/
|
|
98
|
+
declare function checkPermission(user: UserContext | null | undefined, permission: string, registry: RoleRegistry): PermissionCheckResult;
|
|
99
|
+
|
|
100
|
+
export { PermissionCheckResult, RoleDefinition, RoleRegistry, UserContext, canUser, canUserAll, canUserAny, checkPermission, createEmptyRegistry, defineRoles };
|