diff --git a/backend/src/controllers/permissionsController.js b/backend/src/controllers/permissionsController.js index d932449..5a5ef61 100644 --- a/backend/src/controllers/permissionsController.js +++ b/backend/src/controllers/permissionsController.js @@ -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 }); } diff --git a/backend/tests/permissions.test.js b/backend/tests/permissions.test.js index 5442c18..6fc2160 100644 --- a/backend/tests/permissions.test.js +++ b/backend/tests/permissions.test.js @@ -41,8 +41,8 @@ describe('Permissions', () => { assert.equal(res.status, 401, `expected 401, got ${res.status}`); }); - // ── 3. Admin can toggle role-level permission + token_version bumped ─────── - it('admin toggles role permission and student token_version is bumped', async () => { + // ── 3. Role-level toggle сохраняется и НЕ инвалидирует сессии (live enforcement) ─ + it('admin toggles role permission; token_version НЕ бампается (нет массового разлогина)', async () => { const tvBefore = db.prepare('SELECT token_version FROM users WHERE id = ?') .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.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 = ?') .get(studentUser.userId).token_version; - assert.ok(tvAfter > tvBefore, `token_version should increase (was ${tvBefore}, got ${tvAfter})`); + assert.equal(tvAfter, tvBefore, 'role-level изменение НЕ должно разлогинивать пользователей роли'); // Restore await inject('POST', '/api/permissions', {