mirror of
https://github.com/UNITRONIX/BetterDesk.git
synced 2026-09-10 09:35:39 +00:00
feat(permissions): add server attestation permission and update related UI
- Introduced a new permission for server attestation, allowing users to run and view server performance attestations. - Updated localization files in English and Polish to include descriptions for the new permission. - Modified authentication middleware and various UI components to integrate the server attestation feature, ensuring proper access control and visibility in the sidebar. - Enhanced the server attestation page to handle rendering and error management effectively.
This commit is contained in:
@@ -24,8 +24,9 @@ const (
|
||||
PermUserDelete = "user.delete"
|
||||
|
||||
// Server configuration
|
||||
PermServerConfig = "server.config" // read/write server_config
|
||||
PermServerKeys = "server.keys" // manage API keys
|
||||
PermServerConfig = "server.config" // read/write server_config
|
||||
PermServerKeys = "server.keys" // manage API keys
|
||||
PermServerAttestation = "server.attestation" // run/view server performance attestation
|
||||
|
||||
// Organization permissions
|
||||
PermOrgCreate = "org.create"
|
||||
@@ -61,7 +62,7 @@ var AllPermissions = []string{
|
||||
PermDeviceView, PermDeviceConnect, PermDeviceEdit, PermDeviceDelete,
|
||||
PermDeviceBan, PermDeviceChangeID,
|
||||
PermUserView, PermUserCreate, PermUserEdit, PermUserDelete,
|
||||
PermServerConfig, PermServerKeys,
|
||||
PermServerConfig, PermServerKeys, PermServerAttestation,
|
||||
PermOrgCreate, PermOrgEdit, PermOrgDelete, PermOrgManageUsers, PermOrgManageDevices,
|
||||
PermAuditView, PermMetricsView, PermBlocklistEdit,
|
||||
PermCDAPView, PermCDAPCommand, PermCDAPTerminal, PermCDAPFiles,
|
||||
@@ -88,7 +89,7 @@ var DefaultRolePermissions = map[string]map[string]bool{
|
||||
// Server Admin: infrastructure + monitoring, read-only user visibility.
|
||||
// Cannot create/edit/delete users, cannot manage orgs.
|
||||
RoleServerAdmin: buildPermMap([]string{
|
||||
PermServerConfig, PermServerKeys,
|
||||
PermServerConfig, PermServerKeys, PermServerAttestation,
|
||||
PermBlocklistEdit,
|
||||
PermUserView, // read-only
|
||||
PermDeviceView, // read-only
|
||||
|
||||
@@ -2130,6 +2130,7 @@
|
||||
"perm_user_delete": "Delete users",
|
||||
"perm_server_config": "Server configuration",
|
||||
"perm_server_keys": "Manage API keys",
|
||||
"perm_server_attestation": "Server performance attestation",
|
||||
"perm_org_create": "Create organizations",
|
||||
"perm_org_edit": "Edit organizations",
|
||||
"perm_org_delete": "Delete organizations",
|
||||
@@ -3546,6 +3547,7 @@
|
||||
"starting": "Starting…",
|
||||
"complete": "Benchmark complete",
|
||||
"signal_peers": "Signal peers",
|
||||
"relay_sessions": "Relay sessions"
|
||||
"relay_sessions": "Relay sessions",
|
||||
"page_error": "Failed to load the Server Attestation page"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2131,6 +2131,7 @@
|
||||
"perm_user_delete": "Usuwanie użytkowników",
|
||||
"perm_server_config": "Konfiguracja serwera",
|
||||
"perm_server_keys": "Zarządzanie kluczami API",
|
||||
"perm_server_attestation": "Atest wydajności serwera",
|
||||
"perm_org_create": "Tworzenie organizacji",
|
||||
"perm_org_edit": "Edycja organizacji",
|
||||
"perm_org_delete": "Usuwanie organizacji",
|
||||
@@ -3547,6 +3548,7 @@
|
||||
"starting": "Uruchamianie…",
|
||||
"complete": "Benchmark zakończony",
|
||||
"signal_peers": "Peery signal",
|
||||
"relay_sessions": "Sesje relay"
|
||||
"relay_sessions": "Sesje relay",
|
||||
"page_error": "Nie udało się załadować strony atestu serwera"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ const DEFAULT_ROLE_PERMISSIONS = {
|
||||
|
||||
// Server Admin: infrastructure + read-only users
|
||||
server_admin: new Set([
|
||||
'server.config', 'server.keys',
|
||||
'server.config', 'server.keys', 'server.attestation',
|
||||
'blocklist.edit',
|
||||
'user.view',
|
||||
'device.view',
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
{
|
||||
id: 'server',
|
||||
icon: 'dns',
|
||||
permissions: ['server.config', 'server.keys']
|
||||
permissions: ['server.config', 'server.keys', 'server.attestation']
|
||||
},
|
||||
{
|
||||
id: 'org',
|
||||
|
||||
@@ -9,7 +9,7 @@ const router = express.Router();
|
||||
const { requireAuth, requirePermission } = require('../middleware/auth');
|
||||
const attestation = require('../services/serverAttestation');
|
||||
|
||||
const REQUIRED_PERMISSION = 'server.config';
|
||||
const REQUIRED_PERMISSION = 'server.attestation';
|
||||
const auth = [requireAuth, requirePermission(REQUIRED_PERMISSION)];
|
||||
|
||||
let runPromise = null;
|
||||
@@ -17,15 +17,23 @@ let runPromise = null;
|
||||
// ─── Page ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
router.get('/server-attestation', ...auth, async (req, res) => {
|
||||
const lastResult = await attestation.getLastResult();
|
||||
res.render('server-attestation', {
|
||||
title: req.t('server_attestation.title'),
|
||||
pageStyles: ['server-attestation'],
|
||||
pageScripts: ['server-attestation'],
|
||||
currentPage: 'server-attestation',
|
||||
breadcrumb: [{ label: req.t('server_attestation.title') }],
|
||||
lastResult
|
||||
});
|
||||
try {
|
||||
const lastResult = await attestation.getLastResult();
|
||||
res.render('server-attestation', {
|
||||
title: req.t('server_attestation.title'),
|
||||
pageStyles: ['server-attestation'],
|
||||
pageScripts: ['server-attestation'],
|
||||
currentPage: 'server-attestation',
|
||||
breadcrumb: [{ label: req.t('server_attestation.title') }],
|
||||
lastResult
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('[ServerAttestation] page render failed:', err);
|
||||
res.status(500).render('errors/500', {
|
||||
title: 'Error',
|
||||
message: req.t('server_attestation.page_error') || 'Failed to load Server Attestation page'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ─── Authenticated API ────────────────────────────────────────────────────────
|
||||
|
||||
@@ -48,6 +48,8 @@
|
||||
data-category="server-mgmt" title="<%= _('nav.server_management') || 'Server Management' %>">
|
||||
<span class="material-icons">dns</span>
|
||||
</button>
|
||||
<% } %>
|
||||
<% if (hasPermission('server.attestation')) { %>
|
||||
<button class="sidebar-rail-btn <%= currentPage === 'server-attestation' ? 'active' : '' %>"
|
||||
data-category="server-attest" title="<%= _('nav.server_attestation') || 'Server Attestation' %>">
|
||||
<span class="material-icons">verified</span>
|
||||
@@ -280,7 +282,7 @@
|
||||
<% } %>
|
||||
|
||||
<!-- Server Attestation -->
|
||||
<% if (hasPermission('server.config')) { %>
|
||||
<% if (hasPermission('server.attestation')) { %>
|
||||
<div class="sidebar-flyout-panel" data-panel="server-attest">
|
||||
<a href="/server-attestation" class="sidebar-link <%= currentPage === 'server-attestation' ? 'active' : '' %>">
|
||||
<span class="material-icons">verified</span>
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
<%
|
||||
var saTier = (typeof lastResult !== 'undefined' && lastResult && lastResult.tier)
|
||||
? String(lastResult.tier).toLowerCase() : '';
|
||||
var saTierLabel = saTier
|
||||
? (_('server_attestation.tier_' + saTier) || saTier.toUpperCase())
|
||||
: (_('server_attestation.tier_none') || 'UNRATED');
|
||||
var saTierClass = saTier ? ('bd-attest-tier-' + saTier) : 'bd-attest-tier-none';
|
||||
var saMaxConn = (typeof lastResult !== 'undefined' && lastResult) ? lastResult.maxConnections : null;
|
||||
var saHasResult = typeof lastResult !== 'undefined' && lastResult;
|
||||
var saHeroBadge = ''
|
||||
+ '<div class="bd-attest-badge bd-attest-badge--large ' + saTierClass + '" title="' + (_('server_attestation.badge_tooltip') || 'BetterDesk Server Attestation') + '">'
|
||||
+ '<div class="bd-attest-brand">Better<span class="bd-attest-i">i</span>Desk</div>'
|
||||
+ '<div class="bd-attest-tier">' + saTierLabel + '</div>'
|
||||
+ (saMaxConn ? ('<div class="bd-attest-conn">' + saMaxConn + ' ' + (_('server_attestation.connections_short') || 'conn.') + '</div>') : '')
|
||||
+ '</div>';
|
||||
%>
|
||||
<%- include('layouts/main', {
|
||||
title: _('server_attestation.title'),
|
||||
pageStyles: ['server-attestation'],
|
||||
@@ -27,14 +43,9 @@
|
||||
|
||||
<div class="sa-layout">
|
||||
<div class="sa-hero">
|
||||
<%- include('partials/server-attestation-badge', {
|
||||
tier: (typeof lastResult !== 'undefined' && lastResult && lastResult.tier) ? lastResult.tier : null,
|
||||
size: 'large',
|
||||
showConnections: true,
|
||||
maxConnections: (typeof lastResult !== 'undefined' && lastResult) ? lastResult.maxConnections : null
|
||||
}) %>
|
||||
${saHeroBadge}
|
||||
<div class="sa-hero-meta" id="sa-hero-meta">
|
||||
${typeof lastResult !== 'undefined' && lastResult && lastResult.testedAt
|
||||
${saHasResult && lastResult.testedAt
|
||||
? '<span>' + _('server_attestation.last_test') + ': ' + new Date(lastResult.testedAt).toLocaleString() + '</span>'
|
||||
: '<span class="sa-muted">' + _('server_attestation.no_test_yet') + '</span>'}
|
||||
</div>
|
||||
@@ -49,7 +60,7 @@
|
||||
<span class="material-icons">stop</span>
|
||||
${_('server_attestation.abort')}
|
||||
</button>
|
||||
<button type="button" class="btn btn-ghost" id="sa-download-btn" ${!(typeof lastResult !== 'undefined' && lastResult) ? 'disabled' : ''}>
|
||||
<button type="button" class="btn btn-ghost" id="sa-download-btn" ${!saHasResult ? 'disabled' : ''}>
|
||||
<span class="material-icons">download</span>
|
||||
${_('server_attestation.download_report')}
|
||||
</button>
|
||||
@@ -66,7 +77,7 @@
|
||||
<span class="material-icons">hub</span>
|
||||
<h3>${_('server_attestation.max_connections')}</h3>
|
||||
</div>
|
||||
<div class="sa-stat-value" id="sa-max-conn">${(typeof lastResult !== 'undefined' && lastResult && lastResult.maxConnections) ? lastResult.maxConnections : '—'}</div>
|
||||
<div class="sa-stat-value" id="sa-max-conn">${saHasResult && lastResult.maxConnections ? lastResult.maxConnections : '—'}</div>
|
||||
<div class="sa-stat-detail" id="sa-conn-detail"></div>
|
||||
</div>
|
||||
|
||||
@@ -77,9 +88,9 @@
|
||||
</div>
|
||||
<table class="sa-threshold-table">
|
||||
<tbody>
|
||||
<tr><td>CPU</td><td id="sa-th-cpu">${(typeof lastResult !== 'undefined' && lastResult && lastResult.finalMetrics) ? lastResult.finalMetrics.cpu + '%' : '—'}</td></tr>
|
||||
<tr><td>RAM</td><td id="sa-th-mem">${(typeof lastResult !== 'undefined' && lastResult && lastResult.finalMetrics) ? lastResult.finalMetrics.mem + '%' : '—'}</td></tr>
|
||||
<tr><td>${_('server_attestation.disk')}</td><td id="sa-th-disk">${(typeof lastResult !== 'undefined' && lastResult && lastResult.finalMetrics) ? lastResult.finalMetrics.disk + '%' : '—'}</td></tr>
|
||||
<tr><td>CPU</td><td id="sa-th-cpu">${saHasResult && lastResult.finalMetrics ? lastResult.finalMetrics.cpu + '%' : '—'}</td></tr>
|
||||
<tr><td>RAM</td><td id="sa-th-mem">${saHasResult && lastResult.finalMetrics ? lastResult.finalMetrics.mem + '%' : '—'}</td></tr>
|
||||
<tr><td>${_('server_attestation.disk')}</td><td id="sa-th-disk">${saHasResult && lastResult.finalMetrics ? lastResult.finalMetrics.disk + '%' : '—'}</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user