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:
@@ -39,15 +39,13 @@ function setPermission(req, res) {
|
|||||||
return res.status(400).json({ error: 'Invalid role' });
|
return res.status(400).json({ error: 'Invalid role' });
|
||||||
if (!ALL_PERMISSIONS.find(p => p.key === permission && p.role === role))
|
if (!ALL_PERMISSIONS.find(p => p.key === permission && p.role === role))
|
||||||
return res.status(400).json({ error: 'Unknown permission' });
|
return res.status(400).json({ error: 'Unknown permission' });
|
||||||
db.transaction(() => {
|
// Серверное применение прав — ЖИВОЕ: requirePermission() читает role_permissions
|
||||||
db.prepare(
|
// из БД на каждый запрос (auth.js). Поэтому role-level изменение НЕ инвалидирует
|
||||||
'INSERT OR REPLACE INTO role_permissions (role, permission, enabled) VALUES (?, ?, ?)'
|
// сессии — раньше bump token_version разлогинивал ВСЕХ пользователей роли из-за
|
||||||
).run(role, permission, enabled ? 1 : 0);
|
// одного тумблера. Клиент подхватит новые права при следующем /permissions/me.
|
||||||
// Invalidate JWTs for all users of that role so the change takes effect immediately
|
db.prepare(
|
||||||
db.prepare(
|
'INSERT OR REPLACE INTO role_permissions (role, permission, enabled) VALUES (?, ?, ?)'
|
||||||
'UPDATE users SET token_version = token_version + 1 WHERE role = ?'
|
).run(role, permission, enabled ? 1 : 0);
|
||||||
).run(role);
|
|
||||||
})();
|
|
||||||
audit(req, 'permission.set', `role:${role}/${permission}`, `enabled=${enabled ? 1 : 0}`);
|
audit(req, 'permission.set', `role:${role}/${permission}`, `enabled=${enabled ? 1 : 0}`);
|
||||||
res.json({ ok: true });
|
res.json({ ok: true });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ describe('Permissions', () => {
|
|||||||
assert.equal(res.status, 401, `expected 401, got ${res.status}`);
|
assert.equal(res.status, 401, `expected 401, got ${res.status}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── 3. Admin can toggle role-level permission + token_version bumped ───────
|
// ── 3. Role-level toggle сохраняется и НЕ инвалидирует сессии (live enforcement) ─
|
||||||
it('admin toggles role permission and student token_version is bumped', async () => {
|
it('admin toggles role permission; token_version НЕ бампается (нет массового разлогина)', async () => {
|
||||||
const tvBefore = db.prepare('SELECT token_version FROM users WHERE id = ?')
|
const tvBefore = db.prepare('SELECT token_version FROM users WHERE id = ?')
|
||||||
.get(studentUser.userId).token_version;
|
.get(studentUser.userId).token_version;
|
||||||
|
|
||||||
@@ -52,9 +52,14 @@ describe('Permissions', () => {
|
|||||||
assert.equal(res.status, 200, `expected 200, got ${res.status}: ${JSON.stringify(res.body)}`);
|
assert.equal(res.status, 200, `expected 200, got ${res.status}: ${JSON.stringify(res.body)}`);
|
||||||
assert.equal(res.body.ok, true);
|
assert.equal(res.body.ok, true);
|
||||||
|
|
||||||
|
// Значение сохранено в role_permissions — сервер применяет его живо (requirePermission читает БД).
|
||||||
|
const row = db.prepare('SELECT enabled FROM role_permissions WHERE role = ? AND permission = ?')
|
||||||
|
.get('student', 'tests.free');
|
||||||
|
assert.equal(row.enabled, 0, 'role-level значение сохранено');
|
||||||
|
|
||||||
const tvAfter = db.prepare('SELECT token_version FROM users WHERE id = ?')
|
const tvAfter = db.prepare('SELECT token_version FROM users WHERE id = ?')
|
||||||
.get(studentUser.userId).token_version;
|
.get(studentUser.userId).token_version;
|
||||||
assert.ok(tvAfter > tvBefore, `token_version should increase (was ${tvBefore}, got ${tvAfter})`);
|
assert.equal(tvAfter, tvBefore, 'role-level изменение НЕ должно разлогинивать пользователей роли');
|
||||||
|
|
||||||
// Restore
|
// Restore
|
||||||
await inject('POST', '/api/permissions', {
|
await inject('POST', '/api/permissions', {
|
||||||
|
|||||||
Reference in New Issue
Block a user