|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const logger = require('../utils/logger') |
|
|
const { CLIENT_DEFINITIONS, getAllClientDefinitions } = require('./clientDefinitions') |
|
|
const ClaudeCodeValidator = require('./clients/claudeCodeValidator') |
|
|
const GeminiCliValidator = require('./clients/geminiCliValidator') |
|
|
const CodexCliValidator = require('./clients/codexCliValidator') |
|
|
const DroidCliValidator = require('./clients/droidCliValidator') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ClientValidator { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static getValidator(clientId) { |
|
|
switch (clientId) { |
|
|
case 'claude_code': |
|
|
return ClaudeCodeValidator |
|
|
case 'gemini_cli': |
|
|
return GeminiCliValidator |
|
|
case 'codex_cli': |
|
|
return CodexCliValidator |
|
|
case 'droid_cli': |
|
|
return DroidCliValidator |
|
|
default: |
|
|
logger.warn(`Unknown client ID: ${clientId}`) |
|
|
return null |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static getSupportedClients() { |
|
|
return ['claude_code', 'gemini_cli', 'codex_cli', 'droid_cli'] |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static validateClient(clientId, req) { |
|
|
const validator = this.getValidator(clientId) |
|
|
|
|
|
if (!validator) { |
|
|
logger.warn(`No validator found for client: ${clientId}`) |
|
|
return false |
|
|
} |
|
|
|
|
|
try { |
|
|
return validator.validate(req) |
|
|
} catch (error) { |
|
|
logger.error(`Error validating client ${clientId}:`, error) |
|
|
return false |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static validateRequest(allowedClients, req) { |
|
|
const userAgent = req.headers['user-agent'] || '' |
|
|
const clientIP = req.ip || req.connection?.remoteAddress || 'unknown' |
|
|
|
|
|
|
|
|
logger.api(`🔍 Starting client validation for User-Agent: "${userAgent}"`) |
|
|
logger.api(` Allowed clients: ${allowedClients.join(', ')}`) |
|
|
logger.api(` Request from IP: ${clientIP}`) |
|
|
|
|
|
|
|
|
for (const clientId of allowedClients) { |
|
|
const validator = this.getValidator(clientId) |
|
|
|
|
|
if (!validator) { |
|
|
logger.warn(`Skipping unknown client ID: ${clientId}`) |
|
|
continue |
|
|
} |
|
|
|
|
|
logger.debug(`Checking against ${validator.getName()}...`) |
|
|
|
|
|
try { |
|
|
if (validator.validate(req)) { |
|
|
|
|
|
logger.api(`✅ Client validated: ${validator.getName()} (${clientId})`) |
|
|
logger.api(` Matched User-Agent: "${userAgent}"`) |
|
|
|
|
|
return { |
|
|
allowed: true, |
|
|
matchedClient: clientId, |
|
|
clientName: validator.getName(), |
|
|
clientInfo: Object.values(CLIENT_DEFINITIONS).find((def) => def.id === clientId) |
|
|
} |
|
|
} |
|
|
} catch (error) { |
|
|
logger.error(`Error during validation for ${clientId}:`, error) |
|
|
continue |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
logger.api(`❌ No matching client found for User-Agent: "${userAgent}"`) |
|
|
return { |
|
|
allowed: false, |
|
|
matchedClient: null, |
|
|
reason: 'No matching client found' |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static getClientInfo(clientId) { |
|
|
const validator = this.getValidator(clientId) |
|
|
if (!validator) { |
|
|
return null |
|
|
} |
|
|
|
|
|
return validator.getInfo() |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static getAvailableClients() { |
|
|
|
|
|
return getAllClientDefinitions() |
|
|
} |
|
|
} |
|
|
|
|
|
module.exports = ClientValidator |
|
|
|