Files
cannaiq/backend/dist/middleware/wordpressPermissions.js
Kelly 66e07b2009 fix(monitor): remove non-existent worker columns from job_run_logs query
The job_run_logs table tracks scheduled job orchestration, not individual
worker jobs. Worker info (worker_id, worker_hostname) belongs on
dispensary_crawl_jobs, not job_run_logs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 18:45:05 -07:00

164 lines
5.7 KiB
JavaScript

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateWordPressPermissions = validateWordPressPermissions;
const migrate_1 = require("../db/migrate");
const ipaddr_js_1 = __importDefault(require("ipaddr.js"));
/**
* Validates if an IP address matches any of the allowed IP patterns
* Supports CIDR notation and wildcards
*/
function isIpAllowed(clientIp, allowedIps) {
try {
const clientAddr = ipaddr_js_1.default.process(clientIp);
for (const allowedIp of allowedIps) {
const trimmed = allowedIp.trim();
if (!trimmed)
continue;
// Check for CIDR notation
if (trimmed.includes('/')) {
try {
const [subnet, bits] = trimmed.split('/');
const range = ipaddr_js_1.default.parseCIDR(trimmed);
if (clientAddr.match(range)) {
return true;
}
}
catch (e) {
console.warn(`Invalid CIDR notation: ${trimmed}`);
continue;
}
}
else {
// Exact match
try {
const allowedAddr = ipaddr_js_1.default.process(trimmed);
if (clientAddr.toString() === allowedAddr.toString()) {
return true;
}
}
catch (e) {
console.warn(`Invalid IP address: ${trimmed}`);
continue;
}
}
}
return false;
}
catch (error) {
console.error('Error processing client IP:', error);
return false;
}
}
/**
* Validates if a domain matches any of the allowed domain patterns
* Supports wildcard subdomains (*.example.com)
*/
function isDomainAllowed(origin, allowedDomains) {
try {
// Extract domain from origin URL
const url = new URL(origin);
const domain = url.hostname;
for (const allowedDomain of allowedDomains) {
const trimmed = allowedDomain.trim();
if (!trimmed)
continue;
// Wildcard subdomain support
if (trimmed.startsWith('*.')) {
const baseDomain = trimmed.substring(2);
if (domain === baseDomain || domain.endsWith('.' + baseDomain)) {
return true;
}
}
else {
// Exact match
if (domain === trimmed) {
return true;
}
}
}
return false;
}
catch (error) {
console.error('Error processing domain:', error);
return false;
}
}
/**
* WordPress API Permissions Middleware
* Validates API access based on WordPress permissions table
*/
async function validateWordPressPermissions(req, res, next) {
// Get API key from header
const apiKey = req.headers['x-api-key'];
// If no API key provided, skip WordPress validation
if (!apiKey) {
return next();
}
try {
// Query WordPress permissions table
const result = await migrate_1.pool.query(`
SELECT id, user_name, api_key, allowed_ips, allowed_domains, is_active
FROM wp_dutchie_api_permissions
WHERE api_key = $1 AND is_active = 1
`, [apiKey]);
if (result.rows.length === 0) {
return res.status(401).json({
error: 'Invalid API key'
});
}
const permission = result.rows[0];
// Get client IP
const clientIp = req.headers['x-forwarded-for']?.split(',')[0].trim() ||
req.headers['x-real-ip'] ||
req.ip ||
req.connection.remoteAddress ||
'';
// Validate IP if configured
if (permission.allowed_ips) {
const allowedIps = permission.allowed_ips.split('\n').filter((ip) => ip.trim());
if (allowedIps.length > 0 && !isIpAllowed(clientIp, allowedIps)) {
return res.status(403).json({
error: 'IP address not allowed',
client_ip: clientIp
});
}
}
// Validate domain if configured
const origin = req.get('origin') || req.get('referer') || '';
if (permission.allowed_domains && origin) {
const allowedDomains = permission.allowed_domains.split('\n').filter((d) => d.trim());
if (allowedDomains.length > 0 && !isDomainAllowed(origin, allowedDomains)) {
return res.status(403).json({
error: 'Domain not allowed',
origin: origin
});
}
}
// Update last_used_at timestamp (async, don't wait)
migrate_1.pool.query(`
UPDATE wp_dutchie_api_permissions
SET last_used_at = CURRENT_TIMESTAMP
WHERE id = $1
`, [permission.id]).catch((err) => {
console.error('Error updating last_used_at:', err);
});
// Set apiToken on request for tracking middleware
// Default rate limit of 100 requests/minute for WordPress permissions
req.apiToken = {
id: permission.id,
name: permission.user_name,
rate_limit: 100
};
next();
}
catch (error) {
console.error('WordPress permissions validation error:', error);
return res.status(500).json({
error: 'Internal server error during API validation'
});
}
}