astro-sessionkit 0.1.17 → 0.1.18
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guardMiddleware.d.ts","sourceRoot":"","sources":["../../src/core/guardMiddleware.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAa,iBAAiB,EAAC,MAAM,OAAO,CAAC;AAsEzD,wBAAgB,qBAAqB,IAAI,iBAAiB,
|
|
1
|
+
{"version":3,"file":"guardMiddleware.d.ts","sourceRoot":"","sources":["../../src/core/guardMiddleware.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAa,iBAAiB,EAAC,MAAM,OAAO,CAAC;AAsEzD,wBAAgB,qBAAqB,IAAI,iBAAiB,CA8DzD"}
|
|
@@ -61,12 +61,15 @@ function createGuardMiddleware() {
|
|
|
61
61
|
const sessionContext = getContextStore();
|
|
62
62
|
const session = sessionContext?.session ?? null;
|
|
63
63
|
const rule = protect.find((r) => matchesPattern(r.pattern, pathname));
|
|
64
|
+
debug(`[Guard] Pathname: ${pathname}, Found rule: ${rule ? JSON.stringify(rule) : 'none'}, GlobalProtect: ${globalProtect}`);
|
|
64
65
|
if (!rule) {
|
|
65
66
|
if (globalProtect) {
|
|
66
67
|
if (exclude.some((pattern) => matchesPattern(pattern, pathname))) {
|
|
68
|
+
debug(`[GlobalProtect] Skipping ${pathname} because it matches an exclude pattern`);
|
|
67
69
|
return next();
|
|
68
70
|
}
|
|
69
71
|
if (pathname === loginPath) {
|
|
72
|
+
debug(`[GlobalProtect] Skipping ${pathname} because it is the loginPath`);
|
|
70
73
|
return next();
|
|
71
74
|
}
|
|
72
75
|
if (!session || !isValidSessionStructure(session)) {
|
|
@@ -74,6 +77,7 @@ function createGuardMiddleware() {
|
|
|
74
77
|
return context.redirect(loginPath);
|
|
75
78
|
}
|
|
76
79
|
}
|
|
80
|
+
debug(`[GlobalProtect] Allowing ${pathname} because session is valid or globalProtect is false`);
|
|
77
81
|
return next();
|
|
78
82
|
}
|
|
79
83
|
const allowed = await checkRule(rule, session);
|
|
@@ -82,6 +86,7 @@ function createGuardMiddleware() {
|
|
|
82
86
|
debug(`[Guard] Redirecting to ${redirectTo} because access was denied by rule:`, rule);
|
|
83
87
|
return context.redirect(redirectTo);
|
|
84
88
|
}
|
|
89
|
+
debug(`[Guard] Allowing ${pathname} because access was granted by rule:`, rule);
|
|
85
90
|
return next();
|
|
86
91
|
};
|
|
87
92
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guardMiddleware.js","sources":["../../src/core/guardMiddleware.ts"],"sourcesContent":["// ============================================================================\n// Route Guard Middleware - Enforces protection rules\n// ============================================================================\n\nimport type {APIContext, MiddlewareHandler} from \"astro\";\nimport { getContextStore } from \"./context\";\nimport { getConfig } from \"./config\";\nimport { matchesPattern } from \"./matcher\";\nimport type { ProtectionRule, Session } from \"./types\";\nimport { isValidSessionStructure } from \"./validation\";\nimport * as logger from \"./logger\";\n\n/**\n * Check if session satisfies a protection rule\n */\nasync function checkRule(rule: ProtectionRule, session: Session | null): Promise<boolean> {\n const { access } = getConfig();\n\n // Custom check overrides everything\n if (access.check) {\n try {\n return await access.check(rule, session);\n } catch (error) {\n logger.error('Error in custom access check hook:', error);\n return false;\n }\n }\n\n // Custom allow function\n if (\"allow\" in rule) {\n try {\n return await rule.allow(session);\n } catch (error) {\n logger.error('Error in custom rule allow function:', error);\n return false;\n }\n }\n\n // Must be authenticated and have a valid session structure for all other checks\n if (!session || !isValidSessionStructure(session)) {\n return false;\n }\n\n // Single role check\n if (\"role\" in rule) {\n const userRole = access.getRole(session);\n return userRole === rule.role;\n }\n\n // Multiple roles check (user must have ONE of these)\n if (\"roles\" in rule) {\n const userRole = access.getRole(session);\n return userRole !== null && rule.roles.includes(userRole);\n }\n\n // Single permission check\n if (\"permission\" in rule) {\n const userPermissions = access.getPermissions(session);\n return userPermissions.includes(rule.permission);\n }\n\n // Multiple permissions check (user must have ALL of these)\n if (\"permissions\" in rule) {\n const userPermissions = access.getPermissions(session);\n return rule.permissions.every((p) => userPermissions.includes(p));\n }\n\n // No specific rule matched - allow by default\n return true;\n}\n\n/**\n * Create route guard middleware\n */\nexport function createGuardMiddleware(): MiddlewareHandler {\n return async (context : APIContext, next) => {\n const { protect, loginPath, globalProtect, exclude } = getConfig();\n \n // No rules configured and no global protect - skip\n if (protect.length === 0 && !globalProtect) {\n return next();\n }\n\n let pathname: string;\n try {\n pathname = new URL(context.request.url).pathname;\n } catch {\n // Fallback if URL is invalid (unlikely in Astro)\n pathname = \"/\";\n }\n const sessionContext = getContextStore();\n const session = sessionContext?.session ?? null;\n\n // Find matching rule\n const rule = protect.find((r) => matchesPattern(r.pattern, pathname));\n \n // No matching rule - check global protection\n if (!rule) {\n if (globalProtect) {\n // Skip if path is in exclude list\n if (exclude.some((pattern) => matchesPattern(pattern, pathname))) {\n return next();\n }\n \n // Skip if it's the login page itself (to avoid redirect loops)\n if (pathname === loginPath) {\n return next();\n }\n\n
|
|
1
|
+
{"version":3,"file":"guardMiddleware.js","sources":["../../src/core/guardMiddleware.ts"],"sourcesContent":["// ============================================================================\n// Route Guard Middleware - Enforces protection rules\n// ============================================================================\n\nimport type {APIContext, MiddlewareHandler} from \"astro\";\nimport { getContextStore } from \"./context\";\nimport { getConfig } from \"./config\";\nimport { matchesPattern } from \"./matcher\";\nimport type { ProtectionRule, Session } from \"./types\";\nimport { isValidSessionStructure } from \"./validation\";\nimport * as logger from \"./logger\";\n\n/**\n * Check if session satisfies a protection rule\n */\nasync function checkRule(rule: ProtectionRule, session: Session | null): Promise<boolean> {\n const { access } = getConfig();\n\n // Custom check overrides everything\n if (access.check) {\n try {\n return await access.check(rule, session);\n } catch (error) {\n logger.error('Error in custom access check hook:', error);\n return false;\n }\n }\n\n // Custom allow function\n if (\"allow\" in rule) {\n try {\n return await rule.allow(session);\n } catch (error) {\n logger.error('Error in custom rule allow function:', error);\n return false;\n }\n }\n\n // Must be authenticated and have a valid session structure for all other checks\n if (!session || !isValidSessionStructure(session)) {\n return false;\n }\n\n // Single role check\n if (\"role\" in rule) {\n const userRole = access.getRole(session);\n return userRole === rule.role;\n }\n\n // Multiple roles check (user must have ONE of these)\n if (\"roles\" in rule) {\n const userRole = access.getRole(session);\n return userRole !== null && rule.roles.includes(userRole);\n }\n\n // Single permission check\n if (\"permission\" in rule) {\n const userPermissions = access.getPermissions(session);\n return userPermissions.includes(rule.permission);\n }\n\n // Multiple permissions check (user must have ALL of these)\n if (\"permissions\" in rule) {\n const userPermissions = access.getPermissions(session);\n return rule.permissions.every((p) => userPermissions.includes(p));\n }\n\n // No specific rule matched - allow by default\n return true;\n}\n\n/**\n * Create route guard middleware\n */\nexport function createGuardMiddleware(): MiddlewareHandler {\n return async (context : APIContext, next) => {\n const { protect, loginPath, globalProtect, exclude } = getConfig();\n \n // No rules configured and no global protect - skip\n if (protect.length === 0 && !globalProtect) {\n return next();\n }\n\n let pathname: string;\n try {\n pathname = new URL(context.request.url).pathname;\n } catch {\n // Fallback if URL is invalid (unlikely in Astro)\n pathname = \"/\";\n }\n const sessionContext = getContextStore();\n const session = sessionContext?.session ?? null;\n\n // Find matching rule\n const rule = protect.find((r) => matchesPattern(r.pattern, pathname));\n \n logger.debug(`[Guard] Pathname: ${pathname}, Found rule: ${rule ? JSON.stringify(rule) : 'none'}, GlobalProtect: ${globalProtect}`);\n\n // No matching rule - check global protection\n if (!rule) {\n if (globalProtect) {\n // Skip if path is in exclude list\n if (exclude.some((pattern) => matchesPattern(pattern, pathname))) {\n logger.debug(`[GlobalProtect] Skipping ${pathname} because it matches an exclude pattern`);\n return next();\n }\n \n // Skip if it's the login page itself (to avoid redirect loops)\n if (pathname === loginPath) {\n logger.debug(`[GlobalProtect] Skipping ${pathname} because it is the loginPath`);\n return next();\n }\n\n // Require valid session\n if (!session || !isValidSessionStructure(session)) {\n logger.debug(`[GlobalProtect] Redirecting to ${loginPath} because session is ${session ? 'invalid' : 'missing'}`);\n return context.redirect(loginPath);\n }\n }\n \n logger.debug(`[GlobalProtect] Allowing ${pathname} because session is valid or globalProtect is false`);\n return next();\n }\n\n // Check if access is allowed\n const allowed = await checkRule(rule, session);\n\n if (!allowed) {\n const redirectTo = rule.redirectTo ?? loginPath;\n logger.debug(`[Guard] Redirecting to ${redirectTo} because access was denied by rule:`, rule);\n return context.redirect(redirectTo);\n }\n\n logger.debug(`[Guard] Allowing ${pathname} because access was granted by rule:`, rule);\n return next();\n };\n}\n"],"names":["error","logger.error","logger.debug"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -94,37 +94,44 @@ export function createGuardMiddleware(): MiddlewareHandler {
|
|
|
94
94
|
// Find matching rule
|
|
95
95
|
const rule = protect.find((r) => matchesPattern(r.pattern, pathname));
|
|
96
96
|
|
|
97
|
+
logger.debug(`[Guard] Pathname: ${pathname}, Found rule: ${rule ? JSON.stringify(rule) : 'none'}, GlobalProtect: ${globalProtect}`);
|
|
98
|
+
|
|
97
99
|
// No matching rule - check global protection
|
|
98
100
|
if (!rule) {
|
|
99
101
|
if (globalProtect) {
|
|
100
102
|
// Skip if path is in exclude list
|
|
101
103
|
if (exclude.some((pattern) => matchesPattern(pattern, pathname))) {
|
|
104
|
+
logger.debug(`[GlobalProtect] Skipping ${pathname} because it matches an exclude pattern`);
|
|
102
105
|
return next();
|
|
103
106
|
}
|
|
104
107
|
|
|
105
108
|
// Skip if it's the login page itself (to avoid redirect loops)
|
|
106
109
|
if (pathname === loginPath) {
|
|
110
|
+
logger.debug(`[GlobalProtect] Skipping ${pathname} because it is the loginPath`);
|
|
107
111
|
return next();
|
|
108
112
|
}
|
|
109
113
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
+
// Require valid session
|
|
115
|
+
if (!session || !isValidSessionStructure(session)) {
|
|
116
|
+
logger.debug(`[GlobalProtect] Redirecting to ${loginPath} because session is ${session ? 'invalid' : 'missing'}`);
|
|
117
|
+
return context.redirect(loginPath);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
logger.debug(`[GlobalProtect] Allowing ${pathname} because session is valid or globalProtect is false`);
|
|
122
|
+
return next();
|
|
114
123
|
}
|
|
115
|
-
}
|
|
116
|
-
return next();
|
|
117
|
-
}
|
|
118
124
|
|
|
119
|
-
// Check if access is allowed
|
|
120
|
-
const allowed = await checkRule(rule, session);
|
|
125
|
+
// Check if access is allowed
|
|
126
|
+
const allowed = await checkRule(rule, session);
|
|
121
127
|
|
|
122
|
-
if (!allowed) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
128
|
+
if (!allowed) {
|
|
129
|
+
const redirectTo = rule.redirectTo ?? loginPath;
|
|
130
|
+
logger.debug(`[Guard] Redirecting to ${redirectTo} because access was denied by rule:`, rule);
|
|
131
|
+
return context.redirect(redirectTo);
|
|
132
|
+
}
|
|
127
133
|
|
|
134
|
+
logger.debug(`[Guard] Allowing ${pathname} because access was granted by rule:`, rule);
|
|
128
135
|
return next();
|
|
129
136
|
};
|
|
130
137
|
}
|