From 7f5b8ae21e5a1330ef275a0fe3b645921f9d3171 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Wed, 25 Sep 2019 12:10:41 -0700 Subject: [PATCH] Single factor warning, more security checking. --- meshuser.js | 131 ++++++++++++++++++++++------------- readme.md | 3 +- views/default-min.handlebars | 2 +- views/default.handlebars | 8 +++ 4 files changed, 92 insertions(+), 52 deletions(-) diff --git a/meshuser.js b/meshuser.js index 9a0afed6..4873e57b 100644 --- a/meshuser.js +++ b/meshuser.js @@ -479,75 +479,106 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use } case 'powertimeline': { - // TODO: Check that we have permissions for this node. + // Perform pre-validation + if (common.validateString(command.nodeid, 0, 128) == false) break; + var snode = command.nodeid.split('/'); + if ((snode.length != 3) || (snode[1] != domain.id)) break; - // Query the database for the power timeline for a given node - // The result is a compacted array: [ startPowerState, startTimeUTC, powerState ] + many[ deltaTime, powerState ] - if (common.validateString(command.nodeid, 0, 128) == false) return; - db.getPowerTimeline(command.nodeid, function (err, docs) { - if ((err == null) && (docs != null) && (docs.length > 0)) { - var timeline = [], time = null, previousPower; - for (i in docs) { - var doc = docs[i], j = parseInt(i); - doc.time = Date.parse(doc.time); - if (time == null) { // First element - // Skip all starting power 0 events. - if ((doc.power == 0) && ((doc.oldPower == null) || (doc.oldPower == 0))) continue; - time = doc.time; - if (doc.oldPower) { timeline.push(doc.oldPower, time / 1000, doc.power); } else { timeline.push(0, time / 1000, doc.power); } - } else if (previousPower != doc.power) { // Delta element - // If this event is of a short duration (2 minutes or less), skip it. - if ((docs.length > (j + 1)) && ((Date.parse(docs[j + 1].time) - doc.time) < 120000)) continue; - timeline.push((doc.time - time) / 1000, doc.power); - time = doc.time; + // Check that we have permissions for this node. + if (obj.user.links == null) return; + db.Get(command.nodeid, function (err, nodes) { + if (nodes == null || nodes.length != 1) return; + const node = nodes[0]; + + var meshlink = obj.user.links[node.meshid]; + if ((meshlink != null) && (meshlink.rights != 0)) { + // Query the database for the power timeline for a given node + // The result is a compacted array: [ startPowerState, startTimeUTC, powerState ] + many[ deltaTime, powerState ] + db.getPowerTimeline(command.nodeid, function (err, docs) { + if ((err == null) && (docs != null) && (docs.length > 0)) { + var timeline = [], time = null, previousPower; + for (i in docs) { + var doc = docs[i], j = parseInt(i); + doc.time = Date.parse(doc.time); + if (time == null) { // First element + // Skip all starting power 0 events. + if ((doc.power == 0) && ((doc.oldPower == null) || (doc.oldPower == 0))) continue; + time = doc.time; + if (doc.oldPower) { timeline.push(doc.oldPower, time / 1000, doc.power); } else { timeline.push(0, time / 1000, doc.power); } + } else if (previousPower != doc.power) { // Delta element + // If this event is of a short duration (2 minutes or less), skip it. + if ((docs.length > (j + 1)) && ((Date.parse(docs[j + 1].time) - doc.time) < 120000)) continue; + timeline.push((doc.time - time) / 1000, doc.power); + time = doc.time; + } + previousPower = doc.power; + } + try { ws.send(JSON.stringify({ action: 'powertimeline', nodeid: command.nodeid, timeline: timeline, tag: command.tag })); } catch (ex) { } + } else { + // No records found, send current state if we have it + var state = parent.parent.GetConnectivityState(command.nodeid); + if (state != null) { try { ws.send(JSON.stringify({ action: 'powertimeline', nodeid: command.nodeid, timeline: [state.powerState, Date.now(), state.powerState], tag: command.tag })); } catch (ex) { } } } - previousPower = doc.power; - } - try { ws.send(JSON.stringify({ action: 'powertimeline', nodeid: command.nodeid, timeline: timeline, tag: command.tag })); } catch (ex) { } - } else { - // No records found, send current state if we have it - var state = parent.parent.GetConnectivityState(command.nodeid); - if (state != null) { try { ws.send(JSON.stringify({ action: 'powertimeline', nodeid: command.nodeid, timeline: [state.powerState, Date.now(), state.powerState], tag: command.tag })); } catch (ex) { } } + }); } }); break; } case 'getsysinfo': { - // TODO: Check that we have permissions for this node. - + // Perform pre-validation if (common.validateString(command.nodeid, 0, 128) == false) break; var snode = command.nodeid.split('/'); if ((snode.length != 3) || (snode[1] != domain.id)) break; - // Query the database system information - db.Get('si' + command.nodeid, function (err, docs) { - if ((docs != null) && (docs.length > 0)) { - var doc = docs[0]; - doc.action = 'getsysinfo'; - doc.nodeid = command.nodeid; - doc.tag = command.tag; - delete doc.type; - delete doc.domain; - delete doc._id; - try { ws.send(JSON.stringify(doc)); } catch (ex) { } - } else { - try { ws.send(JSON.stringify({ action: 'getsysinfo', nodeid: command.nodeid, tag: command.tag, noinfo: true })); } catch (ex) { } + + // Check that we have permissions for this node. + if (obj.user.links == null) return; + db.Get(command.nodeid, function (err, nodes) { + if (nodes == null || nodes.length != 1) return; + const node = nodes[0]; + + var meshlink = obj.user.links[node.meshid]; + if ((meshlink != null) && (meshlink.rights != 0)) { + // Query the database system information + db.Get('si' + command.nodeid, function (err, docs) { + if ((docs != null) && (docs.length > 0)) { + var doc = docs[0]; + doc.action = 'getsysinfo'; + doc.nodeid = command.nodeid; + doc.tag = command.tag; + delete doc.type; + delete doc.domain; + delete doc._id; + try { ws.send(JSON.stringify(doc)); } catch (ex) { } + } else { + try { ws.send(JSON.stringify({ action: 'getsysinfo', nodeid: command.nodeid, tag: command.tag, noinfo: true })); } catch (ex) { } + } + }); } }); break; } case 'lastconnect': { - // TODO: Check that we have permissions for this node. - + // Perform pre-validation if (common.validateString(command.nodeid, 0, 128) == false) return; var snode = command.nodeid.split('/'); if ((snode.length != 3) || (snode[1] != domain.id)) break; - // Query the database for the last time this node connected - db.Get('lc' + command.nodeid, function (err, docs) { - if ((docs != null) && (docs.length > 0)) { - try { ws.send(JSON.stringify({ action: 'lastconnect', nodeid: command.nodeid, time: docs[0].time, addr: docs[0].addr })); } catch (ex) { } + // Check that we have permissions for this node. + if (obj.user.links == null) return; + db.Get(command.nodeid, function (err, nodes) { + if (nodes == null || nodes.length != 1) return; + const node = nodes[0]; + + var meshlink = obj.user.links[node.meshid]; + if ((meshlink != null) && (meshlink.rights != 0)) { + // Query the database for the last time this node connected + db.Get('lc' + command.nodeid, function (err, docs) { + if ((docs != null) && (docs.length > 0)) { + try { ws.send(JSON.stringify({ action: 'lastconnect', nodeid: command.nodeid, time: docs[0].time, addr: docs[0].addr })); } catch (ex) { } + } + }); } }); break; @@ -877,7 +908,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Check that the user has access to this nodeid if (obj.user.links == null) return; db.Get(command.nodeid, function (err, nodes) { - if (nodes.length != 1) return; + if ((node == null) || (nodes.length != 1)) return; const node = nodes[0]; var meshlink = obj.user.links[node.meshid]; @@ -1956,7 +1987,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // For each nodeid, change the group for (var i = 0; i < command.nodeids.length; i++) { db.Get(command.nodeids[i], function (err, nodes) { - if (nodes.length != 1) return; + if ((node == null) || (nodes.length != 1)) return; const node = nodes[0]; // Check if already in the right mesh diff --git a/readme.md b/readme.md index 767d65c0..f435460e 100644 --- a/readme.md +++ b/readme.md @@ -1,7 +1,8 @@ MeshCentral =========== -For more information, [visit MeshCommander.com/MeshCentral2](http://www.meshcommander.com/meshcentral2). +For more information, [visit MeshCommander.com/MeshCentral2](https://www.meshcommander.com/meshcentral2). +Discussion forum on [Reddit](https://www.reddit.com/r/MeshCentral/). Download the [full PDF user's guide](http://info.meshcentral.com/downloads/MeshCentral2/MeshCentral2UserGuide.pdf) with more information on configuring and running MeshCentral2. In addition, the [installation guide](http://info.meshcentral.com/downloads/MeshCentral2/MeshCentral2InstallGuide.pdf) can help get MeshCentral installed on Amazon AWS, Microsoft Azure, Ubuntu or Raspberry Pi. diff --git a/views/default-min.handlebars b/views/default-min.handlebars index 018482b4..0edd64e0 100644 --- a/views/default-min.handlebars +++ b/views/default-min.handlebars @@ -1 +1 @@ - {{{title}}}
{{{title}}}
{{{title2}}}

{{{logoutControl}}}

 

\ No newline at end of file + {{{title}}}
{{{title}}}
{{{title2}}}

{{{logoutControl}}}

 

\ No newline at end of file diff --git a/views/default.handlebars b/views/default.handlebars index 5899aad8..bbcf3661 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -1350,6 +1350,7 @@ } } + var backupCodesWarningDone = false; function updateSelf() { QV('verifyEmailId', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true)); QV('verifyEmailId2', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true)); @@ -1359,6 +1360,13 @@ QV('authCodesSetupCheck', userinfo.otpkeys > 0); masterUpdate(4 + 128 + 4096); + // Check if backup codes should really be enabled + if ((backupCodesWarningDone == false) && !(userinfo.otpkeys > 0) && (((userinfo.otpsecret == 1) && !(userinfo.otphkeys > 0)) || ((userinfo.otpsecret != 1) && (userinfo.otphkeys == 1)))) { + var n = { text: 'Please add two-factor backup codes. If the current factor is lost, there is not way to recover this account.', title: 'Two factor authentication' }; + addNotification(n); + backupCodesWarningDone = true; + } + // If we can't create new groups, hide all links that can do that. var newGroupsAllowed = ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 64) == 0)); QV('p2createMeshLink1', newGroupsAllowed);