Added per-domain configuration of the MyServer action tab.
Ylian Saint-Hilaire committed
Sep 28, 2020 at 13:28 UTC
00ea208f0eeb3d0ee617380c806afbbb2c9ecd3b
6 files changed
+91
-18
meshcentral-config-schema.json
+17
-1
@@ -170,7 +170,23 @@
170
"welcomePicture": { "type": "string", "description": "Name of the PNG or JPEG file that will be shown on the login screen. Put this file in the meshcentral-data folder and place the file name here." },
171
"hide": { "type": "integer" },
172
"footer": { "type": "string" },
173
- "certUrl": { "type": "string", "format": "uri", "description": "https url when to get the TLS certificate that MeshAgent's will see when connecting to this server. This setting is used when a reverse proxy like NGINX is used in front of MeshCentral." },
173
+ "certUrl": {
174
+ "type": "string",
175
+ "format": "uri",
176
+ "description": "https url when to get the TLS certificate that MeshAgent's will see when connecting to this server. This setting is used when a reverse proxy like NGINX is used in front of MeshCentral."
177
+ },
178
+ "myServer": {
179
+ "type": "object",
180
+ "additionalProperties": false,
181
+ "properties": {
182
+ "Backup": { "type": "boolean", "default": true, "description": "Allows administrators to backup the server from the My Server tab." },
183
+ "Restore": { "type": "boolean", "default": true, "description": "Allows administrators to restore the server from the My Server tab." },
184
+ "Upgrade": { "type": "boolean", "default": true, "description": "Allows administrators to update the server from the My Server tab." },
185
+ "ShowLog": { "type": "boolean", "default": true, "description": "Allows administrators to see the server crash log the server from the My Server tab." },
186
+ "Console": { "type": "boolean", "default": true, "description": "Allows administrators to access the server console from the My Server tab." },
187
+ "Trace": { "type": "boolean", "default": true, "description": "Allows administrators to access the server trace tab from from the My Server tab." }
188
+ }
189
+ },
190
"passwordRequirements": {
191
"type": "object",
192
"properties": {
meshuser.js
+14
-2
@@ -444,8 +444,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
444
try { ws.send(JSON.stringify({ action: 'userinfo', userinfo: parent.CloneSafeUser(parent.users[user._id]) })); } catch (ex) { }
445
446
if (user.siteadmin === SITERIGHT_ADMIN) {
447
- // Send server tracing information
448
- try { ws.send(JSON.stringify({ action: 'traceinfo', traceSources: parent.parent.debugRemoteSources })); } catch (ex) { }
447
+ // Check if tracing is allowed for this domain
448
+ if ((domain.myserver == null) || (domain.myserver.trace === true)) {
449
+ // Send server tracing information
450
+ try { ws.send(JSON.stringify({ action: 'traceinfo', traceSources: parent.parent.debugRemoteSources })); } catch (ex) { }
451
+ }
452
453
// Send any server warnings if any
454
var serverWarnings = parent.parent.getServerWarnings();
@@ -807,6 +810,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
810
// This is a server console message, only process this if full administrator
811
if (user.siteadmin != SITERIGHT_ADMIN) break;
812
813
+ // Only accept is the console is allowed for this domain
814
+ if ((domain.myserver != null) && (domain.myserver.console !== true)) break;
815
+
816
var r = '';
817
var cmdargs = splitArgs(command.value);
818
if (cmdargs.length == 0) break;
@@ -2600,6 +2606,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2606
{
2607
// Check the server version
2608
if ((user.siteadmin & 16) == 0) break;
2609
+ if ((domain.myserver != null) && (domain.myserver.upgrade !== true)) break;
2610
//parent.parent.getLatestServerVersion(function (currentVersion, latestVersion) { try { ws.send(JSON.stringify({ action: 'serverversion', current: currentVersion, latest: latestVersion })); } catch (ex) { } });
2611
parent.parent.getServerTags(function (tags, err) { try { ws.send(JSON.stringify({ action: 'serverversion', tags: tags })); } catch (ex) { } });
2612
break;
@@ -2608,6 +2615,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2615
{
2616
// Perform server update
2617
if ((user.siteadmin & 16) == 0) break;
2618
+ if ((domain.myserver != null) && (domain.myserver.upgrade !== true)) break;
2619
if ((command.version != null) && (typeof command.version != 'string')) break;
2620
parent.parent.performServerUpdate(command.version);
2621
break;
@@ -2616,6 +2624,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2624
{
2625
// Load the server error log
2626
if ((user.siteadmin & 16) == 0) break;
2627
+ if ((domain.myserver != null) && (domain.myserver.errorlog !== true)) break;
2628
fs.readFile(parent.parent.getConfigFilePath('mesherrors.txt'), 'utf8', function (err, data) { try { ws.send(JSON.stringify({ action: 'servererrors', data: data })); } catch (ex) { } });
2629
break;
2630
}
@@ -4512,6 +4521,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
4521
break;
4522
}
4523
case 'traceinfo': {
4524
+ // Only accept is the tracing is allowed for this domain
4525
+ if ((domain.myserver != null) && (domain.myserver.trace !== true)) break;
4526
+
4527
if ((user.siteadmin === SITERIGHT_ADMIN) && (typeof command.traceSources == 'object')) {
4528
parent.parent.debugRemoteSources = command.traceSources;
4529
parent.parent.DispatchEvent(['*'], obj, { action: 'traceinfo', userid: user._id, username: user.name, traceSources: command.traceSources, nolog: 1, domain: domain.id });
sample-config-advanced.json
+8
@@ -140,6 +140,14 @@
140
"_hide": 4,
141
"_footer": "<a href='https://twitter.com/mytwitter'>Twitter</a>",
142
"_certUrl": "https://192.168.2.106:443/",
143
+ "myServer": {
144
+ "Backup": false,
145
+ "Restore": false,
146
+ "Upgrade": false,
147
+ "ErrorLog": false,
148
+ "Console": false,
149
+ "Trace": false
150
+ },
151
"_passwordRequirements": {
152
"min": 8,
153
"max": 128,
translate/translate.json
+1
-1
@@ -31786,7 +31786,7 @@
31786
"zh-chs": "服务器统计",
31787
"zh-cht": "伺服器統計",
31788
"xloc": [
31789
- "default.handlebars->container->column_l->p6->p6info->6"
31789
+ "default.handlebars->container->column_l->p6->p6info->5"
31790
]
31791
},
31792
{
views/default.handlebars
+10
-9
@@ -474,8 +474,9 @@
474
<div id="p2ServerActionsVersion"><div class="p2AccountActions"><span style="display:none"><strong>✓</strong></span></div><a href=# onclick="return server_showVersionDlg()">Check server version</a></div>
475
<div id="p2ServerActionsErrors"><div class="p2AccountActions"><span style="display:none"><strong>✓</strong></span></div><a href=# onclick="return server_showErrorsDlg()">Show server error log</a></div>
476
</div>
477
+ <br />
478
</div>
478
- <br /><strong>Server Statistics</strong><br /><br />
479
+ <strong>Server Statistics</strong><br /><br />
480
<div id="serverStats">
481
<div id="serverCpuChartView" style="display:none">
482
<div class="chartViewCanvas"><canvas id="serverCpuChart"></canvas></div>
@@ -1809,10 +1810,9 @@
1810
}
1811
1812
function updateSiteAdmin() {
1812
- var noServerBackup = '{{{noServerBackup}}}';
1813
+ var serverFeatures = parseInt('{{{serverfeatures}}}');
1814
var siteRights = userinfo.siteadmin;
1815
var accountSettingsLocked = ((siteRights != 0xFFFFFFFF) && ((siteRights & 1024) != 0));
1815
- if (noServerBackup == 1) { siteRights &= 0xFFFFFFFA; } // If not server backups allowed, remove server backup and restore permissions
1816
1817
// Update account actions
1818
QV('p2AccountSecurity', ((features & 4) == 0) && (serverinfo.domainauth == false) && ((features & 4096) != 0) && (accountSettingsLocked == false)); // Hide Account Security if in single user mode or domain authentication, 2 factor auth not supported.
@@ -1823,12 +1823,13 @@
1823
QV('p2AccountPassActions', ((features & 4) == 0) && (serverinfo.domainauth == false) && (userinfo != null) && (userinfo._id.split('/')[2].startsWith('~') == false)); // Hide Account Actions if in single user mode or domain authentication
1824
//QV('p2AccountImage', ((features & 4) == 0) && (serverinfo.domainauth == false)); // If account actions are not visible, also remove the image on that panel
1825
QV('p2AccountImage', !accountSettingsLocked)
1826
- QV('p2ServerActions', siteRights & 21);
1826
+ QV('p2ServerActions', (siteRights & 21) && ((serverFeatures & 15) != 0));
1827
QV('LeftMenuMyServer', siteRights & 21); // 16 + 4 + 1
1828
QV('MainMenuMyServer', siteRights & 21);
1829
- QV('p2ServerActionsBackup', siteRights & 1);
1830
- QV('p2ServerActionsRestore', siteRights & 4);
1831
- QV('p2ServerActionsVersion', siteRights & 16);
1829
+ QV('p2ServerActionsBackup', (siteRights & 1) && ((serverFeatures & 1) != 0));
1830
+ QV('p2ServerActionsRestore', (siteRights & 4) && ((serverFeatures & 2) != 0));
1831
+ QV('p2ServerActionsVersion', (siteRights & 16) && ((serverFeatures & 4) != 0));
1832
+ QV('p2ServerActionsErrors', (siteRights & 16) && ((serverFeatures & 8) != 0));
1833
QV('MainMenuMyFiles', siteRights & 8);
1834
QV('LeftMenuMyFiles', siteRights & 8);
1835
if (((siteRights & 8) == 0) && (xxcurrentView == 5)) { setDialogMode(0); go(1); }
@@ -1847,8 +1848,8 @@
1848
if (xxcurrentView == 4 || ((xxcurrentView >= 30) && (xxcurrentView < 40))) { setDialogMode(0); go(1); currentUser = null; }
1849
}
1850
meshserver.send({ action: 'events', limit: parseInt(p3limitdropdown.value) });
1850
- QV('ServerConsole', userinfo.siteadmin === 0xFFFFFFFF);
1851
- QV('ServerTrace', userinfo.siteadmin === 0xFFFFFFFF);
1851
+ QV('ServerConsole', (userinfo.siteadmin === 0xFFFFFFFF) && ((serverFeatures & 16) != 0));
1852
+ QV('ServerTrace', (userinfo.siteadmin === 0xFFFFFFFF) && ((serverFeatures & 32) != 0));
1853
if ((xxcurrentView == 115) && (userinfo.siteadmin != 0xFFFFFFFF)) { go(6); }
1854
if ((xxcurrentView == 6) && ((userinfo.siteadmin & 21) == 0)) { go(1); }
1855
webserver.js
+41
-5
@@ -2370,8 +2370,41 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
2370
var customui = '';
2371
if (domain.customui != null) { customui = encodeURIComponent(JSON.stringify(domain.customui)); }
2372
2373
+ // Server features
2374
+ var serverFeatures = 63;
2375
+ if (domain.myserver) {
2376
+ if (domain.myserver.backup !== true) { serverFeatures -= 1; } // Allow server backups
2377
+ if (domain.myserver.restore !== true) { serverFeatures -= 2; } // Allow server restore
2378
+ if (domain.myserver.upgrade !== true) { serverFeatures -= 4; } // Allow server upgrade
2379
+ if (domain.myserver.errorlog !== true) { serverFeatures -= 8; } // Allow show server crash log
2380
+ if (domain.myserver.console !== true) { serverFeatures -= 16; } // Allow server console
2381
+ if (domain.myserver.trace !== true) { serverFeatures -= 32; } // Allow server tracing
2382
+ }
2383
+
2384
// Refresh the session
2374
- render(req, res, getRenderPage('default', req, domain), getRenderArgs({ authCookie: authCookie, authRelayCookie: authRelayCookie, viewmode: viewmode, currentNode: currentNode, logoutControls: encodeURIComponent(JSON.stringify(logoutcontrols)).replace(/'/g, '%27'), domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, customui: customui, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer, webstate: encodeURIComponent(webstate).replace(/'/g, '%27'), amtscanoptions: amtscanoptions, pluginHandler: (parent.pluginHandler == null) ? 'null' : parent.pluginHandler.prepExports() }, req, domain));
2385
+ render(req, res, getRenderPage('default', req, domain), getRenderArgs({
2386
+ authCookie: authCookie,
2387
+ authRelayCookie: authRelayCookie,
2388
+ viewmode: viewmode,
2389
+ currentNode: currentNode,
2390
+ logoutControls: encodeURIComponent(JSON.stringify(logoutcontrols)).replace(/'/g, '%27'),
2391
+ domain: domain.id,
2392
+ debuglevel: parent.debugLevel,
2393
+ serverDnsName: obj.getWebServerName(domain),
2394
+ serverRedirPort: args.redirport,
2395
+ serverPublicPort: httpsPort,
2396
+ serverfeatures: serverFeatures,
2397
+ features: features,
2398
+ sessiontime: args.sessiontime,
2399
+ mpspass: args.mpspass,
2400
+ passRequirements: passRequirements,
2401
+ customui: customui,
2402
+ webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'),
2403
+ footer: (domain.footer == null) ? '' : domain.footer,
2404
+ webstate: encodeURIComponent(webstate).replace(/'/g, '%27'),
2405
+ amtscanoptions: amtscanoptions,
2406
+ pluginHandler: (parent.pluginHandler == null) ? 'null' : parent.pluginHandler.prepExports()
2407
+ }, req, domain));
2408
});
2409
} else {
2410
// Send back the login application
@@ -4016,7 +4049,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
4049
const domain = checkUserIpAddress(req, res);
4050
if (domain == null) { return; }
4051
if ((domain.loginkey != null) && (domain.loginkey.indexOf(req.query.key) == -1)) { res.sendStatus(404); return; } // Check 3FA URL key
4019
- if ((!req.session) || (req.session == null) || (!req.session.userid) || (obj.parent.args.noserverbackup == 1)) { res.sendStatus(401); return; }
4052
+ if ((!req.session) || (req.session == null) || (!req.session.userid)) { res.sendStatus(401); return; }
4053
+ if ((domain.myserver != null) && (domain.myserver.backup !== true)) { res.sendStatus(401); return; }
4054
+
4055
var user = obj.users[req.session.userid];
4056
if ((user == null) || ((user.siteadmin & 1) == 0)) { res.sendStatus(401); return; } // Check if we have server backup rights
4057
@@ -4044,7 +4079,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
4079
const domain = checkUserIpAddress(req, res);
4080
if (domain == null) { return; }
4081
if ((domain.loginkey != null) && (domain.loginkey.indexOf(req.query.key) == -1)) { res.sendStatus(404); return; } // Check 3FA URL key
4047
- if (obj.parent.args.noserverbackup == 1) { res.sendStatus(401); return; }
4082
+ if ((domain.myserver != null) && (domain.myserver.restore !== true)) { res.sendStatus(401); return; }
4083
+
4084
var authUserid = null;
4085
if ((req.session != null) && (typeof req.session.userid == 'string')) { authUserid = req.session.userid; }
4086
const multiparty = require('multiparty');
@@ -4769,8 +4805,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
4805
obj.app.get(url, handleRootRequest);
4806
obj.app.post(url, handleRootPostRequest);
4807
obj.app.get(url + 'refresh.ashx', function (req, res) { res.sendStatus(200); });
4772
- obj.app.get(url + 'backup.zip', handleBackupRequest);
4773
- obj.app.post(url + 'restoreserver.ashx', handleRestoreRequest);
4808
+ if ((domain.myserver == null) || (domain.myserver.backup === true)) { obj.app.get(url + 'backup.zip', handleBackupRequest); }
4809
+ if ((domain.myserver == null) || (domain.myserver.restore === true)) { obj.app.post(url + 'restoreserver.ashx', handleRestoreRequest); }
4810
obj.app.get(url + 'terms', handleTermsRequest);
4811
obj.app.get(url + 'xterm', handleXTermRequest);
4812
obj.app.post(url + 'login', handleLoginRequest);