feat(permissions): A4 — убрать role-level token_version bump (нет массового разлогина)

requirePermission читает права из БД на каждый запрос → серверное применение
живое. Прежний bump token_version при role-level изменении разлогинивал ВСЕХ
пользователей роли из-за одного тумблера. Убрали его: изменение применяется
сразу на сервере, клиент подхватит при следующем /permissions/me. User-level
bump оставлен (точечно одному пользователю — целевое обновление, не массовое).
Тест 3 обновлён: role-level НЕ бампает token_version + значение сохраняется.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-06-03 14:17:32 +03:00
parent 7d474b40c0
commit 6bd1532735
2 changed files with 15 additions and 12 deletions
@@ -39,15 +39,13 @@ function setPermission(req, res) {
return res.status(400).json({ error: 'Invalid role' });
if (!ALL_PERMISSIONS.find(p => p.key === permission && p.role === role))
return res.status(400).json({ error: 'Unknown permission' });
db.transaction(() => {
db.prepare(
'INSERT OR REPLACE INTO role_permissions (role, permission, enabled) VALUES (?, ?, ?)'
).run(role, permission, enabled ? 1 : 0);
// Invalidate JWTs for all users of that role so the change takes effect immediately
db.prepare(
'UPDATE users SET token_version = token_version + 1 WHERE role = ?'
).run(role);
})();
// Серверное применение прав — ЖИВОЕ: requirePermission() читает role_permissions
// из БД на каждый запрос (auth.js). Поэтому role-level изменение НЕ инвалидирует
// сессии — раньше bump token_version разлогинивал ВСЕХ пользователей роли из-за
// одного тумблера. Клиент подхватит новые права при следующем /permissions/me.
db.prepare(
'INSERT OR REPLACE INTO role_permissions (role, permission, enabled) VALUES (?, ?, ?)'
).run(role, permission, enabled ? 1 : 0);
audit(req, 'permission.set', `role:${role}/${permission}`, `enabled=${enabled ? 1 : 0}`);
res.json({ ok: true });
}