astro-sessionkit 0.1.16 → 0.1.17
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/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +4 -9
- package/dist/core/config.js.map +1 -1
- package/dist/core/guardMiddleware.d.ts.map +1 -1
- package/dist/core/guardMiddleware.js +7 -8
- package/dist/core/guardMiddleware.js.map +1 -1
- package/dist/core/logger.d.ts +1 -0
- package/dist/core/logger.d.ts.map +1 -1
- package/dist/core/logger.js +13 -1
- package/dist/core/logger.js.map +1 -1
- package/dist/core/sessionMiddleware.js +3 -3
- package/dist/core/sessionMiddleware.js.map +1 -1
- package/package.json +1 -1
- package/src/core/config.ts +4 -9
- package/src/core/guardMiddleware.ts +17 -18
- package/src/core/logger.ts +10 -0
- package/src/core/sessionMiddleware.ts +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,gBAAgB,EAAE,WAAW,EAAE,cAAc,EAAW,cAAc,EAAC,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,gBAAgB,EAAE,WAAW,EAAE,cAAc,EAAW,cAAc,EAAC,MAAM,SAAS,CAAC;AAOpG,MAAM,WAAW,cAAc;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,GAAG;QAC3C,KAAK,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;KAChC,CAAC;IACF,cAAc,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC7E,eAAe,CAAC,EAAE,MAAM,cAAc,GAAG,SAAS,CAAC;IACnD,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IACpD,aAAa,EAAE,OAAO,CAAC;IACvB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;CAClB;AAoBD,wBAAgB,SAAS,CAAC,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAiG5D;AAKD,wBAAgB,SAAS,IAAI,cAAc,CAE1C"}
|
package/dist/core/config.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isValidRedirectPath, isValidPattern } from './validation.js';
|
|
2
|
+
import { warn } from './logger.js';
|
|
2
3
|
|
|
3
4
|
const DEFAULT_CONFIG = {
|
|
4
5
|
loginPath: "/login",
|
|
@@ -46,21 +47,15 @@ function setConfig(userConfig) {
|
|
|
46
47
|
const anyAccess = userConfig.access;
|
|
47
48
|
if (anyAccess.globalProtect !== undefined && userConfig.globalProtect === undefined) {
|
|
48
49
|
newConfig.globalProtect = anyAccess.globalProtect;
|
|
49
|
-
|
|
50
|
-
console.warn('[SessionKit] Deprecation: globalProtect should be at the top level of configuration, not inside "access".');
|
|
51
|
-
}
|
|
50
|
+
warn('Deprecation: globalProtect should be at the top level of configuration, not inside "access".');
|
|
52
51
|
}
|
|
53
52
|
if (anyAccess.exclude !== undefined && userConfig.exclude === undefined) {
|
|
54
53
|
newConfig.exclude = anyAccess.exclude;
|
|
55
|
-
|
|
56
|
-
console.warn('[SessionKit] Deprecation: exclude should be at the top level of configuration, not inside "access".');
|
|
57
|
-
}
|
|
54
|
+
warn('Deprecation: exclude should be at the top level of configuration, not inside "access".');
|
|
58
55
|
}
|
|
59
56
|
if (anyAccess.debug !== undefined && userConfig.debug === undefined) {
|
|
60
57
|
newConfig.debug = anyAccess.debug;
|
|
61
|
-
|
|
62
|
-
console.warn('[SessionKit] Deprecation: debug should be at the top level of configuration, not inside "access".');
|
|
63
|
-
}
|
|
58
|
+
warn('Deprecation: debug should be at the top level of configuration, not inside "access".');
|
|
64
59
|
}
|
|
65
60
|
}
|
|
66
61
|
newConfig.runWithContext = userConfig.runWithContext;
|
package/dist/core/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sources":["../../src/core/config.ts"],"sourcesContent":["// ============================================================================\n// Configuration Store\n// ============================================================================\n\nimport type {SessionKitConfig, AccessHooks, ProtectionRule, Session, SessionContext} from \"./types\";\nimport {isValidPattern, isValidRedirectPath} from \"./validation\";\n\n/**\n * Internal config with defaults applied\n */\nexport interface ResolvedConfig {\n loginPath: string;\n protect: ProtectionRule[];\n access: Required<Omit<AccessHooks, \"check\">> & {\n check?: AccessHooks[\"check\"];\n };\n runWithContext?: <T>(context: SessionContext, fn: () => T) => T | Promise<T>;\n getContextStore?: () => SessionContext | undefined;\n setContextStore?: (context: SessionContext) => void;\n globalProtect: boolean;\n exclude: string[];\n debug: boolean;\n}\n\nconst DEFAULT_CONFIG: ResolvedConfig = {\n loginPath: \"/login\",\n protect: [],\n access: {\n getRole: (session: Session | null) => session?.role ?? null,\n getPermissions: (session: Session | null) => session?.permissions ?? [],\n check: undefined,\n },\n globalProtect: false,\n exclude: [],\n debug: false,\n};\n\nlet config: ResolvedConfig = { ...DEFAULT_CONFIG };\n\n/**\n * Set configuration (called by integration)\n */\nexport function setConfig(userConfig: SessionKitConfig): void {\n // Start with default config\n const newConfig: ResolvedConfig = { ...DEFAULT_CONFIG };\n\n // Validate and set loginPath\n if (userConfig.loginPath !== undefined) {\n if (!isValidRedirectPath(userConfig.loginPath)) {\n throw new Error(\n `[SessionKit] Invalid loginPath: \"${userConfig.loginPath}\". Must start with / and be less than 500 characters.`\n );\n }\n newConfig.loginPath = userConfig.loginPath;\n }\n\n // Validate protection rules\n if (userConfig.protect) {\n for (const rule of userConfig.protect) {\n // Validate pattern\n if (!isValidPattern(rule.pattern)) {\n throw new Error(\n `[SessionKit] Invalid pattern: \"${rule.pattern}\". ` +\n `Patterns must start with / and be less than 1000 characters.`\n );\n }\n\n // Validate redirectTo if present\n if (rule.redirectTo && !isValidRedirectPath(rule.redirectTo)) {\n throw new Error(\n `[SessionKit] Invalid redirectTo: \"${rule.redirectTo}\". ` +\n `Must start with / and be less than 500 characters.`\n );\n }\n }\n newConfig.protect = [...userConfig.protect];\n }\n\n // Validate context store getter/setter pair\n if ((userConfig.getContextStore && !userConfig.setContextStore) || (!userConfig.getContextStore && userConfig.setContextStore)) {\n throw new Error(\n '[SessionKit] Both getContextStore and setContextStore must be provided together if using custom context storage.'\n );\n }\n\n // Set access hooks\n if (userConfig.access) {\n newConfig.access = {\n getRole: userConfig.access.getRole ?? DEFAULT_CONFIG.access.getRole,\n getPermissions: userConfig.access.getPermissions ?? DEFAULT_CONFIG.access.getPermissions,\n check: userConfig.access.check,\n };\n\n // Migration/Safety: If user misplaced globalProtect/exclude/debug in access object,\n // we honor them but warn about it.\n const anyAccess = userConfig.access as any;\n if (anyAccess.globalProtect !== undefined && userConfig.globalProtect === undefined) {\n newConfig.globalProtect = anyAccess.globalProtect;\n
|
|
1
|
+
{"version":3,"file":"config.js","sources":["../../src/core/config.ts"],"sourcesContent":["// ============================================================================\n// Configuration Store\n// ============================================================================\n\nimport type {SessionKitConfig, AccessHooks, ProtectionRule, Session, SessionContext} from \"./types\";\nimport {isValidPattern, isValidRedirectPath} from \"./validation\";\nimport * as logger from \"./logger\";\n\n/**\n * Internal config with defaults applied\n */\nexport interface ResolvedConfig {\n loginPath: string;\n protect: ProtectionRule[];\n access: Required<Omit<AccessHooks, \"check\">> & {\n check?: AccessHooks[\"check\"];\n };\n runWithContext?: <T>(context: SessionContext, fn: () => T) => T | Promise<T>;\n getContextStore?: () => SessionContext | undefined;\n setContextStore?: (context: SessionContext) => void;\n globalProtect: boolean;\n exclude: string[];\n debug: boolean;\n}\n\nconst DEFAULT_CONFIG: ResolvedConfig = {\n loginPath: \"/login\",\n protect: [],\n access: {\n getRole: (session: Session | null) => session?.role ?? null,\n getPermissions: (session: Session | null) => session?.permissions ?? [],\n check: undefined,\n },\n globalProtect: false,\n exclude: [],\n debug: false,\n};\n\nlet config: ResolvedConfig = { ...DEFAULT_CONFIG };\n\n/**\n * Set configuration (called by integration)\n */\nexport function setConfig(userConfig: SessionKitConfig): void {\n // Start with default config\n const newConfig: ResolvedConfig = { ...DEFAULT_CONFIG };\n\n // Validate and set loginPath\n if (userConfig.loginPath !== undefined) {\n if (!isValidRedirectPath(userConfig.loginPath)) {\n throw new Error(\n `[SessionKit] Invalid loginPath: \"${userConfig.loginPath}\". Must start with / and be less than 500 characters.`\n );\n }\n newConfig.loginPath = userConfig.loginPath;\n }\n\n // Validate protection rules\n if (userConfig.protect) {\n for (const rule of userConfig.protect) {\n // Validate pattern\n if (!isValidPattern(rule.pattern)) {\n throw new Error(\n `[SessionKit] Invalid pattern: \"${rule.pattern}\". ` +\n `Patterns must start with / and be less than 1000 characters.`\n );\n }\n\n // Validate redirectTo if present\n if (rule.redirectTo && !isValidRedirectPath(rule.redirectTo)) {\n throw new Error(\n `[SessionKit] Invalid redirectTo: \"${rule.redirectTo}\". ` +\n `Must start with / and be less than 500 characters.`\n );\n }\n }\n newConfig.protect = [...userConfig.protect];\n }\n\n // Validate context store getter/setter pair\n if ((userConfig.getContextStore && !userConfig.setContextStore) || (!userConfig.getContextStore && userConfig.setContextStore)) {\n throw new Error(\n '[SessionKit] Both getContextStore and setContextStore must be provided together if using custom context storage.'\n );\n }\n\n // Set access hooks\n if (userConfig.access) {\n newConfig.access = {\n getRole: userConfig.access.getRole ?? DEFAULT_CONFIG.access.getRole,\n getPermissions: userConfig.access.getPermissions ?? DEFAULT_CONFIG.access.getPermissions,\n check: userConfig.access.check,\n };\n\n // Migration/Safety: If user misplaced globalProtect/exclude/debug in access object,\n // we honor them but warn about it.\n const anyAccess = userConfig.access as any;\n if (anyAccess.globalProtect !== undefined && userConfig.globalProtect === undefined) {\n newConfig.globalProtect = anyAccess.globalProtect;\n logger.warn('Deprecation: globalProtect should be at the top level of configuration, not inside \"access\".');\n }\n if (anyAccess.exclude !== undefined && userConfig.exclude === undefined) {\n newConfig.exclude = anyAccess.exclude;\n logger.warn('Deprecation: exclude should be at the top level of configuration, not inside \"access\".');\n }\n if (anyAccess.debug !== undefined && userConfig.debug === undefined) {\n newConfig.debug = anyAccess.debug;\n logger.warn('Deprecation: debug should be at the top level of configuration, not inside \"access\".');\n }\n }\n\n // Set context hooks\n newConfig.runWithContext = userConfig.runWithContext;\n newConfig.getContextStore = userConfig.getContextStore;\n newConfig.setContextStore = userConfig.setContextStore;\n \n if (userConfig.globalProtect !== undefined) {\n newConfig.globalProtect = userConfig.globalProtect;\n }\n\n if (userConfig.exclude !== undefined) {\n for (const pattern of userConfig.exclude) {\n if (!isValidPattern(pattern)) {\n throw new Error(\n `[SessionKit] Invalid exclude pattern: \"${pattern}\". ` +\n `Patterns must start with / and be less than 1000 characters.`\n );\n }\n }\n newConfig.exclude = [...userConfig.exclude];\n } else if (newConfig.exclude.length === 0) {\n newConfig.exclude = [...DEFAULT_CONFIG.exclude];\n }\n\n if (userConfig.debug !== undefined) {\n newConfig.debug = userConfig.debug;\n }\n\n // Atomic update\n config = Object.freeze(newConfig);\n}\n\n/**\n * Get current configuration\n */\nexport function getConfig(): ResolvedConfig {\n return config;\n}\n"],"names":["logger.warn"],"mappings":"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"}
|
|
@@ -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;
|
|
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,CAuDzD"}
|
|
@@ -2,6 +2,7 @@ import { getContextStore } from './context.js';
|
|
|
2
2
|
import { getConfig } from './config.js';
|
|
3
3
|
import { matchesPattern } from './matcher.js';
|
|
4
4
|
import { isValidSessionStructure } from './validation.js';
|
|
5
|
+
import { debug, error } from './logger.js';
|
|
5
6
|
|
|
6
7
|
async function checkRule(rule, session) {
|
|
7
8
|
const { access } = getConfig();
|
|
@@ -9,10 +10,8 @@ async function checkRule(rule, session) {
|
|
|
9
10
|
try {
|
|
10
11
|
return await access.check(rule, session);
|
|
11
12
|
}
|
|
12
|
-
catch (error) {
|
|
13
|
-
|
|
14
|
-
console.error('[SessionKit] Error in custom access check hook:', error);
|
|
15
|
-
}
|
|
13
|
+
catch (error$1) {
|
|
14
|
+
error('Error in custom access check hook:', error$1);
|
|
16
15
|
return false;
|
|
17
16
|
}
|
|
18
17
|
}
|
|
@@ -20,10 +19,8 @@ async function checkRule(rule, session) {
|
|
|
20
19
|
try {
|
|
21
20
|
return await rule.allow(session);
|
|
22
21
|
}
|
|
23
|
-
catch (error) {
|
|
24
|
-
|
|
25
|
-
console.error('[SessionKit] Error in custom rule allow function:', error);
|
|
26
|
-
}
|
|
22
|
+
catch (error$1) {
|
|
23
|
+
error('Error in custom rule allow function:', error$1);
|
|
27
24
|
return false;
|
|
28
25
|
}
|
|
29
26
|
}
|
|
@@ -73,6 +70,7 @@ function createGuardMiddleware() {
|
|
|
73
70
|
return next();
|
|
74
71
|
}
|
|
75
72
|
if (!session || !isValidSessionStructure(session)) {
|
|
73
|
+
debug(`[GlobalProtect] Redirecting to ${loginPath} because session is ${session ? 'invalid' : 'missing'}`);
|
|
76
74
|
return context.redirect(loginPath);
|
|
77
75
|
}
|
|
78
76
|
}
|
|
@@ -81,6 +79,7 @@ function createGuardMiddleware() {
|
|
|
81
79
|
const allowed = await checkRule(rule, session);
|
|
82
80
|
if (!allowed) {
|
|
83
81
|
const redirectTo = rule.redirectTo ?? loginPath;
|
|
82
|
+
debug(`[Guard] Redirecting to ${redirectTo} because access was denied by rule:`, rule);
|
|
84
83
|
return context.redirect(redirectTo);
|
|
85
84
|
}
|
|
86
85
|
return next();
|
|
@@ -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\";\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
|
|
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 // 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 return next();\n}\n\n// Check if access is allowed\nconst allowed = await checkRule(rule, session);\n\nif (!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 return next();\n };\n}\n"],"names":["error","logger.error","logger.debug"],"mappings":"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"}
|
package/dist/core/logger.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export declare function debug(message: string, ...args: any[]): void;
|
|
2
2
|
export declare function error(message: string, ...args: any[]): void;
|
|
3
3
|
export declare function warn(message: string, ...args: any[]): void;
|
|
4
|
+
export declare function info(message: string, ...args: any[]): void;
|
|
4
5
|
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"AAKA,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAK3D;AAKD,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAK3D;AAKD,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAK1D"}
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"AAKA,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAK3D;AAKD,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAK3D;AAKD,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAK1D;AAKD,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAK1D"}
|
package/dist/core/logger.js
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { getConfig } from './config.js';
|
|
2
2
|
|
|
3
|
+
function debug(message, ...args) {
|
|
4
|
+
const { debug } = getConfig();
|
|
5
|
+
if (debug) {
|
|
6
|
+
console.debug(`[SessionKit] ${message}`, ...args);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
function error(message, ...args) {
|
|
10
|
+
const { debug } = getConfig();
|
|
11
|
+
if (debug || process.env.NODE_ENV !== 'production') {
|
|
12
|
+
console.error(`[SessionKit] ${message}`, ...args);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
3
15
|
function warn(message, ...args) {
|
|
4
16
|
const { debug } = getConfig();
|
|
5
17
|
if (debug || process.env.NODE_ENV !== 'production') {
|
|
@@ -7,5 +19,5 @@ function warn(message, ...args) {
|
|
|
7
19
|
}
|
|
8
20
|
}
|
|
9
21
|
|
|
10
|
-
export { warn };
|
|
22
|
+
export { debug, error, warn };
|
|
11
23
|
//# sourceMappingURL=logger.js.map
|
package/dist/core/logger.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","sources":["../../src/core/logger.ts"],"sourcesContent":["import { getConfig } from \"./config\";\n\n/**\n * Log message if debug mode is enabled\n */\nexport function debug(message: string, ...args: any[]): void {\n const { debug } = getConfig();\n if (debug) {\n console.debug(`[SessionKit] ${message}`, ...args);\n }\n}\n\n/**\n * Log error message. Always logs unless in production, but can be forced via debug flag.\n */\nexport function error(message: string, ...args: any[]): void {\n const { debug } = getConfig();\n if (debug || process.env.NODE_ENV !== 'production') {\n console.error(`[SessionKit] ${message}`, ...args);\n }\n}\n\n/**\n * Log warning message. Always logs unless in production, but can be forced via debug flag.\n */\nexport function warn(message: string, ...args: any[]): void {\n const { debug } = getConfig();\n if (debug || process.env.NODE_ENV !== 'production') {\n console.warn(`[SessionKit] ${message}`, ...args);\n }\n}\n"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"logger.js","sources":["../../src/core/logger.ts"],"sourcesContent":["import { getConfig } from \"./config\";\n\n/**\n * Log message if debug mode is enabled\n */\nexport function debug(message: string, ...args: any[]): void {\n const { debug } = getConfig();\n if (debug) {\n console.debug(`[SessionKit] ${message}`, ...args);\n }\n}\n\n/**\n * Log error message. Always logs unless in production, but can be forced via debug flag.\n */\nexport function error(message: string, ...args: any[]): void {\n const { debug } = getConfig();\n if (debug || process.env.NODE_ENV !== 'production') {\n console.error(`[SessionKit] ${message}`, ...args);\n }\n}\n\n/**\n * Log warning message. Always logs unless in production, but can be forced via debug flag.\n */\nexport function warn(message: string, ...args: any[]): void {\n const { debug } = getConfig();\n if (debug || process.env.NODE_ENV !== 'production') {\n console.warn(`[SessionKit] ${message}`, ...args);\n }\n}\n\n/**\n * Log info message. Always logs unless in production, but can be forced via debug flag.\n */\nexport function info(message: string, ...args: any[]): void {\n const { debug } = getConfig();\n if (debug || process.env.NODE_ENV !== 'production') {\n console.log(`[SessionKit] ${message}`, ...args);\n }\n}\n"],"names":[],"mappings":";;SAKgB,KAAK,CAAC,OAAe,EAAE,GAAG,IAAW,EAAA;AACnD,IAAA,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE;IAC7B,IAAI,KAAK,EAAE;QACT,OAAO,CAAC,KAAK,CAAC,CAAA,aAAA,EAAgB,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC;IACnD;AACF;SAKgB,KAAK,CAAC,OAAe,EAAE,GAAG,IAAW,EAAA;AACnD,IAAA,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE;IAC7B,IAAI,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE;QAClD,OAAO,CAAC,KAAK,CAAC,CAAA,aAAA,EAAgB,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC;IACnD;AACF;SAKgB,IAAI,CAAC,OAAe,EAAE,GAAG,IAAW,EAAA;AAClD,IAAA,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE;IAC7B,IAAI,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE;QAClD,OAAO,CAAC,IAAI,CAAC,CAAA,aAAA,EAAgB,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC;IAClD;AACF;;;;"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { runWithContext } from './context.js';
|
|
2
2
|
import { isValidSessionStructure } from './validation.js';
|
|
3
3
|
import { getConfig } from './config.js';
|
|
4
|
-
import { warn } from './logger.js';
|
|
4
|
+
import { warn, error } from './logger.js';
|
|
5
5
|
|
|
6
6
|
const SESSION_KEY = "__session__";
|
|
7
7
|
const sessionMiddleware = async (context, next) => {
|
|
@@ -27,8 +27,8 @@ const sessionMiddleware = async (context, next) => {
|
|
|
27
27
|
else if (config.setContextStore) {
|
|
28
28
|
config.setContextStore({ session });
|
|
29
29
|
}
|
|
30
|
-
else
|
|
31
|
-
|
|
30
|
+
else {
|
|
31
|
+
error('getContextStore returned undefined, cannot set session');
|
|
32
32
|
}
|
|
33
33
|
return next();
|
|
34
34
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionMiddleware.js","sources":["../../src/core/sessionMiddleware.ts"],"sourcesContent":["// ============================================================================\n// Session Middleware - Loads session into AsyncLocalStorage\n// ============================================================================\n\nimport type {MiddlewareHandler} from \"astro\";\nimport {runWithContext as defaultRunWithContext} from \"./context\";\nimport {isValidSessionStructure} from \"./validation\";\nimport type {Session} from \"./types\";\nimport {getConfig} from \"./config\";\nimport
|
|
1
|
+
{"version":3,"file":"sessionMiddleware.js","sources":["../../src/core/sessionMiddleware.ts"],"sourcesContent":["// ============================================================================\n// Session Middleware - Loads session into AsyncLocalStorage\n// ============================================================================\n\nimport type {MiddlewareHandler} from \"astro\";\nimport {runWithContext as defaultRunWithContext} from \"./context\";\nimport {isValidSessionStructure} from \"./validation\";\nimport type {Session} from \"./types\";\nimport {getConfig} from \"./config\";\nimport * as logger from \"./logger\";\n\n/**\n * Session key used to store session in context.session\n */\nconst SESSION_KEY = \"__session__\";\n\n/**\n * Main session middleware\n *\n * Reads session from context.session.get('__session__') and makes it available\n * throughout the request via AsyncLocalStorage\n */\nexport const sessionMiddleware: MiddlewareHandler = async (context, next) => {\n // Get session from context.session store\n const rawSession = context.session?.get<Session>(SESSION_KEY) ?? null;\n\n // Validate session structure if present\n let session: Session | null = null;\n\n if (rawSession) {\n if (isValidSessionStructure(rawSession)) {\n session = rawSession;\n } else {\n // Invalid session structure - log warning and treat as unauthenticated\n logger.warn(\n 'Invalid session structure detected. Session will be ignored. ' +\n 'Ensure context.session.set(\"__session__\", ...) has the correct structure. ' +\n 'Received: ' + JSON.stringify(rawSession)\n );\n session = null;\n }\n }\n\n // Run the rest of the request chain with session context\n const config = getConfig();\n\n // If getContextStore is provided, but runWithContext is NOT,\n // we assume the user is managing the context at a superior level\n // and we should NOT wrap the call in our default runner.\n if (config.getContextStore && !config.runWithContext) {\n // Initialize context store if setter is provided\n const store = config.getContextStore();\n if (store) {\n store.session = session;\n } else if (config.setContextStore) {\n config.setContextStore({session});\n } else {\n logger.error('getContextStore returned undefined, cannot set session');\n }\n return next();\n }\n\n const runner = config.runWithContext ?? defaultRunWithContext;\n return runner({session}, () => next());\n};"],"names":["logger.warn","logger.error","defaultRunWithContext"],"mappings":";;;;;AAcA,MAAM,WAAW,GAAG,aAAa;AAQ1B,MAAM,iBAAiB,GAAsB,OAAO,OAAO,EAAE,IAAI,KAAI;AAExE,IAAA,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,EAAE,GAAG,CAAU,WAAW,CAAC,IAAI,IAAI;IAGrE,IAAI,OAAO,GAAmB,IAAI;IAElC,IAAI,UAAU,EAAE;AACZ,QAAA,IAAI,uBAAuB,CAAC,UAAU,CAAC,EAAE;YACrC,OAAO,GAAG,UAAU;QACxB;aAAO;YAEHA,IAAW,CACP,+DAA+D;gBAC/D,4EAA4E;gBAC5E,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAC5C;YACD,OAAO,GAAG,IAAI;QAClB;IACJ;AAGA,IAAA,MAAM,MAAM,GAAG,SAAS,EAAE;IAK1B,IAAI,MAAM,CAAC,eAAe,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE;AAElD,QAAA,MAAM,KAAK,GAAG,MAAM,CAAC,eAAe,EAAE;QACtC,IAAI,KAAK,EAAE;AACP,YAAA,KAAK,CAAC,OAAO,GAAG,OAAO;QAC3B;AAAO,aAAA,IAAI,MAAM,CAAC,eAAe,EAAE;AAC/B,YAAA,MAAM,CAAC,eAAe,CAAC,EAAC,OAAO,EAAC,CAAC;QACrC;aAAO;AACH,YAAAC,KAAY,CAAC,wDAAwD,CAAC;QAC1E;QACA,OAAO,IAAI,EAAE;IACjB;AAEA,IAAA,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,IAAIC,cAAqB;AAC7D,IAAA,OAAO,MAAM,CAAC,EAAC,OAAO,EAAC,EAAE,MAAM,IAAI,EAAE,CAAC;AAC1C;;;;"}
|
package/package.json
CHANGED
package/src/core/config.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import type {SessionKitConfig, AccessHooks, ProtectionRule, Session, SessionContext} from "./types";
|
|
6
6
|
import {isValidPattern, isValidRedirectPath} from "./validation";
|
|
7
|
+
import * as logger from "./logger";
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Internal config with defaults applied
|
|
@@ -96,21 +97,15 @@ export function setConfig(userConfig: SessionKitConfig): void {
|
|
|
96
97
|
const anyAccess = userConfig.access as any;
|
|
97
98
|
if (anyAccess.globalProtect !== undefined && userConfig.globalProtect === undefined) {
|
|
98
99
|
newConfig.globalProtect = anyAccess.globalProtect;
|
|
99
|
-
|
|
100
|
-
console.warn('[SessionKit] Deprecation: globalProtect should be at the top level of configuration, not inside "access".');
|
|
101
|
-
}
|
|
100
|
+
logger.warn('Deprecation: globalProtect should be at the top level of configuration, not inside "access".');
|
|
102
101
|
}
|
|
103
102
|
if (anyAccess.exclude !== undefined && userConfig.exclude === undefined) {
|
|
104
103
|
newConfig.exclude = anyAccess.exclude;
|
|
105
|
-
|
|
106
|
-
console.warn('[SessionKit] Deprecation: exclude should be at the top level of configuration, not inside "access".');
|
|
107
|
-
}
|
|
104
|
+
logger.warn('Deprecation: exclude should be at the top level of configuration, not inside "access".');
|
|
108
105
|
}
|
|
109
106
|
if (anyAccess.debug !== undefined && userConfig.debug === undefined) {
|
|
110
107
|
newConfig.debug = anyAccess.debug;
|
|
111
|
-
|
|
112
|
-
console.warn('[SessionKit] Deprecation: debug should be at the top level of configuration, not inside "access".');
|
|
113
|
-
}
|
|
108
|
+
logger.warn('Deprecation: debug should be at the top level of configuration, not inside "access".');
|
|
114
109
|
}
|
|
115
110
|
}
|
|
116
111
|
|
|
@@ -8,6 +8,7 @@ import { getConfig } from "./config";
|
|
|
8
8
|
import { matchesPattern } from "./matcher";
|
|
9
9
|
import type { ProtectionRule, Session } from "./types";
|
|
10
10
|
import { isValidSessionStructure } from "./validation";
|
|
11
|
+
import * as logger from "./logger";
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Check if session satisfies a protection rule
|
|
@@ -20,9 +21,7 @@ async function checkRule(rule: ProtectionRule, session: Session | null): Promise
|
|
|
20
21
|
try {
|
|
21
22
|
return await access.check(rule, session);
|
|
22
23
|
} catch (error) {
|
|
23
|
-
|
|
24
|
-
console.error('[SessionKit] Error in custom access check hook:', error);
|
|
25
|
-
}
|
|
24
|
+
logger.error('Error in custom access check hook:', error);
|
|
26
25
|
return false;
|
|
27
26
|
}
|
|
28
27
|
}
|
|
@@ -32,9 +31,7 @@ async function checkRule(rule: ProtectionRule, session: Session | null): Promise
|
|
|
32
31
|
try {
|
|
33
32
|
return await rule.allow(session);
|
|
34
33
|
} catch (error) {
|
|
35
|
-
|
|
36
|
-
console.error('[SessionKit] Error in custom rule allow function:', error);
|
|
37
|
-
}
|
|
34
|
+
logger.error('Error in custom rule allow function:', error);
|
|
38
35
|
return false;
|
|
39
36
|
}
|
|
40
37
|
}
|
|
@@ -110,21 +107,23 @@ export function createGuardMiddleware(): MiddlewareHandler {
|
|
|
110
107
|
return next();
|
|
111
108
|
}
|
|
112
109
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
return next();
|
|
110
|
+
// Require valid session
|
|
111
|
+
if (!session || !isValidSessionStructure(session)) {
|
|
112
|
+
logger.debug(`[GlobalProtect] Redirecting to ${loginPath} because session is ${session ? 'invalid' : 'missing'}`);
|
|
113
|
+
return context.redirect(loginPath);
|
|
119
114
|
}
|
|
115
|
+
}
|
|
116
|
+
return next();
|
|
117
|
+
}
|
|
120
118
|
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
// Check if access is allowed
|
|
120
|
+
const allowed = await checkRule(rule, session);
|
|
123
121
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
122
|
+
if (!allowed) {
|
|
123
|
+
const redirectTo = rule.redirectTo ?? loginPath;
|
|
124
|
+
logger.debug(`[Guard] Redirecting to ${redirectTo} because access was denied by rule:`, rule);
|
|
125
|
+
return context.redirect(redirectTo);
|
|
126
|
+
}
|
|
128
127
|
|
|
129
128
|
return next();
|
|
130
129
|
};
|
package/src/core/logger.ts
CHANGED
|
@@ -29,3 +29,13 @@ export function warn(message: string, ...args: any[]): void {
|
|
|
29
29
|
console.warn(`[SessionKit] ${message}`, ...args);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Log info message. Always logs unless in production, but can be forced via debug flag.
|
|
35
|
+
*/
|
|
36
|
+
export function info(message: string, ...args: any[]): void {
|
|
37
|
+
const { debug } = getConfig();
|
|
38
|
+
if (debug || process.env.NODE_ENV !== 'production') {
|
|
39
|
+
console.log(`[SessionKit] ${message}`, ...args);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -7,7 +7,7 @@ import {runWithContext as defaultRunWithContext} from "./context";
|
|
|
7
7
|
import {isValidSessionStructure} from "./validation";
|
|
8
8
|
import type {Session} from "./types";
|
|
9
9
|
import {getConfig} from "./config";
|
|
10
|
-
import
|
|
10
|
+
import * as logger from "./logger";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Session key used to store session in context.session
|
|
@@ -32,7 +32,7 @@ export const sessionMiddleware: MiddlewareHandler = async (context, next) => {
|
|
|
32
32
|
session = rawSession;
|
|
33
33
|
} else {
|
|
34
34
|
// Invalid session structure - log warning and treat as unauthenticated
|
|
35
|
-
warn(
|
|
35
|
+
logger.warn(
|
|
36
36
|
'Invalid session structure detected. Session will be ignored. ' +
|
|
37
37
|
'Ensure context.session.set("__session__", ...) has the correct structure. ' +
|
|
38
38
|
'Received: ' + JSON.stringify(rawSession)
|
|
@@ -54,8 +54,8 @@ export const sessionMiddleware: MiddlewareHandler = async (context, next) => {
|
|
|
54
54
|
store.session = session;
|
|
55
55
|
} else if (config.setContextStore) {
|
|
56
56
|
config.setContextStore({session});
|
|
57
|
-
} else
|
|
58
|
-
|
|
57
|
+
} else {
|
|
58
|
+
logger.error('getContextStore returned undefined, cannot set session');
|
|
59
59
|
}
|
|
60
60
|
return next();
|
|
61
61
|
}
|