@noony-serverless/core 0.1.0 → 0.1.5
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/build/middlewares/authenticationMiddleware.d.ts +379 -0
- package/build/middlewares/authenticationMiddleware.js +216 -0
- package/build/middlewares/bodyParserMiddleware.d.ts +99 -0
- package/build/middlewares/bodyParserMiddleware.js +99 -0
- package/build/middlewares/bodyValidationMiddleware.d.ts +68 -4
- package/build/middlewares/bodyValidationMiddleware.js +64 -0
- package/build/middlewares/dependencyInjectionMiddleware.d.ts +238 -0
- package/build/middlewares/dependencyInjectionMiddleware.js +238 -0
- package/build/middlewares/errorHandlerMiddleware.d.ts +94 -0
- package/build/middlewares/errorHandlerMiddleware.js +105 -0
- package/build/middlewares/guards/RouteGuards.d.ts +475 -0
- package/build/middlewares/guards/RouteGuards.js +604 -0
- package/build/middlewares/guards/cache/CacheAdapter.d.ts +473 -0
- package/build/middlewares/guards/cache/CacheAdapter.js +205 -0
- package/build/middlewares/guards/cache/ConservativeCacheInvalidation.d.ts +191 -0
- package/build/middlewares/guards/cache/ConservativeCacheInvalidation.js +510 -0
- package/build/middlewares/guards/cache/MemoryCacheAdapter.d.ts +228 -0
- package/build/middlewares/guards/cache/MemoryCacheAdapter.js +403 -0
- package/build/middlewares/guards/cache/NoopCacheAdapter.d.ts +95 -0
- package/build/middlewares/guards/cache/NoopCacheAdapter.js +131 -0
- package/build/middlewares/guards/config/GuardConfiguration.d.ts +612 -0
- package/build/middlewares/guards/config/GuardConfiguration.js +334 -0
- package/build/middlewares/guards/guards/FastAuthGuard.d.ts +201 -0
- package/build/middlewares/guards/guards/FastAuthGuard.js +460 -0
- package/build/middlewares/guards/guards/PermissionGuardFactory.d.ts +202 -0
- package/build/middlewares/guards/guards/PermissionGuardFactory.js +563 -0
- package/build/middlewares/guards/index.d.ts +67 -0
- package/build/middlewares/guards/index.js +192 -0
- package/build/middlewares/guards/registry/PermissionRegistry.d.ts +188 -0
- package/build/middlewares/guards/registry/PermissionRegistry.js +425 -0
- package/build/middlewares/guards/resolvers/ExpressionPermissionResolver.d.ts +129 -0
- package/build/middlewares/guards/resolvers/ExpressionPermissionResolver.js +451 -0
- package/build/middlewares/guards/resolvers/PermissionResolver.d.ts +155 -0
- package/build/middlewares/guards/resolvers/PermissionResolver.js +176 -0
- package/build/middlewares/guards/resolvers/PlainPermissionResolver.d.ts +101 -0
- package/build/middlewares/guards/resolvers/PlainPermissionResolver.js +248 -0
- package/build/middlewares/guards/resolvers/WildcardPermissionResolver.d.ts +146 -0
- package/build/middlewares/guards/resolvers/WildcardPermissionResolver.js +377 -0
- package/build/middlewares/guards/services/FastUserContextService.d.ts +216 -0
- package/build/middlewares/guards/services/FastUserContextService.js +435 -0
- package/build/middlewares/headerVariablesMiddleware.d.ts +118 -0
- package/build/middlewares/headerVariablesMiddleware.js +118 -0
- package/build/middlewares/httpAttributesMiddleware.d.ts +235 -0
- package/build/middlewares/httpAttributesMiddleware.js +235 -0
- package/build/middlewares/index.d.ts +1 -0
- package/build/middlewares/index.js +1 -0
- package/build/middlewares/queryParametersMiddleware.d.ts +105 -0
- package/build/middlewares/queryParametersMiddleware.js +105 -0
- package/build/middlewares/rateLimitingMiddleware.d.ts +109 -5
- package/build/middlewares/rateLimitingMiddleware.js +109 -5
- package/build/middlewares/responseWrapperMiddleware.d.ts +170 -1
- package/build/middlewares/responseWrapperMiddleware.js +170 -1
- package/build/middlewares/securityAuditMiddleware.js +5 -5
- package/build/middlewares/validationMiddleware.d.ts +145 -0
- package/build/middlewares/validationMiddleware.js +145 -0
- package/package.json +2 -2
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Permission Resolution System
|
|
4
|
+
*
|
|
5
|
+
* Base interfaces and types for the three distinct permission resolution strategies:
|
|
6
|
+
* - PlainPermissionResolver: Direct O(1) set membership checks
|
|
7
|
+
* - WildcardPermissionResolver: Pattern matching with configurable strategies
|
|
8
|
+
* - ExpressionPermissionResolver: Complex AND/OR/NOT expression evaluation
|
|
9
|
+
*
|
|
10
|
+
* Each resolver operates independently and cannot be combined on a single endpoint,
|
|
11
|
+
* ensuring clear performance characteristics and simplified reasoning.
|
|
12
|
+
*
|
|
13
|
+
* @author Noony Framework Team
|
|
14
|
+
* @version 1.0.0
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.PermissionUtils = exports.PermissionResolverType = exports.PermissionResolver = void 0;
|
|
18
|
+
/**
|
|
19
|
+
* Base interface for all permission resolvers
|
|
20
|
+
*
|
|
21
|
+
* Each resolver implements this interface with specific logic for handling
|
|
22
|
+
* different types of permission requirements. The generic type T represents
|
|
23
|
+
* the specific requirement format for each resolver.
|
|
24
|
+
*/
|
|
25
|
+
class PermissionResolver {
|
|
26
|
+
}
|
|
27
|
+
exports.PermissionResolver = PermissionResolver;
|
|
28
|
+
/**
|
|
29
|
+
* Permission resolver types for identification
|
|
30
|
+
*/
|
|
31
|
+
var PermissionResolverType;
|
|
32
|
+
(function (PermissionResolverType) {
|
|
33
|
+
PermissionResolverType["PLAIN"] = "plain";
|
|
34
|
+
PermissionResolverType["WILDCARD"] = "wildcard";
|
|
35
|
+
PermissionResolverType["EXPRESSION"] = "expression";
|
|
36
|
+
})(PermissionResolverType || (exports.PermissionResolverType = PermissionResolverType = {}));
|
|
37
|
+
/**
|
|
38
|
+
* Utility functions for working with permissions
|
|
39
|
+
*/
|
|
40
|
+
class PermissionUtils {
|
|
41
|
+
/**
|
|
42
|
+
* Convert array of permissions to Set for O(1) lookups
|
|
43
|
+
*
|
|
44
|
+
* @param permissions - Array of permission strings
|
|
45
|
+
* @returns Set of permissions for fast membership testing
|
|
46
|
+
*/
|
|
47
|
+
static arrayToSet(permissions) {
|
|
48
|
+
return new Set(permissions);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Validate permission string format
|
|
52
|
+
*
|
|
53
|
+
* Ensures permissions follow expected naming conventions:
|
|
54
|
+
* - 2-3 levels separated by dots
|
|
55
|
+
* - Only alphanumeric characters, dots, and hyphens
|
|
56
|
+
* - No leading/trailing dots
|
|
57
|
+
*
|
|
58
|
+
* @param permission - Permission string to validate
|
|
59
|
+
* @returns true if valid, false otherwise
|
|
60
|
+
*/
|
|
61
|
+
static isValidPermission(permission) {
|
|
62
|
+
if (!permission || typeof permission !== 'string') {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
// Check for valid format: 2-3 levels, alphanumeric + dots + hyphens
|
|
66
|
+
const validPattern = /^[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+){1,2}$/;
|
|
67
|
+
// Check for wildcard format: ends with .* for wildcards
|
|
68
|
+
const wildcardPattern = /^[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.\*$/;
|
|
69
|
+
return validPattern.test(permission) || wildcardPattern.test(permission);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Extract base permission from wildcard pattern
|
|
73
|
+
*
|
|
74
|
+
* @param wildcardPermission - Wildcard permission like "admin.*"
|
|
75
|
+
* @returns Base permission like "admin"
|
|
76
|
+
*/
|
|
77
|
+
static extractWildcardBase(wildcardPermission) {
|
|
78
|
+
if (wildcardPermission.endsWith('.*')) {
|
|
79
|
+
return wildcardPermission.slice(0, -2);
|
|
80
|
+
}
|
|
81
|
+
return wildcardPermission;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Check if a permission matches a wildcard pattern
|
|
85
|
+
*
|
|
86
|
+
* @param permission - Specific permission to test
|
|
87
|
+
* @param wildcardPattern - Wildcard pattern to match against
|
|
88
|
+
* @returns true if permission matches the pattern
|
|
89
|
+
*/
|
|
90
|
+
static matchesWildcard(permission, wildcardPattern) {
|
|
91
|
+
if (!wildcardPattern.includes('*')) {
|
|
92
|
+
return permission === wildcardPattern;
|
|
93
|
+
}
|
|
94
|
+
// Convert wildcard pattern to regex
|
|
95
|
+
// "admin.*" becomes /^admin\.[^.]+$/
|
|
96
|
+
// "admin.users.*" becomes /^admin\.users\.[^.]+$/
|
|
97
|
+
const regexPattern = wildcardPattern
|
|
98
|
+
.replace(/\./g, '\\.') // Escape dots
|
|
99
|
+
.replace(/\*/g, '[^.]+'); // Replace * with non-dot characters
|
|
100
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
101
|
+
return regex.test(permission);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Validate permission expression structure
|
|
105
|
+
*
|
|
106
|
+
* Ensures expressions follow the 2-level nesting limit and contain
|
|
107
|
+
* valid permission strings.
|
|
108
|
+
*
|
|
109
|
+
* @param expression - Permission expression to validate
|
|
110
|
+
* @param depth - Current nesting depth (internal use)
|
|
111
|
+
* @returns true if valid, false otherwise
|
|
112
|
+
*/
|
|
113
|
+
static isValidExpression(expression, depth = 0) {
|
|
114
|
+
if (depth > 2) {
|
|
115
|
+
return false; // Exceeds maximum nesting depth
|
|
116
|
+
}
|
|
117
|
+
// Must have exactly one operation type
|
|
118
|
+
const operationCount = [
|
|
119
|
+
expression.and ? 1 : 0,
|
|
120
|
+
expression.or ? 1 : 0,
|
|
121
|
+
expression.not ? 1 : 0,
|
|
122
|
+
expression.permission ? 1 : 0,
|
|
123
|
+
].reduce((sum, count) => sum + count, 0);
|
|
124
|
+
if (operationCount !== 1) {
|
|
125
|
+
return false; // Must have exactly one operation
|
|
126
|
+
}
|
|
127
|
+
// Validate leaf permission
|
|
128
|
+
if (expression.permission) {
|
|
129
|
+
return this.isValidPermission(expression.permission);
|
|
130
|
+
}
|
|
131
|
+
// Validate AND operation
|
|
132
|
+
if (expression.and) {
|
|
133
|
+
if (!Array.isArray(expression.and) || expression.and.length === 0) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
return expression.and.every((subExpr) => this.isValidExpression(subExpr, depth + 1));
|
|
137
|
+
}
|
|
138
|
+
// Validate OR operation
|
|
139
|
+
if (expression.or) {
|
|
140
|
+
if (!Array.isArray(expression.or) || expression.or.length === 0) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
return expression.or.every((subExpr) => this.isValidExpression(subExpr, depth + 1));
|
|
144
|
+
}
|
|
145
|
+
// Validate NOT operation
|
|
146
|
+
if (expression.not) {
|
|
147
|
+
return this.isValidExpression(expression.not, depth + 1);
|
|
148
|
+
}
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Generate a stable hash for caching permission expressions
|
|
153
|
+
*
|
|
154
|
+
* @param expression - Permission expression to hash
|
|
155
|
+
* @returns Stable string hash for cache keys
|
|
156
|
+
*/
|
|
157
|
+
static hashExpression(expression) {
|
|
158
|
+
// Sort keys to ensure consistent serialization
|
|
159
|
+
const sortedJson = JSON.stringify(expression, Object.keys(expression).sort());
|
|
160
|
+
return this.simpleHash(sortedJson);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Simple hash function for string data (not cryptographically secure)
|
|
164
|
+
*/
|
|
165
|
+
static simpleHash(str) {
|
|
166
|
+
let hash = 0;
|
|
167
|
+
for (let i = 0; i < str.length; i++) {
|
|
168
|
+
const char = str.charCodeAt(i);
|
|
169
|
+
hash = (hash << 5) - hash + char;
|
|
170
|
+
hash = hash & hash; // Convert to 32-bit integer
|
|
171
|
+
}
|
|
172
|
+
return hash.toString(36);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
exports.PermissionUtils = PermissionUtils;
|
|
176
|
+
//# sourceMappingURL=PermissionResolver.js.map
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plain Permission Resolver
|
|
3
|
+
*
|
|
4
|
+
* Fastest permission resolver using direct O(1) set membership checks.
|
|
5
|
+
* Optimized for high-performance scenarios where sub-millisecond permission
|
|
6
|
+
* checks are critical. No pattern matching or complex logic - just pure
|
|
7
|
+
* set-based lookups for maximum speed.
|
|
8
|
+
*
|
|
9
|
+
* Use Cases:
|
|
10
|
+
* - High-traffic API endpoints requiring maximum performance
|
|
11
|
+
* - Simple permission models without wildcards or expressions
|
|
12
|
+
* - Scenarios where all required permissions are known at compile time
|
|
13
|
+
* - Serverless functions with strict latency requirements
|
|
14
|
+
*
|
|
15
|
+
* Performance Characteristics:
|
|
16
|
+
* - Time Complexity: O(1) for permission lookup
|
|
17
|
+
* - Space Complexity: O(n) where n is the number of user permissions
|
|
18
|
+
* - Cache Utilization: None (no caching needed due to speed)
|
|
19
|
+
* - Memory Footprint: Minimal (uses JavaScript Set)
|
|
20
|
+
*
|
|
21
|
+
* @author Noony Framework Team
|
|
22
|
+
* @version 1.0.0
|
|
23
|
+
*/
|
|
24
|
+
import { PermissionResolver, PermissionResolverType, PerformanceCharacteristics, PermissionCheckResult } from './PermissionResolver';
|
|
25
|
+
/**
|
|
26
|
+
* Plain permission resolver for direct O(1) permission checks
|
|
27
|
+
*
|
|
28
|
+
* This resolver performs the simplest and fastest permission checks by
|
|
29
|
+
* using JavaScript Set's has() method for O(1) membership testing.
|
|
30
|
+
* It checks if the user has ANY of the required permissions (OR logic).
|
|
31
|
+
*/
|
|
32
|
+
export declare class PlainPermissionResolver extends PermissionResolver<string[]> {
|
|
33
|
+
private checkCount;
|
|
34
|
+
private totalResolutionTimeUs;
|
|
35
|
+
/**
|
|
36
|
+
* Check if user has any of the required permissions
|
|
37
|
+
*
|
|
38
|
+
* Uses OR logic: user needs only ONE of the required permissions
|
|
39
|
+
* to pass the check. This is the most common permission pattern.
|
|
40
|
+
*
|
|
41
|
+
* @param userPermissions - Set of user's permissions for O(1) lookup
|
|
42
|
+
* @param requiredPermissions - Array of required permissions (OR logic)
|
|
43
|
+
* @returns Promise resolving to true if user has any required permission
|
|
44
|
+
*/
|
|
45
|
+
check(userPermissions: Set<string>, requiredPermissions: string[]): Promise<boolean>;
|
|
46
|
+
/**
|
|
47
|
+
* Check permissions with detailed result information
|
|
48
|
+
*
|
|
49
|
+
* Provides additional metadata about the permission check for
|
|
50
|
+
* debugging, monitoring, and audit purposes.
|
|
51
|
+
*
|
|
52
|
+
* @param userPermissions - Set of user's permissions
|
|
53
|
+
* @param requiredPermissions - Array of required permissions
|
|
54
|
+
* @returns Detailed permission check result
|
|
55
|
+
*/
|
|
56
|
+
checkWithResult(userPermissions: Set<string>, requiredPermissions: string[]): Promise<PermissionCheckResult>;
|
|
57
|
+
/**
|
|
58
|
+
* Check if user has ALL required permissions (AND logic)
|
|
59
|
+
*
|
|
60
|
+
* Alternative method for scenarios requiring ALL permissions
|
|
61
|
+
* instead of ANY permission. Less commonly used but available
|
|
62
|
+
* for completeness.
|
|
63
|
+
*
|
|
64
|
+
* @param userPermissions - Set of user's permissions
|
|
65
|
+
* @param requiredPermissions - Array of ALL required permissions
|
|
66
|
+
* @returns Promise resolving to true if user has ALL permissions
|
|
67
|
+
*/
|
|
68
|
+
checkAllRequired(userPermissions: Set<string>, requiredPermissions: string[]): Promise<boolean>;
|
|
69
|
+
/**
|
|
70
|
+
* Get resolver type for identification
|
|
71
|
+
*/
|
|
72
|
+
getType(): PermissionResolverType;
|
|
73
|
+
/**
|
|
74
|
+
* Get performance characteristics for monitoring
|
|
75
|
+
*/
|
|
76
|
+
getPerformanceCharacteristics(): PerformanceCharacteristics;
|
|
77
|
+
/**
|
|
78
|
+
* Get performance statistics for monitoring
|
|
79
|
+
*/
|
|
80
|
+
getStats(): {
|
|
81
|
+
checkCount: number;
|
|
82
|
+
averageResolutionTimeUs: number;
|
|
83
|
+
totalResolutionTimeUs: number;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Reset performance statistics
|
|
87
|
+
*/
|
|
88
|
+
resetStats(): void;
|
|
89
|
+
/**
|
|
90
|
+
* Get resolver name for debugging
|
|
91
|
+
*/
|
|
92
|
+
getName(): string;
|
|
93
|
+
/**
|
|
94
|
+
* Check if this resolver can handle the given requirement type
|
|
95
|
+
*
|
|
96
|
+
* @param requirement - The requirement to check
|
|
97
|
+
* @returns true if this resolver can handle the requirement
|
|
98
|
+
*/
|
|
99
|
+
canHandle(requirement: any): requirement is string[];
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=PlainPermissionResolver.d.ts.map
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Plain Permission Resolver
|
|
4
|
+
*
|
|
5
|
+
* Fastest permission resolver using direct O(1) set membership checks.
|
|
6
|
+
* Optimized for high-performance scenarios where sub-millisecond permission
|
|
7
|
+
* checks are critical. No pattern matching or complex logic - just pure
|
|
8
|
+
* set-based lookups for maximum speed.
|
|
9
|
+
*
|
|
10
|
+
* Use Cases:
|
|
11
|
+
* - High-traffic API endpoints requiring maximum performance
|
|
12
|
+
* - Simple permission models without wildcards or expressions
|
|
13
|
+
* - Scenarios where all required permissions are known at compile time
|
|
14
|
+
* - Serverless functions with strict latency requirements
|
|
15
|
+
*
|
|
16
|
+
* Performance Characteristics:
|
|
17
|
+
* - Time Complexity: O(1) for permission lookup
|
|
18
|
+
* - Space Complexity: O(n) where n is the number of user permissions
|
|
19
|
+
* - Cache Utilization: None (no caching needed due to speed)
|
|
20
|
+
* - Memory Footprint: Minimal (uses JavaScript Set)
|
|
21
|
+
*
|
|
22
|
+
* @author Noony Framework Team
|
|
23
|
+
* @version 1.0.0
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.PlainPermissionResolver = void 0;
|
|
27
|
+
const PermissionResolver_1 = require("./PermissionResolver");
|
|
28
|
+
/**
|
|
29
|
+
* Plain permission resolver for direct O(1) permission checks
|
|
30
|
+
*
|
|
31
|
+
* This resolver performs the simplest and fastest permission checks by
|
|
32
|
+
* using JavaScript Set's has() method for O(1) membership testing.
|
|
33
|
+
* It checks if the user has ANY of the required permissions (OR logic).
|
|
34
|
+
*/
|
|
35
|
+
class PlainPermissionResolver extends PermissionResolver_1.PermissionResolver {
|
|
36
|
+
checkCount = 0;
|
|
37
|
+
totalResolutionTimeUs = 0;
|
|
38
|
+
/**
|
|
39
|
+
* Check if user has any of the required permissions
|
|
40
|
+
*
|
|
41
|
+
* Uses OR logic: user needs only ONE of the required permissions
|
|
42
|
+
* to pass the check. This is the most common permission pattern.
|
|
43
|
+
*
|
|
44
|
+
* @param userPermissions - Set of user's permissions for O(1) lookup
|
|
45
|
+
* @param requiredPermissions - Array of required permissions (OR logic)
|
|
46
|
+
* @returns Promise resolving to true if user has any required permission
|
|
47
|
+
*/
|
|
48
|
+
async check(userPermissions, requiredPermissions) {
|
|
49
|
+
const startTime = process.hrtime.bigint();
|
|
50
|
+
try {
|
|
51
|
+
// Validate inputs
|
|
52
|
+
if (!userPermissions || userPermissions.size === 0) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
if (!requiredPermissions || requiredPermissions.length === 0) {
|
|
56
|
+
return false; // No permissions required means access denied
|
|
57
|
+
}
|
|
58
|
+
// Validate permission format
|
|
59
|
+
for (const permission of requiredPermissions) {
|
|
60
|
+
if (!PermissionResolver_1.PermissionUtils.isValidPermission(permission)) {
|
|
61
|
+
throw new Error(`Invalid permission format: ${permission}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// O(1) set membership check for each required permission
|
|
65
|
+
// Return true as soon as we find a match (short-circuit OR)
|
|
66
|
+
for (const requiredPermission of requiredPermissions) {
|
|
67
|
+
if (userPermissions.has(requiredPermission)) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
// Track performance metrics
|
|
75
|
+
const endTime = process.hrtime.bigint();
|
|
76
|
+
const resolutionTimeUs = Number(endTime - startTime) / 1000; // Convert to microseconds
|
|
77
|
+
this.checkCount++;
|
|
78
|
+
this.totalResolutionTimeUs += resolutionTimeUs;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Check permissions with detailed result information
|
|
83
|
+
*
|
|
84
|
+
* Provides additional metadata about the permission check for
|
|
85
|
+
* debugging, monitoring, and audit purposes.
|
|
86
|
+
*
|
|
87
|
+
* @param userPermissions - Set of user's permissions
|
|
88
|
+
* @param requiredPermissions - Array of required permissions
|
|
89
|
+
* @returns Detailed permission check result
|
|
90
|
+
*/
|
|
91
|
+
async checkWithResult(userPermissions, requiredPermissions) {
|
|
92
|
+
const startTime = process.hrtime.bigint();
|
|
93
|
+
const matchedPermissions = [];
|
|
94
|
+
try {
|
|
95
|
+
// Validate inputs
|
|
96
|
+
if (!userPermissions || userPermissions.size === 0) {
|
|
97
|
+
return {
|
|
98
|
+
allowed: false,
|
|
99
|
+
resolverType: this.getType(),
|
|
100
|
+
resolutionTimeUs: 0,
|
|
101
|
+
cached: false,
|
|
102
|
+
reason: 'User has no permissions',
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if (!requiredPermissions || requiredPermissions.length === 0) {
|
|
106
|
+
return {
|
|
107
|
+
allowed: false,
|
|
108
|
+
resolverType: this.getType(),
|
|
109
|
+
resolutionTimeUs: 0,
|
|
110
|
+
cached: false,
|
|
111
|
+
reason: 'No permissions specified',
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
// Find all matching permissions (not just the first one)
|
|
115
|
+
for (const requiredPermission of requiredPermissions) {
|
|
116
|
+
if (!PermissionResolver_1.PermissionUtils.isValidPermission(requiredPermission)) {
|
|
117
|
+
throw new Error(`Invalid permission format: ${requiredPermission}`);
|
|
118
|
+
}
|
|
119
|
+
if (userPermissions.has(requiredPermission)) {
|
|
120
|
+
matchedPermissions.push(requiredPermission);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const allowed = matchedPermissions.length > 0;
|
|
124
|
+
const endTime = process.hrtime.bigint();
|
|
125
|
+
const resolutionTimeUs = Number(endTime - startTime) / 1000;
|
|
126
|
+
return {
|
|
127
|
+
allowed,
|
|
128
|
+
resolverType: this.getType(),
|
|
129
|
+
resolutionTimeUs,
|
|
130
|
+
cached: false, // Plain resolver doesn't use caching
|
|
131
|
+
reason: allowed
|
|
132
|
+
? undefined
|
|
133
|
+
: `Missing required permissions: ${requiredPermissions.join(', ')}`,
|
|
134
|
+
matchedPermissions: allowed ? matchedPermissions : undefined,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
const endTime = process.hrtime.bigint();
|
|
139
|
+
const resolutionTimeUs = Number(endTime - startTime) / 1000;
|
|
140
|
+
return {
|
|
141
|
+
allowed: false,
|
|
142
|
+
resolverType: this.getType(),
|
|
143
|
+
resolutionTimeUs,
|
|
144
|
+
cached: false,
|
|
145
|
+
reason: error instanceof Error ? error.message : 'Unknown error',
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Check if user has ALL required permissions (AND logic)
|
|
151
|
+
*
|
|
152
|
+
* Alternative method for scenarios requiring ALL permissions
|
|
153
|
+
* instead of ANY permission. Less commonly used but available
|
|
154
|
+
* for completeness.
|
|
155
|
+
*
|
|
156
|
+
* @param userPermissions - Set of user's permissions
|
|
157
|
+
* @param requiredPermissions - Array of ALL required permissions
|
|
158
|
+
* @returns Promise resolving to true if user has ALL permissions
|
|
159
|
+
*/
|
|
160
|
+
async checkAllRequired(userPermissions, requiredPermissions) {
|
|
161
|
+
const startTime = process.hrtime.bigint();
|
|
162
|
+
try {
|
|
163
|
+
// Validate inputs
|
|
164
|
+
if (!userPermissions || userPermissions.size === 0) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
if (!requiredPermissions || requiredPermissions.length === 0) {
|
|
168
|
+
return true; // No permissions required means access allowed
|
|
169
|
+
}
|
|
170
|
+
// Check that user has ALL required permissions
|
|
171
|
+
for (const requiredPermission of requiredPermissions) {
|
|
172
|
+
if (!PermissionResolver_1.PermissionUtils.isValidPermission(requiredPermission)) {
|
|
173
|
+
throw new Error(`Invalid permission format: ${requiredPermission}`);
|
|
174
|
+
}
|
|
175
|
+
if (!userPermissions.has(requiredPermission)) {
|
|
176
|
+
return false; // Missing at least one required permission
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
finally {
|
|
182
|
+
// Track performance metrics
|
|
183
|
+
const endTime = process.hrtime.bigint();
|
|
184
|
+
const resolutionTimeUs = Number(endTime - startTime) / 1000;
|
|
185
|
+
this.checkCount++;
|
|
186
|
+
this.totalResolutionTimeUs += resolutionTimeUs;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Get resolver type for identification
|
|
191
|
+
*/
|
|
192
|
+
getType() {
|
|
193
|
+
return PermissionResolver_1.PermissionResolverType.PLAIN;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Get performance characteristics for monitoring
|
|
197
|
+
*/
|
|
198
|
+
getPerformanceCharacteristics() {
|
|
199
|
+
return {
|
|
200
|
+
timeComplexity: 'O(1) per permission check',
|
|
201
|
+
memoryUsage: 'low',
|
|
202
|
+
cacheUtilization: 'none',
|
|
203
|
+
recommendedFor: [
|
|
204
|
+
'High-traffic API endpoints',
|
|
205
|
+
'Sub-millisecond permission checks',
|
|
206
|
+
'Simple permission models',
|
|
207
|
+
'Serverless functions',
|
|
208
|
+
'Performance-critical paths',
|
|
209
|
+
],
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Get performance statistics for monitoring
|
|
214
|
+
*/
|
|
215
|
+
getStats() {
|
|
216
|
+
return {
|
|
217
|
+
checkCount: this.checkCount,
|
|
218
|
+
averageResolutionTimeUs: this.checkCount > 0 ? this.totalResolutionTimeUs / this.checkCount : 0,
|
|
219
|
+
totalResolutionTimeUs: this.totalResolutionTimeUs,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Reset performance statistics
|
|
224
|
+
*/
|
|
225
|
+
resetStats() {
|
|
226
|
+
this.checkCount = 0;
|
|
227
|
+
this.totalResolutionTimeUs = 0;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Get resolver name for debugging
|
|
231
|
+
*/
|
|
232
|
+
getName() {
|
|
233
|
+
return 'PlainPermissionResolver';
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Check if this resolver can handle the given requirement type
|
|
237
|
+
*
|
|
238
|
+
* @param requirement - The requirement to check
|
|
239
|
+
* @returns true if this resolver can handle the requirement
|
|
240
|
+
*/
|
|
241
|
+
canHandle(requirement) {
|
|
242
|
+
return (Array.isArray(requirement) &&
|
|
243
|
+
requirement.length > 0 &&
|
|
244
|
+
requirement.every((item) => typeof item === 'string'));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
exports.PlainPermissionResolver = PlainPermissionResolver;
|
|
248
|
+
//# sourceMappingURL=PlainPermissionResolver.js.map
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wildcard Permission Resolver
|
|
3
|
+
*
|
|
4
|
+
* Configurable permission resolver supporting hierarchical wildcard patterns
|
|
5
|
+
* with two distinct strategies for optimal performance in different scenarios:
|
|
6
|
+
*
|
|
7
|
+
* 1. PRE_EXPANSION Strategy:
|
|
8
|
+
* - Expand wildcards at user context load time
|
|
9
|
+
* - Store expanded permissions in user context
|
|
10
|
+
* - Runtime: O(1) set membership checks (fastest)
|
|
11
|
+
* - Memory: Higher usage due to expanded permission sets
|
|
12
|
+
* - Best for: Production environments with predictable permission sets
|
|
13
|
+
*
|
|
14
|
+
* 2. ON_DEMAND Strategy:
|
|
15
|
+
* - Pattern matching at permission check time
|
|
16
|
+
* - Cache pattern matching results
|
|
17
|
+
* - Runtime: Pattern matching cost with caching benefits
|
|
18
|
+
* - Memory: Lower usage, only caches results
|
|
19
|
+
* - Best for: Development, dynamic permissions, memory-constrained environments
|
|
20
|
+
*
|
|
21
|
+
* Supported Patterns:
|
|
22
|
+
* - 2 levels: "admin.users", "org.reports"
|
|
23
|
+
* - 3 levels: "admin.users.create", "org.reports.view"
|
|
24
|
+
* - Wildcards: "admin.*", "org.reports.*"
|
|
25
|
+
*
|
|
26
|
+
* @author Noony Framework Team
|
|
27
|
+
* @version 1.0.0
|
|
28
|
+
*/
|
|
29
|
+
import { PermissionResolver, PermissionResolverType, PerformanceCharacteristics, PermissionCheckResult } from './PermissionResolver';
|
|
30
|
+
import { CacheAdapter } from '../cache/CacheAdapter';
|
|
31
|
+
import { PermissionResolutionStrategy } from '../config/GuardConfiguration';
|
|
32
|
+
/**
|
|
33
|
+
* Permission registry interface for wildcard expansion
|
|
34
|
+
*/
|
|
35
|
+
export interface PermissionRegistry {
|
|
36
|
+
/**
|
|
37
|
+
* Get all permissions matching a wildcard pattern
|
|
38
|
+
*
|
|
39
|
+
* @param wildcardPattern - Pattern like "admin.*"
|
|
40
|
+
* @returns Array of concrete permissions matching the pattern
|
|
41
|
+
*/
|
|
42
|
+
getMatchingPermissions(wildcardPattern: string): string[];
|
|
43
|
+
/**
|
|
44
|
+
* Get all available permissions in a category
|
|
45
|
+
*
|
|
46
|
+
* @param category - Permission category like "admin"
|
|
47
|
+
* @returns Array of permissions in that category
|
|
48
|
+
*/
|
|
49
|
+
getCategoryPermissions(category: string): string[];
|
|
50
|
+
/**
|
|
51
|
+
* Check if a permission exists in the registry
|
|
52
|
+
*/
|
|
53
|
+
hasPermission(permission: string): boolean;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Wildcard permission resolver with configurable resolution strategies
|
|
57
|
+
*/
|
|
58
|
+
export declare class WildcardPermissionResolver extends PermissionResolver<string[]> {
|
|
59
|
+
private readonly strategy;
|
|
60
|
+
private readonly permissionRegistry;
|
|
61
|
+
private readonly cache;
|
|
62
|
+
private readonly maxPatternDepth;
|
|
63
|
+
private checkCount;
|
|
64
|
+
private totalResolutionTimeUs;
|
|
65
|
+
private cacheHits;
|
|
66
|
+
private cacheMisses;
|
|
67
|
+
constructor(strategy: PermissionResolutionStrategy, permissionRegistry: PermissionRegistry, cache: CacheAdapter, maxPatternDepth?: number);
|
|
68
|
+
/**
|
|
69
|
+
* Check if user permissions satisfy wildcard patterns
|
|
70
|
+
*
|
|
71
|
+
* @param userPermissions - Set of user's permissions (may be pre-expanded)
|
|
72
|
+
* @param wildcardPatterns - Array of wildcard patterns to check
|
|
73
|
+
* @returns Promise resolving to true if user matches any pattern
|
|
74
|
+
*/
|
|
75
|
+
check(userPermissions: Set<string>, wildcardPatterns: string[]): Promise<boolean>;
|
|
76
|
+
/**
|
|
77
|
+
* Pre-expansion strategy: O(1) set membership checks
|
|
78
|
+
*
|
|
79
|
+
* Assumes user permissions have been pre-expanded to include all
|
|
80
|
+
* concrete permissions that match wildcard patterns in their roles.
|
|
81
|
+
* This provides the fastest runtime performance.
|
|
82
|
+
*/
|
|
83
|
+
private checkPreExpanded;
|
|
84
|
+
/**
|
|
85
|
+
* On-demand strategy: Pattern matching with caching
|
|
86
|
+
*
|
|
87
|
+
* Performs pattern matching at runtime but caches results to avoid
|
|
88
|
+
* repeated pattern matching for the same user/pattern combinations.
|
|
89
|
+
*/
|
|
90
|
+
private checkOnDemand;
|
|
91
|
+
/**
|
|
92
|
+
* Check if any user permission matches the given pattern
|
|
93
|
+
*/
|
|
94
|
+
private matchesAnyUserPermission;
|
|
95
|
+
/**
|
|
96
|
+
* Expand wildcard patterns to concrete permissions
|
|
97
|
+
*
|
|
98
|
+
* Used by the user context service when pre-expansion strategy is enabled.
|
|
99
|
+
* Converts wildcard patterns to all matching concrete permissions.
|
|
100
|
+
*
|
|
101
|
+
* @param patterns - Array of wildcard patterns
|
|
102
|
+
* @returns Set of concrete permissions
|
|
103
|
+
*/
|
|
104
|
+
expandWildcardPatterns(patterns: string[]): Promise<Set<string>>;
|
|
105
|
+
/**
|
|
106
|
+
* Check permissions with detailed result information
|
|
107
|
+
*/
|
|
108
|
+
checkWithResult(userPermissions: Set<string>, wildcardPatterns: string[]): Promise<PermissionCheckResult>;
|
|
109
|
+
/**
|
|
110
|
+
* Validate wildcard pattern format
|
|
111
|
+
*/
|
|
112
|
+
private isValidWildcardPattern;
|
|
113
|
+
/**
|
|
114
|
+
* Get resolver type for identification
|
|
115
|
+
*/
|
|
116
|
+
getType(): PermissionResolverType;
|
|
117
|
+
/**
|
|
118
|
+
* Get performance characteristics for monitoring
|
|
119
|
+
*/
|
|
120
|
+
getPerformanceCharacteristics(): PerformanceCharacteristics;
|
|
121
|
+
/**
|
|
122
|
+
* Get performance statistics
|
|
123
|
+
*/
|
|
124
|
+
getStats(): {
|
|
125
|
+
strategy: PermissionResolutionStrategy;
|
|
126
|
+
checkCount: number;
|
|
127
|
+
averageResolutionTimeUs: number;
|
|
128
|
+
totalResolutionTimeUs: number;
|
|
129
|
+
cacheHitRate: number;
|
|
130
|
+
cacheHits: number;
|
|
131
|
+
cacheMisses: number;
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Reset performance statistics
|
|
135
|
+
*/
|
|
136
|
+
resetStats(): void;
|
|
137
|
+
/**
|
|
138
|
+
* Get resolver name for debugging
|
|
139
|
+
*/
|
|
140
|
+
getName(): string;
|
|
141
|
+
/**
|
|
142
|
+
* Check if this resolver can handle the given requirement type
|
|
143
|
+
*/
|
|
144
|
+
canHandle(requirement: any): requirement is string[];
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=WildcardPermissionResolver.d.ts.map
|