fix(features): админ-оверрайд в requireFeature — API отключённого модуля 404-ил админа

Симптом: collection выключен, админ открывал страницу (фронтовый админ-оверрайд),
но GET /api/collection отдавал 404 — requireFeature 404-ил всех. requireFeature
идёт ДО authMiddleware (req.user нет), поэтому сам декодирую Bearer-токен: если
роль admin — пропускаем к API даже выключенного модуля. Для student/teacher всё
по-прежнему 404 (модуль скрыт). Зеркалит фронтовый _isAdminUser. Чинит ВСЕ
отключённые модули для админа, не только коллекцию.

Проверено: admin→bypass, student/teacher/нет токена/мусор/чужой секрет→404 (6/6).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-06-24 21:41:25 +03:00
parent f37796f07b
commit 81bf5d75eb
+14 -1
View File
@@ -29,6 +29,19 @@
* if (feats.exam9 === false) { ... }
*/
const db = require('../db/db');
const jwt = require('jsonwebtoken');
// Админ-оверрайд: requireFeature идёт ДО authMiddleware (req.user ещё нет),
// поэтому декодируем Bearer-токен сами — админ открывает и отключённые модули
// (зеркалит фронтовый _isAdminUser, см. project_gamification_killswitch).
function _isAdminReq(req) {
try {
const h = req.headers.authorization || '';
if (!h.startsWith('Bearer ')) return false;
const p = jwt.verify(h.slice(7), process.env.JWT_SECRET, { algorithms: ['HS256'] });
return !!(p && p.role === 'admin');
} catch (e) { return false; }
}
const _stmtSingle = db.prepare("SELECT value FROM app_settings WHERE key = ?");
const _stmtGlobalFeats = db.prepare("SELECT key, value FROM app_settings WHERE key LIKE 'feature_%'");
@@ -41,7 +54,7 @@ function requireFeature(name) {
const settingKey = `feature_${name}_enabled`;
return (req, res, next) => {
const row = _stmtSingle.get(settingKey);
if (row && row.value === '0') {
if (row && row.value === '0' && !_isAdminReq(req)) { // админ проходит к API даже выключенного модуля
return res.status(404).json({ error: 'Feature disabled' });
}
next();