diff --git a/src/rbac.js b/src/rbac.js
index b824391..67d72ea 100644
--- a/src/rbac.js
+++ b/src/rbac.js
@@ -28,6 +28,7 @@ const PERMISSION_SECTIONS = [
{ key: 'read', name: 'Read', description: 'View the screen list and open screen details.' },
{ key: 'create', name: 'Create', description: 'Create new screens.' },
{ key: 'edit', name: 'Update', description: 'Edit screens and send screen commands.' },
+ { key: 'allow', name: 'Allow', description: 'Use the connected client actions on the screen page.' },
{ key: 'delete', name: 'Delete', description: 'Delete screens.' }
]
},
diff --git a/src/web/public/js/dashboard/dashboard-page.js b/src/web/public/js/dashboard/dashboard-page.js
index 9ee8148..3869476 100644
--- a/src/web/public/js/dashboard/dashboard-page.js
+++ b/src/web/public/js/dashboard/dashboard-page.js
@@ -206,6 +206,28 @@
}
}
+ function syncClientActionCell(row, client, hasActionsColumn) {
+ if (!row || !row.cells) {
+ return;
+ }
+
+ if (!hasActionsColumn) {
+ if (row.cells.length > 6) {
+ row.deleteCell(row.cells.length - 1);
+ }
+ return;
+ }
+
+ var actionCell = row.cells.length > 6 ? row.cells[6] : null;
+ if (!actionCell) {
+ actionCell = row.insertCell(-1);
+ actionCell.setAttribute('data-label', 'Actions');
+ actionCell.className = 'text-end';
+ }
+
+ updateClientActionCell(actionCell, client);
+ }
+
function renderClientRow(client, hasActionsColumn) {
var connectedAt = client.connectedAt ? '
' + escapeHtml(client.connectedAtLabel || formatDashboardDate(client.connectedAt) || client.connectedAt) + '
' + (client.lastSeenAt ? '' + escapeHtml(client.lastSeenAtLabel || formatDashboardDate(client.lastSeenAt) || client.lastSeenAt) + '
' : '') : 'Unknown';
var clientIpValue = normalizeDisplayIp(client.clientIp);
@@ -317,9 +339,7 @@
row.cells[3].innerHTML = clientIp;
row.cells[4].innerHTML = viewport;
row.cells[5].innerHTML = connectedAt;
- if (hasActionsColumn && row.cells.length >= 7) {
- updateClientActionCell(row.cells[6], client);
- }
+ syncClientActionCell(row, client, hasActionsColumn);
}
var referenceNode = tbody.children[index] || null;
diff --git a/src/web/routes/admin-rbac.js b/src/web/routes/admin-rbac.js
index 5588ac0..e228d33 100644
--- a/src/web/routes/admin-rbac.js
+++ b/src/web/routes/admin-rbac.js
@@ -234,7 +234,14 @@ module.exports = function registerAdminRbacRoutes(app, deps) {
return res.status(404).send('Role not found.');
}
- res.send(pages.renderRbacEditPage(viewModel.role, req.query.message ? String(req.query.message) : '', req.currentUser, viewModel.permissionGroups, viewModel.users));
+ const currentUserId = req.currentUser ? Number(req.currentUser.id) : null;
+ const users = Array.isArray(viewModel.users)
+ ? viewModel.users.filter(function (user) {
+ return Number(user && user.id) !== currentUserId;
+ })
+ : [];
+
+ res.send(pages.renderRbacEditPage(viewModel.role, req.query.message ? String(req.query.message) : '', req.currentUser, viewModel.permissionGroups, users));
} catch (error) {
next(error);
}
diff --git a/src/web/routes/admin-screen-commands.js b/src/web/routes/admin-screen-commands.js
index 92fd214..266b642 100644
--- a/src/web/routes/admin-screen-commands.js
+++ b/src/web/routes/admin-screen-commands.js
@@ -6,7 +6,7 @@ module.exports = function registerAdminScreenCommandRoutes(app, deps) {
const withClientNameReservation = deps.withClientNameReservation;
const requirePermission = deps.requirePermission;
- app.post('/admin/screens/:slug/commands', requirePermission('clients.allow'), async function (req, res, next) {
+ app.post('/admin/screens/:slug/commands', requirePermission('screens.allow'), async function (req, res, next) {
try {
const slug = String(req.params.slug || '').trim();
const command = String((req.body && req.body.command) || req.query.command || '').trim().toLowerCase();
diff --git a/src/web/views/clients/list.hbs b/src/web/views/clients/list.hbs
index e9dceae..381d465 100644
--- a/src/web/views/clients/list.hbs
+++ b/src/web/views/clients/list.hbs
@@ -10,7 +10,7 @@
Active connections