/** * Роуты для управления IPSec паролями * Пароли хранятся в S3 в зашифрованном виде */ const { sendError, sendOk } = require('../middleware/errorHandler'); const { readS3TextObject, writeS3JsonObject, deleteS3Object } = require('../services/s3Service'); const { encrypt, decrypt } = require('../utils/encryption'); const S3_KEY = 'network-config/ipsec-passwords.json'; /** * GET /api/ipsec-passwords - получить список паролей (без расшифровки) */ async function getIpsecPasswords(req, res) { try { const data = await readS3TextObject(S3_KEY).catch(() => ({ body: '[]' })); let passwords = []; try { passwords = JSON.parse(data.body || '[]'); if (!Array.isArray(passwords)) { passwords = []; } } catch (parseError) { passwords = []; } // Возвращаем список паролей БЕЗ расшифровки (только метаданные) const result = passwords.map(p => ({ id: p.id, name: p.name || '', description: p.description || '', createdAt: p.createdAt || null, updatedAt: p.updatedAt || null, // НЕ возвращаем зашифрованный пароль })); res.json(result); } catch (error) { console.error('Error reading IPSec passwords:', error); return sendError(res, 500, 'Error reading IPSec passwords', 'E_S3'); } } /** * GET /api/ipsec-passwords/:id - получить конкретный пароль (расшифрованный) */ async function getIpsecPassword(req, res) { const { id } = req.params; try { const data = await readS3TextObject(S3_KEY).catch(() => ({ body: '[]' })); let passwords = []; try { passwords = JSON.parse(data.body || '[]'); if (!Array.isArray(passwords)) { passwords = []; } } catch (parseError) { passwords = []; } const password = passwords.find(p => p.id === id); if (!password) { return sendError(res, 404, 'IPSec password not found', 'E_NOT_FOUND'); } // Проверяем наличие зашифрованного пароля if (!password.encryptedPassword) { console.error(`IPSec password ${id} has no encryptedPassword field`); console.error('Password object:', JSON.stringify(password, null, 2)); return sendError(res, 500, 'Password has no encrypted data', 'E_DECRYPT'); } // Расшифровываем пароль для возврата let decryptedPassword = ''; try { if (typeof password.encryptedPassword !== 'string') { console.error(`IPSec password ${id} encryptedPassword is not a string:`, typeof password.encryptedPassword); return sendError(res, 500, 'Invalid encrypted password format', 'E_DECRYPT'); } decryptedPassword = decrypt(password.encryptedPassword); if (!decryptedPassword) { console.error(`IPSec password ${id} decrypted to empty string`); return sendError(res, 500, 'Password decrypted to empty string', 'E_DECRYPT'); } } catch (decryptError) { console.error('Error decrypting password:', decryptError); console.error('Password ID:', id); console.error('Encrypted password length:', password.encryptedPassword?.length); console.error('Encrypted password preview:', password.encryptedPassword?.substring(0, 50)); console.error('Encrypted password format check:', password.encryptedPassword?.split(':').length, 'parts'); // Проверяем, возможно это старый формат или другой ключ const errorMessage = decryptError.message || 'Unknown decryption error'; return sendError(res, 500, `Error decrypting password: ${errorMessage}. Check if ENCRYPTION_KEY is correct.`, 'E_DECRYPT'); } res.json({ id: password.id, name: password.name || '', description: password.description || '', password: decryptedPassword, // Расшифрованный пароль createdAt: password.createdAt || null, updatedAt: password.updatedAt || null, }); } catch (error) { console.error('Error reading IPSec password:', error); return sendError(res, 500, 'Error reading IPSec password', 'E_S3'); } } /** * POST /api/ipsec-passwords - создать новый пароль */ async function createIpsecPassword(req, res) { const { name, password, description } = req.body; if (!name || !password) { return sendError(res, 400, 'Name and password are required', 'E_BAD_REQUEST'); } try { // Читаем текущие пароли const data = await readS3TextObject(S3_KEY).catch(() => ({ body: '[]' })); let passwords = []; try { passwords = JSON.parse(data.body || '[]'); if (!Array.isArray(passwords)) { passwords = []; } } catch (parseError) { passwords = []; } // Проверяем, нет ли уже пароля с таким именем if (passwords.some(p => p.name === name)) { return sendError(res, 400, 'Password with this name already exists', 'E_DUPLICATE'); } // Шифруем пароль let encryptedPassword; try { encryptedPassword = encrypt(password); if (!encryptedPassword || encryptedPassword.trim() === '') { console.error('Failed to encrypt password: result is empty'); return sendError(res, 500, 'Failed to encrypt password', 'E_ENCRYPT'); } // Проверяем формат зашифрованного пароля const parts = encryptedPassword.split(':'); if (parts.length !== 3) { console.error('Invalid encrypted password format:', encryptedPassword.substring(0, 50)); return sendError(res, 500, 'Failed to encrypt password: invalid format', 'E_ENCRYPT'); } } catch (encryptError) { console.error('Error encrypting password:', encryptError); return sendError(res, 500, `Failed to encrypt password: ${encryptError.message}`, 'E_ENCRYPT'); } // Создаем новый пароль const newPassword = { id: `ipsec-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`, name: String(name).trim(), description: String(description || '').trim(), encryptedPassword, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), }; passwords.push(newPassword); // Сохраняем в S3 const meta = await writeS3JsonObject(S3_KEY, passwords); // Возвращаем без расшифрованного пароля, но с ID res.json({ id: newPassword.id, name: newPassword.name, description: newPassword.description, createdAt: newPassword.createdAt, updatedAt: newPassword.updatedAt, }); } catch (error) { console.error('Error creating IPSec password:', error); return sendError(res, 500, 'Error creating IPSec password', 'E_S3'); } } /** * PUT /api/ipsec-passwords/:id - обновить пароль */ async function updateIpsecPassword(req, res) { const { id } = req.params; const { name, password, description } = req.body; try { // Читаем текущие пароли const data = await readS3TextObject(S3_KEY).catch(() => ({ body: '[]' })); let passwords = []; try { passwords = JSON.parse(data.body || '[]'); if (!Array.isArray(passwords)) { passwords = []; } } catch (parseError) { passwords = []; } const index = passwords.findIndex(p => p.id === id); if (index === -1) { return sendError(res, 404, 'IPSec password not found', 'E_NOT_FOUND'); } // Проверяем уникальность имени (если изменилось) if (name && name !== passwords[index].name) { if (passwords.some(p => p.id !== id && p.name === name)) { return sendError(res, 400, 'Password with this name already exists', 'E_DUPLICATE'); } } // Обновляем пароль if (name !== undefined) { passwords[index].name = String(name).trim(); } if (description !== undefined) { passwords[index].description = String(description || '').trim(); } if (password !== undefined) { let encryptedPassword; try { encryptedPassword = encrypt(password); if (!encryptedPassword || encryptedPassword.trim() === '') { console.error('Failed to encrypt password: result is empty'); return sendError(res, 500, 'Failed to encrypt password', 'E_ENCRYPT'); } // Проверяем формат зашифрованного пароля const parts = encryptedPassword.split(':'); if (parts.length !== 3) { console.error('Invalid encrypted password format:', encryptedPassword.substring(0, 50)); return sendError(res, 500, 'Failed to encrypt password: invalid format', 'E_ENCRYPT'); } passwords[index].encryptedPassword = encryptedPassword; } catch (encryptError) { console.error('Error encrypting password:', encryptError); return sendError(res, 500, `Failed to encrypt password: ${encryptError.message}`, 'E_ENCRYPT'); } } passwords[index].updatedAt = new Date().toISOString(); // Сохраняем в S3 const meta = await writeS3JsonObject(S3_KEY, passwords); // Возвращаем без расшифрованного пароля res.json({ id: passwords[index].id, name: passwords[index].name, description: passwords[index].description, createdAt: passwords[index].createdAt, updatedAt: passwords[index].updatedAt, }); } catch (error) { console.error('Error updating IPSec password:', error); return sendError(res, 500, 'Error updating IPSec password', 'E_S3'); } } /** * DELETE /api/ipsec-passwords/:id - удалить пароль */ async function deleteIpsecPassword(req, res) { const { id } = req.params; try { // Читаем текущие пароли const data = await readS3TextObject(S3_KEY).catch(() => ({ body: '[]' })); let passwords = []; try { passwords = JSON.parse(data.body || '[]'); if (!Array.isArray(passwords)) { passwords = []; } } catch (parseError) { passwords = []; } const filtered = passwords.filter(p => p.id !== id); if (filtered.length === passwords.length) { return sendError(res, 404, 'IPSec password not found', 'E_NOT_FOUND'); } // Сохраняем в S3 const meta = await writeS3JsonObject(S3_KEY, filtered); res.json({ success: true }); } catch (error) { console.error('Error deleting IPSec password:', error); return sendError(res, 500, 'Error deleting IPSec password', 'E_S3'); } } module.exports = { getIpsecPasswords, getIpsecPassword, createIpsecPassword, updateIpsecPassword, deleteIpsecPassword, };