v0.5.11: /export публичный (временно, с заделом на Keycloak SSO)
Изменения:
- server.js: публичный GET /export до всей авторизации
🔮 Для Keycloak SSO: раскомментировать auth.bearerMiddleware
- Удалён ошибочно созданный src/routes/exp.js
- Обновлены тесты: /export доступен без авторизации
This commit is contained in:
@@ -76,6 +76,25 @@ async function start() {
|
|||||||
// JWKS endpoint для валидации токенов внешними сервисами (только в mock-режиме)
|
// JWKS endpoint для валидации токенов внешними сервисами (только в mock-режиме)
|
||||||
if (auth.jwksHandler) app.get('/.well-known/jwks.json', auth.jwksHandler);
|
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) ─────────────────────────────
|
// ── REST API v1 (Bearer JWT, без сессий/CSRF) ─────────────────────────────
|
||||||
const { createApiRouter } = require('./src/api/index');
|
const { createApiRouter } = require('./src/api/index');
|
||||||
|
|||||||
@@ -299,18 +299,19 @@ async function cleanup() {
|
|||||||
log(r.headers['content-disposition']?.includes('custom.txt'), '4g: имя файла custom.txt', `disp=${r.headers['content-disposition']}`);
|
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');
|
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');
|
const r = await supertest(app).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.status === 200, '4j: 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('inline'), '4k: inline (view=1 приоритетнее filename)');
|
||||||
log(r.headers['content-disposition']?.includes('test.txt'), '4k: имя файла test.txt');
|
log(r.headers['content-disposition']?.includes('test.txt'), '4l: имя файла test.txt');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ════════════════════════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════════════════════════
|
||||||
|
|||||||
@@ -240,13 +240,15 @@ function cidr(ip) { return ip.includes('/') ? ip : ip + '/32'; }
|
|||||||
// ══════════════════════════════════════════════════
|
// ══════════════════════════════════════════════════
|
||||||
console.log('── ТЗ 4.7: Export ──');
|
console.log('── ТЗ 4.7: Export ──');
|
||||||
{
|
{
|
||||||
// 4.7a: /api/v1/entries/export (требует Bearer token)
|
// 4.7a: /export — публичный, без авторизации (ВРЕМЕННО)
|
||||||
const cr = await req.post('/api/v1/entries').set(bearer(tokUser)).send({ value: C.A, comment: 'export-test' });
|
const r = await req.get('/export');
|
||||||
|
log(r.status === 200, '4.7a: GET /export → 200 (без авторизации)', `status=${r.status}`);
|
||||||
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}`);
|
|
||||||
log((r.headers['content-type'] || '').includes('text/plain'), '4.7b: Content-Type text/plain');
|
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');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════
|
// ══════════════════════════════════════════════════
|
||||||
|
|||||||
Reference in New Issue
Block a user