feat(permissions): A1 — зависимости между правами (requires) + план переработки
registry: поле requires (questions.delete→manage, templates.public→manage, courses.interactive→manage, simulations.quiz→access), проброшено в byRole. auth.requirePermission: вынесен isEnabled(); право = own AND все requires (дочернее не работает без родителя). /me и /users/🆔 effective с учётом requires + requires в ответе. UI permissions.js: каскад — дочернее с невыполненной зависимостью неактивно (тумблер заблокирован + «Требует: …»). Тест зависимости. План: plans/permissions-rework/PLAN.md. Backend 216 pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -42,7 +42,19 @@ function requireRole(...roles) {
|
||||
};
|
||||
}
|
||||
|
||||
/* ── Check permission; user override → role override → hardcoded default ── */
|
||||
/* ── Разрешено ли ОДНО право: user override → role override → дефолт реестра ── */
|
||||
function isEnabled(uid, role, key) {
|
||||
const userRow = db.prepare(
|
||||
'SELECT enabled FROM user_permissions WHERE user_id = ? AND permission = ?'
|
||||
).get(uid, key);
|
||||
if (userRow !== undefined) return userRow.enabled === 1;
|
||||
const roleRow = db.prepare(
|
||||
'SELECT enabled FROM role_permissions WHERE role = ? AND permission = ?'
|
||||
).get(role, key);
|
||||
return roleRow !== undefined ? roleRow.enabled === 1 : (PERM_DEFAULTS[role]?.[key] ?? false);
|
||||
}
|
||||
|
||||
/* ── Проверка права с учётом зависимостей (requires): own AND все requires ── */
|
||||
function requirePermission(key) {
|
||||
return (req, res, next) => {
|
||||
if (req.user?.role === 'admin') return next();
|
||||
@@ -50,22 +62,9 @@ function requirePermission(key) {
|
||||
const uid = req.user?.id;
|
||||
if (!role) return res.status(401).json({ error: 'Unauthorized' });
|
||||
|
||||
// 1. User-level override
|
||||
const userRow = db.prepare(
|
||||
'SELECT enabled FROM user_permissions WHERE user_id = ? AND permission = ?'
|
||||
).get(uid, key);
|
||||
if (userRow !== undefined) {
|
||||
if (userRow.enabled === 1) return next();
|
||||
logDenied(req, 'perm_denied', key);
|
||||
return res.status(403).json({ error: 'Permission denied' });
|
||||
}
|
||||
|
||||
// 2. Role-level
|
||||
const roleRow = db.prepare(
|
||||
'SELECT enabled FROM role_permissions WHERE role = ? AND permission = ?'
|
||||
).get(role, key);
|
||||
const enabled = roleRow !== undefined ? roleRow.enabled === 1 : (PERM_DEFAULTS[role]?.[key] ?? false);
|
||||
if (enabled) return next();
|
||||
const reqs = (registry.PERMISSIONS[key] && registry.PERMISSIONS[key].requires) || [];
|
||||
const ok = isEnabled(uid, role, key) && reqs.every(r => isEnabled(uid, role, r));
|
||||
if (ok) return next();
|
||||
logDenied(req, 'perm_denied', key);
|
||||
return res.status(403).json({ error: 'Permission denied' });
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user