315 lines
9.8 KiB
JavaScript
315 lines
9.8 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Test script to demonstrate the composable security architecture
|
|
*
|
|
* This script simulates loading and using the composable security system
|
|
* with different configurations. It can be run from either the project root
|
|
* or from within the test/ directory.
|
|
*
|
|
* Usage:
|
|
* node test/composable-security.test.js (from project root)
|
|
* node composable-security.test.js (from test/ directory)
|
|
*/
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
// Mock the require function first, before any other requires
|
|
const originalRequire = require;
|
|
|
|
// Mock base security plugin (friends)
|
|
const mockFriendsPlugin = (log, loga, argv) => ({
|
|
retrieveOwner: (cb) => {
|
|
console.log('[FRIENDS] Retrieving owner...');
|
|
cb();
|
|
},
|
|
getOwner: () => 'test-owner',
|
|
setOwner: (id, cb) => {
|
|
console.log(`[FRIENDS] Setting owner to: ${id}`);
|
|
cb();
|
|
},
|
|
getUser: (req) => {
|
|
if (req.session && req.session.friend) {
|
|
return 'test-user';
|
|
}
|
|
return '';
|
|
},
|
|
isAuthorized: (req) => {
|
|
return req.session && req.session.friend === 'test-secret';
|
|
},
|
|
isAdmin: (req) => {
|
|
return req.session && req.session.friend === 'admin-secret';
|
|
},
|
|
defineRoutes: (app, cors, updateOwner) => {
|
|
app.post('/login', 'friends-login-handler');
|
|
app.get('/logout', 'friends-logout-handler');
|
|
},
|
|
_debug: { authProvider: 'wiki-security-friends' }
|
|
});
|
|
|
|
// Mock plugin with security enhancer (new approach)
|
|
const mockPluginWithSecurityEnhancer = {
|
|
securityEnhancer: (log, loga, argv, baseHandler) => ({
|
|
isAuthorized: (req, baseIsAuthorized) => {
|
|
console.log('[PLUGIN-ENHANCER] Checking authorization');
|
|
return baseIsAuthorized();
|
|
},
|
|
getUser: (req, baseGetUser) => {
|
|
if (req.tokenAuth && req.tokenAuth.user) {
|
|
console.log('[PLUGIN-ENHANCER] Returning token user');
|
|
return req.tokenAuth.user;
|
|
}
|
|
return baseGetUser();
|
|
},
|
|
middleware: (req, res, next) => {
|
|
console.log('[PLUGIN-ENHANCER] Middleware processing request');
|
|
// Simulate token validation
|
|
if (req.headers.authorization === 'Bearer test-token') {
|
|
req.tokenAuth = { user: 'token-user' };
|
|
}
|
|
next();
|
|
},
|
|
defineRoutes: (app, cors, updateOwner) => {
|
|
console.log('[PLUGIN-ENHANCER] Defining routes');
|
|
app.get('/plugin/test/tokens', 'token-management-handler');
|
|
}
|
|
}),
|
|
startServer: (params) => {
|
|
console.log('[PLUGIN] Regular plugin server started');
|
|
}
|
|
};
|
|
|
|
// Mock authz enhancer (simplified)
|
|
const mockTokenEnhancer = (log, loga, argv, baseHandler) => ({
|
|
middleware: (req, res, next) => {
|
|
console.log('[TOKENS] Processing token middleware');
|
|
next();
|
|
},
|
|
getUser: (req, baseGetUser) => {
|
|
const baseUser = baseGetUser();
|
|
if (baseUser) return baseUser;
|
|
|
|
// Check for token auth
|
|
if (req.headers && req.headers.authorization) {
|
|
console.log('[TOKENS] Token authentication detected');
|
|
return 'token-user';
|
|
}
|
|
return '';
|
|
},
|
|
defineRoutes: (app, cors, updateOwner) => {
|
|
app.post('/auth/tokens', 'create-token-handler');
|
|
app.get('/auth/tokens', 'list-tokens-handler');
|
|
}
|
|
});
|
|
|
|
const mockRateLimitEnhancer = (log, loga, argv, baseHandler) => ({
|
|
middleware: (req, res, next) => {
|
|
console.log('[RATELIMIT] Processing rate limit middleware');
|
|
next();
|
|
},
|
|
defineRoutes: (app, cors, updateOwner) => {
|
|
app.get('/auth/ratelimit/status', 'ratelimit-status-handler');
|
|
}
|
|
});
|
|
|
|
// Set up module mocking
|
|
require = function(module) {
|
|
switch(module) {
|
|
case 'wiki-security-friends':
|
|
return mockFriendsPlugin;
|
|
case 'wiki-plugin-useraccesstokens':
|
|
return mockPluginWithSecurityEnhancer;
|
|
case 'wiki-plugin-ratelimit':
|
|
return mockPluginWithSecurityEnhancer; // Same structure for testing
|
|
default:
|
|
return originalRequire(module);
|
|
}
|
|
};
|
|
|
|
// Mock dependencies that would normally be provided by the wiki server
|
|
const mockLog = (msg) => console.log(`[LOG] ${msg}`);
|
|
const mockLoga = mockLog;
|
|
|
|
// Support running from different directories
|
|
let statusPath = '/tmp/fedwiki-test-status';
|
|
let indexPath = '../index.js'; // Default: relative to test directory
|
|
|
|
// Check if we're running from the root directory
|
|
const cwd = process.cwd();
|
|
const testDir = __dirname;
|
|
|
|
if (fs.existsSync(path.join(cwd, 'index.js'))) {
|
|
// Running from project root - use relative path to current working directory
|
|
statusPath = './test-status';
|
|
indexPath = path.join(cwd, 'index.js');
|
|
} else {
|
|
// Running from test directory or elsewhere - use relative to test file
|
|
statusPath = './test-status';
|
|
indexPath = '../index.js';
|
|
}
|
|
|
|
const mockArgv = {
|
|
status: statusPath,
|
|
auth_provider: mockFriendsPlugin,
|
|
authz_enhancers: [mockPluginWithSecurityEnhancer, mockPluginWithSecurityEnhancer], // Two mock plugins
|
|
ratelimit_config: {
|
|
windowMs: 60000, // 1 minute for testing
|
|
maxRequests: 10,
|
|
maxAuthRequests: 3
|
|
}
|
|
};
|
|
|
|
// Mock Express app
|
|
const mockApp = {
|
|
use: (path, middleware) => {
|
|
if (typeof path === 'function') {
|
|
console.log(`[APP] Added global middleware`);
|
|
} else {
|
|
console.log(`[APP] Added middleware for: ${path || 'all routes'}`);
|
|
}
|
|
},
|
|
get: (path, ...handlers) => console.log(`[APP] Added GET route: ${path}`),
|
|
post: (path, ...handlers) => console.log(`[APP] Added POST route: ${path}`),
|
|
delete: (path, ...handlers) => console.log(`[APP] Added DELETE route: ${path}`)
|
|
};
|
|
|
|
const mockCors = (req, res, next) => next();
|
|
const mockUpdateOwner = (owner) => console.log(`[OWNER] Updated to: ${owner}`);
|
|
|
|
console.log('=== Composable Security Architecture Test ===\n');
|
|
console.log(`Running from: ${process.cwd()}`);
|
|
console.log(`Using index path: ${indexPath}`);
|
|
console.log(`Using status path: ${statusPath}\n`);
|
|
|
|
// Test 1: Load the composable security system
|
|
console.log('1. Loading composable security system...');
|
|
try {
|
|
const ComposableSecurity = require(indexPath);
|
|
const security = ComposableSecurity(mockLog, mockLoga, mockArgv);
|
|
|
|
console.log('✓ Composable security loaded successfully');
|
|
console.log(`✓ Debug info:`, security._debug);
|
|
console.log();
|
|
|
|
// Test 2: Initialize routes
|
|
console.log('2. Initializing routes...');
|
|
security.defineRoutes(mockApp, mockCors, mockUpdateOwner);
|
|
console.log('✓ Routes initialized');
|
|
console.log();
|
|
|
|
// Test 3: Test owner management
|
|
console.log('3. Testing owner management...');
|
|
security.retrieveOwner(() => {
|
|
const owner = security.getOwner();
|
|
console.log(`✓ Current owner: ${owner}`);
|
|
});
|
|
console.log();
|
|
|
|
// Test 4: Test request handling
|
|
console.log('4. Testing request handling...');
|
|
|
|
// Mock requests
|
|
const mockReqAuth = {
|
|
session: { friend: 'test-secret' },
|
|
ip: '127.0.0.1',
|
|
path: '/test',
|
|
get: () => null,
|
|
query: {}
|
|
};
|
|
|
|
const mockReqToken = {
|
|
session: {},
|
|
ip: '127.0.0.1',
|
|
path: '/api/test',
|
|
headers: { authorization: 'Bearer uat_test123' },
|
|
get: (header) => header === 'Authorization' ? 'Bearer uat_test123' : null,
|
|
query: {}
|
|
};
|
|
|
|
const mockReqUnauth = {
|
|
session: {},
|
|
ip: '127.0.0.1',
|
|
path: '/test',
|
|
get: () => null,
|
|
query: {}
|
|
};
|
|
|
|
// Test authenticated request
|
|
console.log('Testing authenticated request...');
|
|
console.log(` User: ${security.getUser(mockReqAuth)}`);
|
|
console.log(` Authorized: ${security.isAuthorized(mockReqAuth)}`);
|
|
console.log(` Admin: ${security.isAdmin(mockReqAuth)}`);
|
|
|
|
// Test token request
|
|
console.log('Testing token request...');
|
|
console.log(` User: ${security.getUser(mockReqToken)}`);
|
|
console.log(` Authorized: ${security.isAuthorized(mockReqToken)}`);
|
|
|
|
// Test unauthenticated request
|
|
console.log('Testing unauthenticated request...');
|
|
console.log(` User: ${security.getUser(mockReqUnauth)}`);
|
|
console.log(` Authorized: ${security.isAuthorized(mockReqUnauth)}`);
|
|
|
|
console.log('✓ Request handling tests completed');
|
|
console.log();
|
|
|
|
console.log('=== Test Summary ===');
|
|
console.log('✓ All tests completed successfully');
|
|
console.log('✓ Composable security architecture is working');
|
|
console.log('✓ Auth provider and authz enhancers loaded correctly');
|
|
console.log('✓ Security interface maintained compatibility');
|
|
|
|
} catch (error) {
|
|
console.error('✗ Test failed:', error.message);
|
|
console.error(error.stack);
|
|
process.exit(1);
|
|
}
|
|
|
|
// Test 5: Plugin-based enhancers
|
|
console.log('\n5. Testing plugin-based enhancers...');
|
|
try {
|
|
const mockArgvPlugin = {
|
|
status: statusPath,
|
|
auth_provider: 'wiki-security-friends',
|
|
authz_enhancers: ['wiki-plugin-useraccesstokens', 'wiki-plugin-ratelimit']
|
|
};
|
|
|
|
const ComposableSecurity = require(indexPath);
|
|
const securityPlugin = ComposableSecurity(mockLog, mockLoga, mockArgvPlugin);
|
|
|
|
console.log('✓ Plugin-based security loaded successfully');
|
|
console.log(`✓ Loaded enhancers: ${securityPlugin._debug.loadedEnhancers.join(', ')}`);
|
|
|
|
// Test routes initialization
|
|
console.log('Initializing plugin-based routes...');
|
|
securityPlugin.defineRoutes(mockApp, mockCors, mockUpdateOwner);
|
|
|
|
// Test token request with plugin enhancer
|
|
const mockReqPluginToken = {
|
|
session: {},
|
|
ip: '127.0.0.1',
|
|
path: '/api/test',
|
|
headers: { authorization: 'Bearer test-token' },
|
|
get: (header) => header === 'Authorization' ? 'Bearer test-token' : null,
|
|
query: {}
|
|
};
|
|
|
|
console.log('Testing plugin token request...');
|
|
console.log(` User: ${securityPlugin.getUser(mockReqPluginToken)}`);
|
|
console.log(` Authorized: ${securityPlugin.isAuthorized(mockReqPluginToken)}`);
|
|
|
|
console.log('✓ Plugin-based enhancer tests completed');
|
|
console.log();
|
|
|
|
} catch (error) {
|
|
console.error('✗ Plugin-based test failed:', error.message);
|
|
console.error(error.stack);
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log('=== Final Test Summary ===');
|
|
console.log('✓ All tests completed successfully');
|
|
console.log('✓ Plugin-based enhancers working correctly');
|
|
console.log('✓ Composable security architecture is fully functional');
|