feat(access): Фаза 0 — целостность правил доступа + подтверждение массового закрытия

- contentAccess.purgeAccessFor(scope,id) — единая точка очистки content_access
  (нет FK). deleteClass и _deleteUserTx переведены на неё (убрано дублирование).
- Админ-UI: confirm() перед «Закрыть у всех / Закрыть весь» (необратимая массовая
  операция больше не срабатывает мгновенно).
- Новый тест content-access.test.js (9/9): allowlist, ученик>класс, наследование
  главой хаба, admin/teacher bypass, allowedRefs/filterTextbooks, purgeAccessFor,
  чистка правил при DELETE класса. Полный backend-набор: 203/206 (3 — baseline Auth).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-06-03 12:39:08 +03:00
parent edb98895df
commit 1bbddc00c8
5 changed files with 126 additions and 4 deletions
+14
View File
@@ -84,6 +84,19 @@ function filterTextbooks(user, rows) {
return rows.filter(r => allow.has(r.slug));
}
/* ── Очистка правил (единая точка; у content_access нет FK) ───────────────
* Вызывать из ВСЕХ путей удаления цели, чтобы не оставлять осиротевших правил:
* purgeAccessFor('class', classId) — при удалении класса
* purgeAccessFor('student', userId) — при удалении пользователя
* Возвращает число удалённых строк. */
const _purgeClass = db.prepare("DELETE FROM content_access WHERE scope = 'class' AND target_id = ?");
const _purgeStudent = db.prepare("DELETE FROM content_access WHERE scope = 'student' AND target_id = ?");
function purgeAccessFor(scope, id) {
if (scope === 'class') return _purgeClass.run(id).changes;
if (scope === 'student') return _purgeStudent.run(id).changes;
return 0;
}
module.exports = {
PRIVILEGED,
textbookAccessKey,
@@ -92,4 +105,5 @@ module.exports = {
canAccessExam,
allowedRefs,
filterTextbooks,
purgeAccessFor,
};