fix(perm): bump token_version on permission change (invalidates JWTs)

setPermission / setUserPermission now bump token_version for affected
users so cached JWTs lose access immediately instead of after expiry.
Aligns with role-change pattern in adminController.updateRole.
Both writes wrapped in db.transaction() so token_version is only bumped
if the permission write itself succeeds.
Also cleaned up inline require('../db/db') calls to use top-level db.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-05-17 14:15:51 +03:00
parent bd7a9dbee2
commit dd1adc0c69
@@ -203,9 +203,15 @@ 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.prepare(
'INSERT OR REPLACE INTO role_permissions (role, permission, enabled) VALUES (?, ?, ?)'
).run(role, permission, enabled ? 1 : 0);
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);
})();
res.json({ ok: true });
}
@@ -239,19 +245,19 @@ function getMyPermissions(req, res) {
/* ── GET /api/permissions/users/:id ──────────────────────────────────── */
function getUserPermissions(req, res) {
const uid = Number(req.params.id);
const target = require('../db/db').prepare('SELECT id, role FROM users WHERE id = ?').get(uid);
const target = db.prepare('SELECT id, role FROM users WHERE id = ?').get(uid);
if (!target) return res.status(404).json({ error: 'User not found' });
seedDefaults();
// role-level values
const roleRows = require('../db/db').prepare(
const roleRows = db.prepare(
'SELECT permission, enabled FROM role_permissions WHERE role = ?'
).all(target.role);
const roleMap = {};
for (const r of roleRows) roleMap[r.permission] = r.enabled === 1;
// user-level overrides
const userRows = require('../db/db').prepare(
const userRows = db.prepare(
'SELECT permission, enabled FROM user_permissions WHERE user_id = ?'
).all(uid);
const userMap = {};
@@ -274,13 +280,19 @@ function getUserPermissions(req, res) {
function setUserPermission(req, res) {
const uid = Number(req.params.id);
const { permission, enabled } = req.body;
const target = require('../db/db').prepare('SELECT role FROM users WHERE id = ?').get(uid);
const target = db.prepare('SELECT role FROM users WHERE id = ?').get(uid);
if (!target) return res.status(404).json({ error: 'User not found' });
if (!ALL_PERMISSIONS.find(p => p.key === permission && p.role === target.role))
return res.status(400).json({ error: 'Unknown permission for this role' });
require('../db/db').prepare(
'INSERT OR REPLACE INTO user_permissions (user_id, permission, enabled) VALUES (?, ?, ?)'
).run(uid, permission, enabled ? 1 : 0);
db.transaction(() => {
db.prepare(
'INSERT OR REPLACE INTO user_permissions (user_id, permission, enabled) VALUES (?, ?, ?)'
).run(uid, permission, enabled ? 1 : 0);
// Invalidate existing JWT for this user immediately
db.prepare(
'UPDATE users SET token_version = token_version + 1 WHERE id = ?'
).run(uid);
})();
res.json({ ok: true });
}
@@ -289,11 +301,11 @@ function resetUserPermissions(req, res) {
const uid = Number(req.params.id);
const { permission } = req.body; // optional: reset one key
if (permission) {
require('../db/db').prepare(
db.prepare(
'DELETE FROM user_permissions WHERE user_id = ? AND permission = ?'
).run(uid, permission);
} else {
require('../db/db').prepare('DELETE FROM user_permissions WHERE user_id = ?').run(uid);
db.prepare('DELETE FROM user_permissions WHERE user_id = ?').run(uid);
}
res.json({ ok: true });
}