diff --git a/server.js b/server.js index 23cb23c..2d941bd 100644 --- a/server.js +++ b/server.js @@ -76,6 +76,25 @@ async function start() { // JWKS endpoint для валидации токенов внешними сервисами (только в mock-режиме) if (auth.jwksHandler) app.get('/.well-known/jwks.json', auth.jwksHandler); + // ── /export — публичный (ВРЕМЕННО без авторизации) ─────────────────────── + // 🔮 КОГДА Keycloak SSO: заменить на app.use('/export', auth.bearerMiddleware, ...) + app.get('/export', + /* 🔮 auth.bearerMiddleware, */ + async (req, res) => { + try { + const cidrs = await q.getExportCIDRs(); + const text = cidrs.join('\n') + (cidrs.length ? '\n' : ''); + res.set('Content-Type', 'text/plain; charset=utf-8'); + const fname = req.query.filename || 'white-list.txt'; + const disp = req.query.view === '1' ? 'inline' : 'attachment'; + res.set('Content-Disposition', disp + '; filename="' + fname + '"'); + res.send(text); + } catch (e) { + console.error('[export] Error:', e); + res.status(500).send('Ошибка экспорта'); + } + } + ); // ── REST API v1 (Bearer JWT, без сессий/CSRF) ───────────────────────────── const { createApiRouter } = require('./src/api/index'); diff --git a/tests/integration.js b/tests/integration.js index 2211b5c..2227209 100644 --- a/tests/integration.js +++ b/tests/integration.js @@ -299,18 +299,19 @@ async function cleanup() { log(r.headers['content-disposition']?.includes('custom.txt'), '4g: имя файла custom.txt', `disp=${r.headers['content-disposition']}`); } - // 4h: GET /export без сессии → редирект + // 4h: GET /export без сессии → 200 (ПУБЛИЧНЫЙ, ВРЕМЕННО) { const r = await supertest(app).get('/export'); - log(r.status === 302, '4h: GET /export без сессии → 302', `status=${r.status}`); + log(r.status === 200, '4h: GET /export без авторизации → 200 (публичный)', `status=${r.status}`); + log(r.headers['content-type']?.includes('text/plain'), '4i: Content-Type text/plain'); } - // 4i: GET /export?view=1&filename=test.txt + // 4j: GET /export?view=1&filename=test.txt { - const r = await agentExp.get('/export?view=1&filename=test.txt'); - log(r.status === 200, '4i: GET /export?view=1&filename=test.txt → 200', `status=${r.status}`); - log(r.headers['content-disposition']?.includes('inline'), '4j: inline (view=1 приоритетнее filename)'); - log(r.headers['content-disposition']?.includes('test.txt'), '4k: имя файла test.txt'); + const r = await supertest(app).get('/export?view=1&filename=test.txt'); + log(r.status === 200, '4j: GET /export?view=1&filename=test.txt → 200', `status=${r.status}`); + log(r.headers['content-disposition']?.includes('inline'), '4k: inline (view=1 приоритетнее filename)'); + log(r.headers['content-disposition']?.includes('test.txt'), '4l: имя файла test.txt'); } // ════════════════════════════════════════════════════════════════════════════ diff --git a/tests/tz-compliance.js b/tests/tz-compliance.js index b7752d3..79e1214 100644 --- a/tests/tz-compliance.js +++ b/tests/tz-compliance.js @@ -240,13 +240,15 @@ function cidr(ip) { return ip.includes('/') ? ip : ip + '/32'; } // ══════════════════════════════════════════════════ console.log('── ТЗ 4.7: Export ──'); { - // 4.7a: /api/v1/entries/export (требует Bearer token) - const cr = await req.post('/api/v1/entries').set(bearer(tokUser)).send({ value: C.A, comment: 'export-test' }); - - const r = await req.get('/api/v1/entries/export').set(bearer(tokUser)); - log(r.status === 200, '4.7a: GET /api/v1/entries/export → 200', `status=${r.status}`); + // 4.7a: /export — публичный, без авторизации (ВРЕМЕННО) + const r = await req.get('/export'); + log(r.status === 200, '4.7a: GET /export → 200 (без авторизации)', `status=${r.status}`); log((r.headers['content-type'] || '').includes('text/plain'), '4.7b: Content-Type text/plain'); - log(r.text.includes(cidr(C.A)), '4.7c: export содержит добавленный CIDR'); + + // 4.7c: /export содержит добавленный CIDR + const cr = await req.post('/api/v1/entries').set(bearer(tokUser)).send({ value: C.A, comment: 'export-test' }); + const r2 = await req.get('/export'); + log(r2.text.includes(cidr(C.A)), '4.7c: /export содержит добавленный CIDR'); } // ══════════════════════════════════════════════════