Release v2.17.0: add read-only user previews and access fallbacks
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile, web, pulse-signage-web) (push) Successful in 1m47s
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile.player, player, pulse-signage-player) (push) Successful in 32s

This commit is contained in:
lzstealth committed 2026-10-23 01:55:33 +01:00
1 parent 11dd801f6d
commit 9c28eca3ef
52 files changed
+1624 -140

No files matched your search

+77
View File
@@ -0,0 +1,77 @@
// Read-only user preview controls.
const { hasPermission } = require('#src/rbac');
module.exports = function registerViewAsRoutes(app, deps) {
const pool = deps.pool;
const requirePermission = deps.requirePermission;
const startViewAsUser = deps.startViewAsUser;
const stopViewAsUser = deps.stopViewAsUser;
const recordRequestAuditEvent = deps.recordRequestAuditEvent;
app.post('/settings/users/:id/view-as', requirePermission('users.read'), requirePermission('impersonate.allow'), async function (req, res, next) {
try {
const actor = req.authenticatedUser || req.currentUser && req.currentUser.authenticatedUser || req.currentUser;
const targetUserId = Number(req.params.id);
if (!actor || !hasPermission(actor, 'impersonate.allow') || req.currentUser && req.currentUser.isViewingAs) {
return res.sendStatus(403);
}
if (!Number.isInteger(targetUserId) || targetUserId <= 0 || targetUserId === Number(actor.id)) {
return res.status(400).send('Choose another user to preview.');
}
const [rows] = await pool.query('SELECT id, name, username FROM a_users WHERE id = ? LIMIT 1', [targetUserId]);
if (!rows.length) {
return res.sendStatus(404);
}
const target = rows[0];
const started = await startViewAsUser(pool, req, actor.id, targetUserId);
if (!started) {
return res.status(409).send('The preview could not be started. Sign in again and retry.');
}
if (typeof recordRequestAuditEvent === 'function') {
await recordRequestAuditEvent(pool, req, {
category: 'users',
eventType: 'user.view_as_started',
actorUserId: actor.id,
targetType: 'user',
targetId: targetUserId,
targetLabel: target.username,
details: { expiresInMinutes: 15 }
});
}
res.redirect('/dashboard');
} catch (error) {
next(error);
}
});
app.post('/settings/view-as/stop', async function (req, res, next) {
try {
const actor = req.authenticatedUser || req.currentUser && req.currentUser.authenticatedUser;
if (!actor || !req.currentUser || !req.currentUser.isViewingAs) {
return res.redirect('/dashboard');
}
const target = req.currentUser;
const stopped = await stopViewAsUser(pool, req, actor.id);
if (!stopped) {
return res.status(409).send('The preview has already ended. Refresh the page to continue.');
}
if (typeof recordRequestAuditEvent === 'function') {
await recordRequestAuditEvent(pool, req, {
category: 'users',
eventType: 'user.view_as_stopped',
actorUserId: actor.id,
targetType: 'user',
targetId: target.id,
targetLabel: target.username
});
}
res.redirect('/settings/users');
} catch (error) {
next(error);
}
});
};