diff --git a/agents/meshcore.js b/agents/meshcore.js index 73ab3b43..8552437a 100644 --- a/agents/meshcore.js +++ b/agents/meshcore.js @@ -1919,13 +1919,10 @@ function createMeshCore(agent) { switch (cmd) { case 'help': { // Displays available commands var fin = '', f = '', availcommands = 'version,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,amt,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,sendcaps,openurl,amtreset,amtccm,amtacm,amtdeactivate,amtpolicy,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,scanamt,wallpaper'; - if (process.platform == 'win32') - { - availcommands += ',safemode,wpfhwacceleration'; - } + if (process.platform == 'win32') { availcommands += ',safemode,wpfhwacceleration'; } availcommands = availcommands.split(',').sort(); while (availcommands.length > 0) { - if (f.length > 100) { fin += (f + ',\r\n'); f = ''; } + if (f.length > 90) { fin += (f + ',\r\n'); f = ''; } f += (((f != '') ? ', ' : ' ') + availcommands.shift()); } if (f != '') { fin += f; } diff --git a/interceptor.js b/interceptor.js index c32ad805..73fe7e62 100644 --- a/interceptor.js +++ b/interceptor.js @@ -12,10 +12,10 @@ /*jshint node: true */ /*jshint strict: false */ /*jshint esversion: 6 */ -"use strict"; +'use strict'; -const crypto = require("crypto"); -const common = require("./common.js"); +const crypto = require('crypto'); +const common = require('./common.js'); var HttpInterceptorAuthentications = {}; //var RedirInterceptorAuthentications = {}; @@ -28,8 +28,8 @@ module.exports.CreateHttpInterceptor = function (args) { obj.randomValueHex = function (len) { return crypto.randomBytes(Math.ceil(len / 2)).toString('hex').slice(0, len); }; obj.args = args; - obj.amt = { acc: "", mode: 0, count: 0, error: false }; // mode: 0:Header, 1:LengthBody, 2:ChunkedBody, 3:UntilClose - obj.ws = { acc: "", mode: 0, count: 0, error: false, authCNonce: obj.randomValueHex(10), authCNonceCount: 1 }; + obj.amt = { acc: '', mode: 0, count: 0, error: false }; // mode: 0:Header, 1:LengthBody, 2:ChunkedBody, 3:UntilClose + obj.ws = { acc: '', mode: 0, count: 0, error: false, authCNonce: obj.randomValueHex(10), authCNonceCount: 1 }; obj.blockAmtStorage = false; // Private method @@ -38,7 +38,7 @@ module.exports.CreateHttpInterceptor = function (args) { // Process data coming from Intel AMT obj.processAmtData = function (data) { obj.amt.acc += data; // Add data to accumulator - data = ""; + data = ''; var datalen = 0; do { datalen = data.length; @@ -53,7 +53,7 @@ module.exports.CreateHttpInterceptor = function (args) { if (obj.amt.mode == 0) { // Header Mode // Decode the HTTP header headerend = obj.amt.acc.indexOf('\r\n\r\n'); - if (headerend < 0) return ""; + if (headerend < 0) return ''; var headerlines = obj.amt.acc.substring(0, headerend).split('\r\n'); obj.amt.acc = obj.amt.acc.substring(headerend + 4); obj.amt.directive = headerlines[0].split(' '); @@ -98,7 +98,7 @@ module.exports.CreateHttpInterceptor = function (args) { } else if (obj.amt.mode == 2) { // Chunked Body Mode // Send data one chunk at a time headerend = obj.amt.acc.indexOf('\r\n'); - if (headerend < 0) return ""; + if (headerend < 0) return ''; var chunksize = parseInt(obj.amt.acc.substring(0, headerend), 16); if ((chunksize == 0) && (obj.amt.acc.length >= headerend + 4)) { // Send the ending chunk (NOTE: We do not support trailing headers) @@ -114,16 +114,16 @@ module.exports.CreateHttpInterceptor = function (args) { } } else if (obj.amt.mode == 3) { // Until Close Mode r = obj.amt.acc; - obj.amt.acc = ""; + obj.amt.acc = ''; return r; } - return ""; + return ''; }; // Process data coming from the Browser obj.processBrowserData = function (data) { obj.ws.acc += data; // Add data to accumulator - data = ""; + data = ''; var datalen = 0; do { datalen = data.length; @@ -138,7 +138,7 @@ module.exports.CreateHttpInterceptor = function (args) { if (obj.ws.mode == 0) { // Header Mode // Decode the HTTP header headerend = obj.ws.acc.indexOf('\r\n\r\n'); - if (headerend < 0) return ""; + if (headerend < 0) return ''; var headerlines = obj.ws.acc.substring(0, headerend).split('\r\n'); obj.ws.acc = obj.ws.acc.substring(headerend + 4); obj.ws.directive = headerlines[0].split(' '); @@ -199,7 +199,7 @@ module.exports.CreateHttpInterceptor = function (args) { } else if (obj.amt.mode == 2) { // Chunked Body Mode // Send data one chunk at a time headerend = obj.amt.acc.indexOf('\r\n'); - if (headerend < 0) return ""; + if (headerend < 0) return ''; var chunksize = parseInt(obj.amt.acc.substring(0, headerend), 16); if (isNaN(chunksize)) { // TODO: Check this path // Chunk is not in this batch, move one @@ -226,10 +226,10 @@ module.exports.CreateHttpInterceptor = function (args) { } } else if (obj.ws.mode == 3) { // Until Close Mode r = obj.ws.acc; - obj.ws.acc = ""; + obj.ws.acc = ''; return r; } - return ""; + return ''; }; // Parse authentication values from the HTTP header @@ -249,9 +249,9 @@ module.exports.CreateHttpInterceptor = function (args) { // Compute the MD5 digest hash for a set of values obj.ComputeDigesthash = function (username, password, realm, method, path, qop, nonce, nc, cnonce) { - var ha1 = crypto.createHash('md5').update(username + ":" + realm + ":" + password).digest("hex"); - var ha2 = crypto.createHash('md5').update(method + ":" + path).digest("hex"); - return crypto.createHash('md5').update(ha1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + ha2).digest("hex"); + var ha1 = crypto.createHash('md5').update(username + ':' + realm + ':' + password).digest('hex'); + var ha2 = crypto.createHash('md5').update(method + ':' + path).digest('hex'); + return crypto.createHash('md5').update(ha1 + ':' + nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + ha2).digest('hex'); }; return obj; @@ -266,8 +266,8 @@ module.exports.CreateRedirInterceptor = function (args) { obj.randomValueHex = function (len) { return crypto.randomBytes(Math.ceil(len / 2)).toString('hex').slice(0, len); }; obj.args = args; - obj.amt = { acc: "", mode: 0, count: 0, error: false, direct: false }; - obj.ws = { acc: "", mode: 0, count: 0, error: false, direct: false, authCNonce: obj.randomValueHex(10), authCNonceCount: 1 }; + obj.amt = { acc: '', mode: 0, count: 0, error: false, direct: false }; + obj.ws = { acc: '', mode: 0, count: 0, error: false, direct: false, authCNonce: obj.randomValueHex(10), authCNonceCount: 1 }; obj.RedirectCommands = { StartRedirectionSession: 0x10, StartRedirectionSessionReply: 0x11, EndRedirectionSession: 0x12, AuthenticateSession: 0x13, AuthenticateSessionReply: 0x14 }; obj.StartRedirectionSessionReplyStatus = { SUCCESS: 0, TYPE_UNKNOWN: 1, BUSY: 2, UNSUPPORTED: 3, ERROR: 0xFF }; @@ -280,7 +280,7 @@ module.exports.CreateRedirInterceptor = function (args) { // Process data coming from Intel AMT obj.processAmtData = function (data) { obj.amt.acc += data; // Add data to accumulator - data = ""; + data = ''; var datalen = 0; do { datalen = data.length; data += obj.processAmtDataEx(); } while (datalen != data.length); // Process as much data as possible return data; @@ -298,11 +298,11 @@ module.exports.CreateRedirInterceptor = function (args) { //console.log(obj.amt.acc.charCodeAt(0)); switch (obj.amt.acc.charCodeAt(0)) { case obj.RedirectCommands.StartRedirectionSessionReply: { - if (obj.amt.acc.length < 4) return ""; + if (obj.amt.acc.length < 4) return ''; if (obj.amt.acc.charCodeAt(1) == obj.StartRedirectionSessionReplyStatus.SUCCESS) { - if (obj.amt.acc.length < 13) return ""; + if (obj.amt.acc.length < 13) return ''; var oemlen = obj.amt.acc.charCodeAt(12); - if (obj.amt.acc.length < 13 + oemlen) return ""; + if (obj.amt.acc.length < 13 + oemlen) return ''; r = obj.amt.acc.substring(0, 13 + oemlen); obj.amt.acc = obj.amt.acc.substring(13 + oemlen); return r; @@ -310,9 +310,9 @@ module.exports.CreateRedirInterceptor = function (args) { break; } case obj.RedirectCommands.AuthenticateSessionReply: { - if (obj.amt.acc.length < 9) return ""; + if (obj.amt.acc.length < 9) return ''; var l = common.ReadIntX(obj.amt.acc, 5); - if (obj.amt.acc.length < 9 + l) return ""; + if (obj.amt.acc.length < 9 + l) return ''; var authstatus = obj.amt.acc.charCodeAt(1); var authType = obj.amt.acc.charCodeAt(4); @@ -337,17 +337,17 @@ module.exports.CreateRedirInterceptor = function (args) { } default: { obj.amt.error = true; - return ""; + return ''; } } } - return ""; + return ''; }; // Process data coming from the Browser obj.processBrowserData = function (data) { obj.ws.acc += data; // Add data to accumulator - data = ""; + data = ''; var datalen = 0; do { datalen = data.length; data += obj.processBrowserDataEx(); } while (datalen != data.length); // Process as much data as possible return data; @@ -356,39 +356,39 @@ module.exports.CreateRedirInterceptor = function (args) { // Process data coming from the Browser in the accumulator obj.processBrowserDataEx = function () { var r; - if (obj.ws.acc.length == 0) return ""; + if (obj.ws.acc.length == 0) return ''; if (obj.ws.direct == true) { var data = obj.ws.acc; - obj.ws.acc = ""; + obj.ws.acc = ''; return data; } else { switch (obj.ws.acc.charCodeAt(0)) { case obj.RedirectCommands.StartRedirectionSession: { - if (obj.ws.acc.length < 8) return ""; + if (obj.ws.acc.length < 8) return ''; r = obj.ws.acc.substring(0, 8); obj.ws.acc = obj.ws.acc.substring(8); return r; } case obj.RedirectCommands.EndRedirectionSession: { - if (obj.ws.acc.length < 4) return ""; + if (obj.ws.acc.length < 4) return ''; r = obj.ws.acc.substring(0, 4); obj.ws.acc = obj.ws.acc.substring(4); return r; } case obj.RedirectCommands.AuthenticateSession: { - if (obj.ws.acc.length < 9) return ""; + if (obj.ws.acc.length < 9) return ''; var l = common.ReadIntX(obj.ws.acc, 5); - if (obj.ws.acc.length < 9 + l) return ""; + if (obj.ws.acc.length < 9 + l) return ''; var authType = obj.ws.acc.charCodeAt(4); if (authType == obj.AuthenticationType.DIGEST && obj.args.user && obj.args.pass) { - var authurl = "/RedirectionService"; + var authurl = '/RedirectionService'; if (obj.amt.digestRealm) { // Replace this authentication digest with a server created one // We have everything we need to authenticate var nc = obj.ws.authCNonceCount; obj.ws.authCNonceCount++; - var digest = obj.ComputeDigesthash(obj.args.user, obj.args.pass, obj.amt.digestRealm, "POST", authurl, obj.amt.digestQOP, obj.amt.digestNonce, nc, obj.ws.authCNonce); + var digest = obj.ComputeDigesthash(obj.args.user, obj.args.pass, obj.amt.digestRealm, 'POST', authurl, obj.amt.digestQOP, obj.amt.digestNonce, nc, obj.ws.authCNonce); // Replace this authentication digest with a server created one // We have everything we need to authenticate @@ -434,18 +434,18 @@ module.exports.CreateRedirInterceptor = function (args) { } default: { obj.ws.error = true; - return ""; + return ''; } } } - return ""; + return ''; }; // Compute the MD5 digest hash for a set of values obj.ComputeDigesthash = function (username, password, realm, method, path, qop, nonce, nc, cnonce) { - var ha1 = crypto.createHash('md5').update(username + ":" + realm + ":" + password).digest("hex"); - var ha2 = crypto.createHash('md5').update(method + ":" + path).digest("hex"); - return crypto.createHash('md5').update(ha1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + ha2).digest("hex"); + var ha1 = crypto.createHash('md5').update(username + ':' + realm + ':' + password).digest('hex'); + var ha2 = crypto.createHash('md5').update(method + ':' + path).digest('hex'); + return crypto.createHash('md5').update(ha1 + ':' + nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + ha2).digest('hex'); }; return obj; diff --git a/meshrelay.js b/meshrelay.js index 37356362..ad2fc002 100644 --- a/meshrelay.js +++ b/meshrelay.js @@ -80,7 +80,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie var agent = parent.wsagents[command.nodeid]; if (agent != null) { // Check if we have permission to send a message to that node - rights = user.links[agent.dbMeshKey]; + rights = user.links[agent.dbMeshKey]; // TODO: Need to include user group / node rights mesh = parent.meshes[agent.dbMeshKey]; if ((rights != null) && (mesh != null) || ((rights & 16) != 0)) { // TODO: 16 is console permission, may need more gradular permission checking if (ws.sessionId) { command.sessionid = ws.sessionId; } // Set the session id, required for responses. @@ -98,7 +98,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie var routing = parent.parent.GetRoutingServerId(command.nodeid, 1); // 1 = MeshAgent routing type if (routing != null) { // Check if we have permission to send a message to that node - rights = user.links[routing.meshid]; + rights = user.links[routing.meshid]; // TODO: Need to include user groups / node rights mesh = parent.meshes[routing.meshid]; if (rights != null || ((rights & 16) != 0)) { // TODO: 16 is console permission, may need more gradular permission checking if (ws.sessionId) { command.fromSessionid = ws.sessionId; } // Set the session id, required for responses. diff --git a/meshuser.js b/meshuser.js index ebec84f7..ce863cef 100644 --- a/meshuser.js +++ b/meshuser.js @@ -152,11 +152,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use var agent = parent.wsagents[command.nodeid]; if (agent != null) { // Check if we have permission to send a message to that node - var meshrights = parent.GetMeshRights(user, agent.dbMeshKey); + var meshrights = parent.GetMeshRights(user, agent.dbMeshKey); // TODO: We will need to get the rights for this specific node. var mesh = parent.meshes[agent.dbMeshKey]; if ((mesh != null) && ((meshrights & MESHRIGHT_REMOTECONTROL) || (meshrights & MESHRIGHT_REMOTEVIEWONLY))) { // 8 is remote control permission, 256 is desktop read only command.sessionid = ws.sessionId; // Set the session id, required for responses - command.rights = meshrights; // Add user rights flags to the message + command.rights = meshrights; // Add user rights flags to the message command.consent = mesh.consent; // Add user consent if (typeof domain.userconsentflags == 'number') { command.consent |= domain.userconsentflags; } // Add server required consent flags command.username = user.name; // Add user name @@ -503,101 +503,74 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use } case 'powertimeline': { - // 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; - - // Check that we have permissions for this node. - db.Get(command.nodeid, function (err, nodes) { - if (nodes == null || nodes.length != 1) return; - const node = nodes[0]; - - if (parent.GetMeshRights(user, node.meshid) != 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; + // Get the node and the rights for this node + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) { + if (visible == false) return; + // 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(node._id, 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; } - 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: node._id, 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: node._id, timeline: [state.powerState, Date.now(), state.powerState], tag: command.tag })); } catch (ex) { } } + } + }); }); break; } case 'getsysinfo': { - // 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; - - // Check that we have permissions for this node. - db.Get(command.nodeid, function (err, nodes) { - if (nodes == null || nodes.length != 1) return; - const node = nodes[0]; - - if (parent.GetMeshRights(user, node.meshid) != 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) { } - } - }); - } + // Get the node and the rights for this node + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) { + if (visible == false) return; + // 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 = node._id; + 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: node._id, tag: command.tag, noinfo: true })); } catch (ex) { } + } + }); }); break; } case 'lastconnect': { - // 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; - - // Check that we have permissions for this node. - db.Get(command.nodeid, function (err, nodes) { - if (nodes == null || nodes.length != 1) return; - const node = nodes[0]; - - if (parent.GetMeshRights(user, node.meshid) != 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) { } - } - }); - } + // Get the node and the rights for this node + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) { + if (visible == false) return; + // 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; } @@ -963,31 +936,27 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use try { ws.send(JSON.stringify({ action: 'events', events: docs, user: command.user, tag: command.tag })); } catch (ex) { } }); } - } else if (common.validateString(command.nodeid, 0, 128) == true) { // Device filtered events + } else if (command.nodeid != null) { // Device filtered events // Check that the user has access to this nodeid - db.Get(command.nodeid, function (err, nodes) { - if ((nodes == null) || (nodes.length != 1)) return; - const node = nodes[0]; + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) { + if (node == null) return; - var meshrights = parent.GetMeshRights(user, node.meshid); - if (meshrights != 0) { - // Put a limit on the number of returned entries if present - var limit = 10000; - if (common.validateInt(command.limit, 1, 60000) == true) { limit = command.limit; } + // Put a limit on the number of returned entries if present + var limit = 10000; + if (common.validateInt(command.limit, 1, 60000) == true) { limit = command.limit; } - if ((meshrights & MESHRIGHT_LIMITEVENTS) != 0) { - // Send the list of most recent events for this nodeid that only apply to us, up to 'limit' count - db.GetNodeEventsSelfWithLimit(command.nodeid, domain.id, user._id, limit, function (err, docs) { - if (err != null) return; - try { ws.send(JSON.stringify({ action: 'events', events: docs, nodeid: command.nodeid, tag: command.tag })); } catch (ex) { } - }); - } else { - // Send the list of most recent events for this nodeid, up to 'limit' count - db.GetNodeEventsWithLimit(command.nodeid, domain.id, limit, function (err, docs) { - if (err != null) return; - try { ws.send(JSON.stringify({ action: 'events', events: docs, nodeid: command.nodeid, tag: command.tag })); } catch (ex) { } - }); - } + if ((rights & MESHRIGHT_LIMITEVENTS) != 0) { + // Send the list of most recent events for this nodeid that only apply to us, up to 'limit' count + db.GetNodeEventsSelfWithLimit(node._id, domain.id, user._id, limit, function (err, docs) { + if (err != null) return; + try { ws.send(JSON.stringify({ action: 'events', events: docs, nodeid: node._id, tag: command.tag })); } catch (ex) { } + }); + } else { + // Send the list of most recent events for this nodeid, up to 'limit' count + db.GetNodeEventsWithLimit(node._id, domain.id, limit, function (err, docs) { + if (err != null) return; + try { ws.send(JSON.stringify({ action: 'events', events: docs, nodeid: node._id, tag: command.tag })); } catch (ex) { } + }); } }); } else { @@ -997,7 +966,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // All events var exGroupFilter2 = [], filter = [], filter2 = user.subscriptions; - // Remove MeshID's that we do not have rights to see events for (TODO: user groups) + // Add all meshes for groups this user is part of + // TODO (UserGroups) + + // Remove MeshID's that we do not have rights to see events for for (var link in obj.user.links) { if (((obj.user.links[link].rights & MESHRIGHT_LIMITEVENTS) != 0) && ((obj.user.links[link].rights != 0xFFFFFFFF))) { exGroupFilter2.push(link); } } for (var i in filter2) { if (exGroupFilter2.indexOf(filter2[i]) == -1) { filter.push(filter2[i]); } } @@ -1198,7 +1170,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use } } - // TODO: Remove user groups?? + // TODO (UserGroups): Remove user groups?? db.Remove('ws' + deluser._id); // Remove user web state db.Remove('nt' + deluser._id); // Remove notes for this user @@ -1507,17 +1479,20 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if (common.validateString(command.meshid, 1, 1024) == false) { err = 'Invalid group identifier'; } // Check the meshid else if (command.meshid.indexOf('/') == -1) { command.meshid = 'mesh/' + domain.id + '/' + command.meshid; } if (common.validateInt(command.notify) == false) { err = 'Invalid notification flags'; } - if (parent.GetMeshRights(user, command.meshid) == 0) err = 'Access denied'; + if (parent.IsMeshViewable(user, command.meshid) == false) err = 'Access denied'; } catch (ex) { err = 'Validation exception: ' + ex; } // Handle any errors if (err != null) { if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'changemeshnotify', responseid: command.responseid, result: err })); } catch (ex) { } } break; } // Change the notification (TODO: Add user group support, not sure how to do this here) - if (command.notify == 0) { - delete user.links[command.meshid].notify; - } else { - user.links[command.meshid].notify = command.notify; + // TODO (UserGroups) + if (user.links[command.meshid]) { + if (command.notify == 0) { + delete user.links[command.meshid].notify; + } else { + user.links[command.meshid].notify = command.notify; + } } // Save the user @@ -1674,33 +1649,23 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use } } + // User-to-device chat is not support in LAN-only mode yet. We need the agent to replace the IP address of the server?? + if (args.lanonly == true) { return; } + // Setup a user-to-node session - if (common.validateString(command.nodeid, 1, 2048)) { - if (args.lanonly == true) { return; } // User-to-device chat is not support in LAN-only mode yet. We need the agent to replace the IP address of the server?? + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) { + // Check if this user has rights to do this + if ((rights & MESHRIGHT_CHATNOTIFY) == 0) return; - // Get the device - db.Get(command.nodeid, function (err, nodes) { - if ((nodes == null) || (nodes.length != 1)) return; - var node = nodes[0]; - - // Get the mesh for this device - mesh = parent.meshes[node.meshid]; - if (mesh) { - // Check if this user has rights to do this - if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_CHATNOTIFY) == 0) return; - - // Create the server url - var httpsPort = ((args.aliasport == null) ? args.port : args.aliasport); // Use HTTPS alias port is specified - var xdomain = (domain.dns == null) ? domain.id : ''; - if (xdomain != '') xdomain += "/"; - var url = "http" + (args.notls ? '' : 's') + "://" + parent.getWebServerName(domain) + ":" + httpsPort + "/" + xdomain + "messenger?id=meshmessenger/" + encodeURIComponent(command.nodeid) + "/" + encodeURIComponent(user._id) + "&title=" + encodeURIComponent(user.name); - - // Create the notification message - routeCommandToNode({ "action": "openUrl", "nodeid": command.nodeid, "userid": user._id, "username": user.name, "url": url }); - } - }); - } + // Create the server url + var httpsPort = ((args.aliasport == null) ? args.port : args.aliasport); // Use HTTPS alias port is specified + var xdomain = (domain.dns == null) ? domain.id : ''; + if (xdomain != '') xdomain += "/"; + var url = "http" + (args.notls ? '' : 's') + "://" + parent.getWebServerName(domain) + ":" + httpsPort + "/" + xdomain + "messenger?id=meshmessenger/" + encodeURIComponent(command.nodeid) + "/" + encodeURIComponent(user._id) + "&title=" + encodeURIComponent(user.name); + // Create the notification message + routeCommandToNode({ "action": "openUrl", "nodeid": command.nodeid, "userid": user._id, "username": user.name, "url": url }); + }); break; } case 'serverversion': @@ -2084,20 +2049,17 @@ 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 == null) || (nodes.length != 1)) return; - const node = nodes[0]; - + // Get the node and the rights for this node + parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) { // Check if already in the right mesh - if (node.meshid == command.meshid) return; + if ((node == null) || (node.meshid == command.meshid)) return; // Make sure both source and target mesh are the same type try { if (parent.meshes[node.meshid].mtype != parent.meshes[command.meshid].mtype) return; } catch (e) { return; }; // Make sure that we have rights on both source and destination mesh - const sourceMeshRights = parent.GetMeshRights(user, node.meshid); const targetMeshRights = parent.GetMeshRights(user, command.meshid); - if (((sourceMeshRights & MESHRIGHT_MANAGECOMPUTERS) == 0) || ((targetMeshRights & MESHRIGHT_MANAGECOMPUTERS) == 0)) return; + if (((rights & MESHRIGHT_MANAGECOMPUTERS) == 0) || ((targetMeshRights & MESHRIGHT_MANAGECOMPUTERS) == 0)) return; // Perform the switch, start by saving the node with the new meshid. const oldMeshId = node.meshid; @@ -2139,146 +2101,103 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use case 'removedevices': { if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's - for (i in command.nodeids) { - nodeid = command.nodeids[i]; - if (common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid - if ((nodeid.split('/').length != 3) || (nodeid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain + // Get the node and the rights for this node + parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) { + // Check we have the rights to delete this device + if ((rights & MESHRIGHT_MANAGECOMPUTERS) == 0) return; - // Get the device - db.Get(nodeid, function (err, nodes) { - if ((nodes == null) || (nodes.length != 1)) return; - var node = nodes[0]; + // Delete this node including network interface information, events and timeline + db.Remove(node._id); // Remove node with that id + db.Remove('if' + node._id); // Remove interface information + db.Remove('nt' + node._id); // Remove notes + db.Remove('lc' + node._id); // Remove last connect time + db.Remove('si' + node._id); // Remove system information + db.RemoveSMBIOS(node._id); // Remove SMBios data + db.RemoveAllNodeEvents(node._id); // Remove all events for this node + db.removeAllPowerEventsForNode(node._id); // Remove all power events for this node + db.Get('ra' + obj.dbNodeKey, function (err, nodes) { + if ((nodes != null) && (nodes.length == 1)) { db.Remove('da' + nodes[0].daid); } // Remove diagnostic agent to real agent link + db.Remove('ra' + node._id); // Remove real agent to diagnostic agent link + }); - // Get the mesh for this device - mesh = parent.meshes[node.meshid]; - if (mesh) { - // Check if this user has rights to do this - if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_MANAGECOMPUTERS) == 0) return; + // Event node deletion + var event = { etype: 'node', userid: user._id, username: user.name, action: 'removenode', nodeid: node._id, msg: 'Removed device ' + node.name + ' from group ' + mesh.name, domain: domain.id }; + // TODO: We can't use the changeStream for node delete because we will not know the meshid the device was in. + //if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to remove the node. Another event will come. + parent.parent.DispatchEvent(['*', node.meshid], obj, event); - // Delete this node including network interface information, events and timeline - db.Remove(node._id); // Remove node with that id - db.Remove('if' + node._id); // Remove interface information - db.Remove('nt' + node._id); // Remove notes - db.Remove('lc' + node._id); // Remove last connect time - db.Remove('si' + node._id); // Remove system information - db.RemoveSMBIOS(node._id); // Remove SMBios data - db.RemoveAllNodeEvents(node._id); // Remove all events for this node - db.removeAllPowerEventsForNode(node._id); // Remove all power events for this node - db.Get('ra' + obj.dbNodeKey, function (err, nodes) { - if ((nodes != null) && (nodes.length == 1)) { db.Remove('da' + nodes[0].daid); } // Remove diagnostic agent to real agent link - db.Remove('ra' + node._id); // Remove real agent to diagnostic agent link - }); - - // Event node deletion - var event = { etype: 'node', userid: user._id, username: user.name, action: 'removenode', nodeid: node._id, msg: 'Removed device ' + node.name + ' from group ' + mesh.name, domain: domain.id }; - // TODO: We can't use the changeStream for node delete because we will not know the meshid the device was in. - //if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to remove the node. Another event will come. - parent.parent.DispatchEvent(['*', node.meshid], obj, event); - - // Disconnect all connections if needed - var state = parent.parent.GetConnectivityState(nodeid); - if ((state != null) && (state.connectivity != null)) { - if ((state.connectivity & 1) != 0) { parent.wsagents[nodeid].close(); } // Disconnect mesh agent - if ((state.connectivity & 2) != 0) { parent.parent.mpsserver.close(parent.parent.mpsserver.ciraConnections[nodeid]); } // Disconnect CIRA connection - } + // Disconnect all connections if needed + var state = parent.parent.GetConnectivityState(nodeid); + if ((state != null) && (state.connectivity != null)) { + if ((state.connectivity & 1) != 0) { parent.wsagents[nodeid].close(); } // Disconnect mesh agent + if ((state.connectivity & 2) != 0) { parent.parent.mpsserver.close(parent.parent.mpsserver.ciraConnections[nodeid]); } // Disconnect CIRA connection } }); } - break; } case 'wakedevices': { - if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's // TODO: We can optimize this a lot. // - We should get a full list of all MAC's to wake first. // - We should try to only have one agent per subnet (using Gateway MAC) send a wake-on-lan. + if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's for (i in command.nodeids) { - nodeid = command.nodeids[i]; - var wakeActions = 0; - if (common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid - if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain - // Get the device - db.Get(nodeid, function (err, nodes) { - if ((nodes == null) || (nodes.length != 1)) return; - var node = nodes[0]; + // Get the node and the rights for this node + parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) { + // Check we have the rights to delete this device + if ((rights & MESHRIGHT_WAKEDEVICE) == 0) return; - // Get the mesh for this device - mesh = parent.meshes[node.meshid]; - if (mesh) { + // If this device is connected on MQTT, send a wake action. + if (parent.parent.mqttbroker != null) { parent.parent.mqttbroker.publish(node._id, 'powerAction', 'wake'); } - // Check if this user has rights to do this - if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_WAKEDEVICE) != 0) { + // Get the device interface information + db.Get('if' + node._id, function (err, nodeifs) { + if ((nodeifs != null) && (nodeifs.length == 1)) { + var macs = [], nodeif = nodeifs[0]; + for (var i in nodeif.netif) { if (nodeif.netif[i].mac) { macs.push(nodeif.netif[i].mac); } } - // If this device is connected on MQTT, send a wake action. - if (parent.parent.mqttbroker != null) { parent.parent.mqttbroker.publish(node._id, 'powerAction', 'wake'); } + // Have the server send a wake-on-lan packet (Will not work in WAN-only) + if (parent.parent.meshScanner != null) { parent.parent.meshScanner.wakeOnLan(macs); } - // Get the device interface information - db.Get('if' + node._id, function (err, nodeifs) { - if ((nodeifs != null) && (nodeifs.length == 1)) { - var nodeif = nodeifs[0]; - var macs = []; - for (var i in nodeif.netif) { if (nodeif.netif[i].mac) { macs.push(nodeif.netif[i].mac); } } - - // Have the server send a wake-on-lan packet (Will not work in WAN-only) - if (parent.parent.meshScanner != null) { parent.parent.meshScanner.wakeOnLan(macs); wakeActions++; } - - // Get the list of mesh this user as access to - var targetMeshes = []; - for (i in user.links) { targetMeshes.push(i); } // TODO: Include used security groups!! - - // Go thru all the connected agents and send wake-on-lan on all the ones in the target mesh list - for (i in parent.wsagents) { - var agent = parent.wsagents[i]; - if ((targetMeshes.indexOf(agent.dbMeshKey) >= 0) && (agent.authenticated == 2)) { - //console.log('Asking agent ' + agent.dbNodeKey + ' to wake ' + macs.join(',')); - try { agent.send(JSON.stringify({ action: 'wakeonlan', macs: macs })); } catch (ex) { } - wakeActions++; - } - } - } - }); + // Get the list of mesh this user as access to + var targetMeshes = []; + for (i in user.links) { targetMeshes.push(i); } // TODO: Include used security groups!! + // Go thru all the connected agents and send wake-on-lan on all the ones in the target mesh list + for (i in parent.wsagents) { + var agent = parent.wsagents[i]; + if ((targetMeshes.indexOf(agent.dbMeshKey) >= 0) && (agent.authenticated == 2)) { + //console.log('Asking agent ' + agent.dbNodeKey + ' to wake ' + macs.join(',')); + try { agent.send(JSON.stringify({ action: 'wakeonlan', macs: macs })); } catch (ex) { } + } } } }); - } + }); // Confirm we may be doing something (TODO) try { ws.send(JSON.stringify({ action: 'wakedevices' })); } catch (ex) { } } - break; } case 'uninstallagent': { if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's for (i in command.nodeids) { - nodeid = command.nodeids[i]; - if (common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid - if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain - // Get the device - db.Get(nodeid, function (err, nodes) { - if ((nodes == null) || (nodes.length != 1)) return; - var node = nodes[0]; + // Get the node and the rights for this node + parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) { + // Check we have the rights to delete this device + if ((rights & MESHRIGHT_UNINSTALL) == 0) return; - // Get the mesh for this device - mesh = parent.meshes[node.meshid]; - if (mesh) { - // Check if this user has rights to do this - if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_UNINSTALL) != 0) { - // Send uninstall command to connected agent - var agent = parent.wsagents[node._id]; - if (agent != null) { - //console.log('Asking agent ' + agent.dbNodeKey + ' to uninstall.'); - try { agent.send(JSON.stringify({ action: 'uninstallagent' })); } catch (ex) { } - } - } - } - }); - } + // Send uninstall command to connected agent + const agent = parent.wsagents[node._id]; + if (agent != null) { + //console.log('Asking agent ' + agent.dbNodeKey + ' to uninstall.'); + try { agent.send(JSON.stringify({ action: 'uninstallagent' })); } catch (ex) { } + } + }); } - break; } case 'poweraction': @@ -2286,34 +2205,21 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's if (common.validateInt(command.actiontype, 2, 4) == false) break; // Check actiontype for (i in command.nodeids) { - nodeid = command.nodeids[i]; - var powerActions = 0; - if (common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid - if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain - // Get the device - db.Get(nodeid, function (err, nodes) { - if ((nodes == null) || (nodes.length != 1)) return; - var node = nodes[0]; + // Get the node and the rights for this node + parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) { + // Check we have the rights to delete this device + if ((rights & MESHRIGHT_REMOTECONTROL) == 0) return; - // Get the mesh for this device - mesh = parent.meshes[node.meshid]; - if (mesh) { - // If this device is connected on MQTT, send a power action. - if (parent.parent.mqttbroker != null) { parent.parent.mqttbroker.publish(nodeid, 'powerAction', ['', '', 'poweroff', 'reset', 'sleep'][command.actiontype]); } + // If this device is connected on MQTT, send a power action. + if (parent.parent.mqttbroker != null) { parent.parent.mqttbroker.publish(node._id, 'powerAction', ['', '', 'poweroff', 'reset', 'sleep'][command.actiontype]); } + + // Get this device and send the power command + const agent = parent.wsagents[node._id]; + if (agent != null) { + try { agent.send(JSON.stringify({ action: 'poweraction', actiontype: command.actiontype })); } catch (ex) { } + } + }); - // Check if this user has rights to do this - if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_REMOTECONTROL) != 0) { // "Remote Control permission" - // Get this device - var agent = parent.wsagents[node._id]; - if (agent != null) { - // Send the power command - try { agent.send(JSON.stringify({ action: 'poweraction', actiontype: command.actiontype })); } catch (ex) { } - powerActions++; - } - } - } - }); - } // Confirm we may be doing something (TODO) try { ws.send(JSON.stringify({ action: 'poweraction' })); } catch (ex) { } } @@ -2325,30 +2231,17 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if (common.validateString(command.title, 1, 512) == false) break; // Check title if (common.validateString(command.msg, 1, 4096) == false) break; // Check message for (i in command.nodeids) { - nodeid = command.nodeids[i]; - var powerActions = 0; - if (common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid - if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain - // Get the device - db.Get(nodeid, function (err, nodes) { - if ((nodes == null) || (nodes.length != 1)) return; - var node = nodes[0]; + // Get the node and the rights for this node + parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) { + // Check we have the rights to delete this device + if ((rights & MESHRIGHT_CHATNOTIFY) == 0) return; - // Get the mesh for this device - mesh = parent.meshes[node.meshid]; - if (mesh) { - // Check if this user has rights to do this - if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_CHATNOTIFY) != 0) { - // Get this device - var agent = parent.wsagents[node._id]; - if (agent != null) { - // Send the power command - try { agent.send(JSON.stringify({ action: 'toast', title: command.title, msg: command.msg, sessionid: ws.sessionId, username: user.name, userid: user._id })); } catch (ex) { } - } - } - } - }); - } + // Get this device and send toast command + const agent = parent.wsagents[node._id]; + if (agent != null) { + try { agent.send(JSON.stringify({ action: 'toast', title: command.title, msg: command.msg, sessionid: ws.sessionId, username: user.name, userid: user._id })); } catch (ex) { } + } + }); } break; } @@ -2358,24 +2251,16 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid if ((command.nodeid.split('/').length != 3) || (command.nodeid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain - // Get the device - db.Get(command.nodeid, function (err, nodes) { - if ((nodes == null) || (nodes.length != 1)) { try { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: command.nodeid, netif: null })); } catch (ex) { } return; } - var node = nodes[0]; + // Get the node and the rights for this node + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) { + if (visible == false) return; - // Get the mesh for this device - mesh = parent.meshes[node.meshid]; - if (mesh) { - // Check if this user has rights to do this - if (parent.GetMeshRights(user, mesh) == 0) { try { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: command.nodeid, netif: null })); } catch (ex) { } return; } - - // Get network information about this node - db.Get('if' + command.nodeid, function (err, netinfos) { - if ((netinfos == null) || (netinfos.length != 1)) { try { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: command.nodeid, netif: null })); } catch (ex) { } return; } - var netinfo = netinfos[0]; - try { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: command.nodeid, updateTime: netinfo.updateTime, netif: netinfo.netif })); } catch (ex) { } - }); - } + // Get network information about this node + db.Get('if' + node._id, function (err, netinfos) { + if ((netinfos == null) || (netinfos.length != 1)) { try { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: node._id, netif: null })); } catch (ex) { } return; } + var netinfo = netinfos[0]; + try { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: node._id, updateTime: netinfo.updateTime, netif: netinfo.netif })); } catch (ex) { } + }); }); break; } @@ -2383,106 +2268,89 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use { // Argument validation if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid - if ((command.nodeid.split('/').length != 3) || (command.nodeid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain if ((command.userloc) && (command.userloc.length != 2) && (command.userloc.length != 0)) return; - // Change the device - db.Get(command.nodeid, function (err, nodes) { - if ((nodes == null) || (nodes.length != 1)) return; - var node = nodes[0]; + // Get the node and the rights for this node + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) { + if ((rights & MESHRIGHT_MANAGECOMPUTERS) == 0) return; + var mesh = parent.meshes[node.meshid]; - // Get the mesh for this device - mesh = parent.meshes[node.meshid]; - if (mesh) { - // Check if this user has rights to do this - if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_MANAGECOMPUTERS) == 0) return; + // Ready the node change event + var changes = [], event = { etype: 'node', userid: user._id, username: user.name, action: 'changenode', nodeid: node._id, domain: domain.id }; + change = 0; + event.msg = ': '; - // Ready the node change event - var changes = [], event = { etype: 'node', userid: user._id, username: user.name, action: 'changenode', nodeid: node._id, domain: domain.id }; - change = 0; - event.msg = ": "; + // If we are in WAN-only mode, host is not used + if ((args.wanonly == true) && (command.host)) { delete command.host; } - // If we are in WAN-only mode, host is not used - if ((args.wanonly == true) && (command.host)) { delete command.host; } - - // Look for a change - if (command.icon && (command.icon != node.icon)) { change = 1; node.icon = command.icon; changes.push('icon'); } - if (command.name && (command.name != node.name)) { change = 1; node.name = command.name; changes.push('name'); } - if (command.host && (command.host != node.host)) { change = 1; node.host = command.host; changes.push('host'); } - if (domain.geolocation && command.userloc && ((node.userloc == null) || (command.userloc[0] != node.userloc[0]) || (command.userloc[1] != node.userloc[1]))) { - change = 1; - if ((command.userloc.length == 0) && (node.userloc)) { - delete node.userloc; - changes.push('location removed'); - } else { - command.userloc.push((Math.floor((new Date()) / 1000))); - node.userloc = command.userloc.join(','); - changes.push('location'); - } + // Look for a change + if (command.icon && (command.icon != node.icon)) { change = 1; node.icon = command.icon; changes.push('icon'); } + if (command.name && (command.name != node.name)) { change = 1; node.name = command.name; changes.push('name'); } + if (command.host && (command.host != node.host)) { change = 1; node.host = command.host; changes.push('host'); } + if (domain.geolocation && command.userloc && ((node.userloc == null) || (command.userloc[0] != node.userloc[0]) || (command.userloc[1] != node.userloc[1]))) { + change = 1; + if ((command.userloc.length == 0) && (node.userloc)) { + delete node.userloc; + changes.push('location removed'); + } else { + command.userloc.push((Math.floor((new Date()) / 1000))); + node.userloc = command.userloc.join(','); + changes.push('location'); } - if (command.desc != null && (command.desc != node.desc)) { change = 1; node.desc = command.desc; changes.push('description'); } - if (command.intelamt != null) { - if ((command.intelamt.user != null) && (command.intelamt.pass != undefined) && ((command.intelamt.user != node.intelamt.user) || (command.intelamt.pass != node.intelamt.pass))) { change = 1; node.intelamt.user = command.intelamt.user; node.intelamt.pass = command.intelamt.pass; changes.push('Intel AMT credentials'); } - if (command.intelamt.tls && (command.intelamt.tls != node.intelamt.tls)) { change = 1; node.intelamt.tls = command.intelamt.tls; changes.push('Intel AMT TLS'); } - } - if (command.tags) { // Node grouping tag, this is a array of strings that can't be empty and can't contain a comma - var ok = true, group2 = []; - if (common.validateString(command.tags, 0, 4096) == true) { command.tags = command.tags.split(','); } - for (var i in command.tags) { var tname = command.tags[i].trim(); if ((tname.length > 0) && (tname.length < 64) && (group2.indexOf(tname) == -1)) { group2.push(tname); } } - group2.sort(); - if (node.tags != group2) { node.tags = group2; change = 1; } - } else if ((command.tags === '') && node.tags) { delete node.tags; change = 1; } + } + if (command.desc != null && (command.desc != node.desc)) { change = 1; node.desc = command.desc; changes.push('description'); } + if (command.intelamt != null) { + if ((command.intelamt.user != null) && (command.intelamt.pass != undefined) && ((command.intelamt.user != node.intelamt.user) || (command.intelamt.pass != node.intelamt.pass))) { change = 1; node.intelamt.user = command.intelamt.user; node.intelamt.pass = command.intelamt.pass; changes.push('Intel AMT credentials'); } + if (command.intelamt.tls && (command.intelamt.tls != node.intelamt.tls)) { change = 1; node.intelamt.tls = command.intelamt.tls; changes.push('Intel AMT TLS'); } + } + if (command.tags) { // Node grouping tag, this is a array of strings that can't be empty and can't contain a comma + var ok = true, group2 = []; + if (common.validateString(command.tags, 0, 4096) == true) { command.tags = command.tags.split(','); } + for (var i in command.tags) { var tname = command.tags[i].trim(); if ((tname.length > 0) && (tname.length < 64) && (group2.indexOf(tname) == -1)) { group2.push(tname); } } + group2.sort(); + if (node.tags != group2) { node.tags = group2; change = 1; } + } else if ((command.tags === '') && node.tags) { delete node.tags; change = 1; } - if (change == 1) { - // Save the node - db.Set(node); + if (change == 1) { + // Save the node + db.Set(node); - // Event the node change. Only do this if the database will not do it. - event.msg = 'Changed device ' + node.name + ' from group ' + mesh.name + ': ' + changes.join(', '); - event.node = parent.CloneSafeNode(node); - if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come. - parent.parent.DispatchEvent(['*', node.meshid, user._id], obj, event); - } + // Event the node change. Only do this if the database will not do it. + event.msg = 'Changed device ' + node.name + ' from group ' + mesh.name + ': ' + changes.join(', '); + event.node = parent.CloneSafeNode(node); + if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come. + parent.parent.DispatchEvent(['*', node.meshid, user._id], obj, event); } }); break; } case 'uploadagentcore': { - if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid if (common.validateString(command.type, 1, 40) == false) break; // Check path - // Change the device - db.Get(command.nodeid, function (err, nodes) { - if ((nodes == null) || (nodes.length != 1)) return; - var node = nodes[0]; + // Get the node and the rights for this node + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) { + if ((node == null) || (((rights & MESHRIGHT_AGENTCONSOLE) == 0) && (user.siteadmin != 0xFFFFFFFF))) return; - // Get the mesh for this device - mesh = parent.meshes[node.meshid]; - if (mesh) { - // Check if this user has rights to do this - if (((parent.GetMeshRights(user, mesh) & MESHRIGHT_AGENTCONSOLE) == 0) && (user.siteadmin != 0xFFFFFFFF)) { return; } - - if (command.type == 'default') { - // Send the default core to the agent - parent.parent.updateMeshCore(function () { parent.sendMeshAgentCore(user, domain, command.nodeid, 'default'); }); - } else if (command.type == 'clear') { - // Clear the mesh agent core on the mesh agent - parent.sendMeshAgentCore(user, domain, command.nodeid, 'clear'); - } else if (command.type == 'recovery') { - // Send the recovery core to the agent - parent.sendMeshAgentCore(user, domain, command.nodeid, 'recovery'); - } else if ((command.type == 'custom') && (common.validateString(command.path, 1, 2048) == true)) { - // Send a mesh agent core to the mesh agent - var file = parent.getServerFilePath(user, domain, command.path); - if (file != null) { - fs.readFile(file.fullpath, 'utf8', function (err, data) { - if (err != null) { - data = common.IntToStr(0) + data; // Add the 4 bytes encoding type & flags (Set to 0 for raw) - parent.sendMeshAgentCore(user, domain, command.nodeid, 'custom', data); - } - }); - } + if (command.type == 'default') { + // Send the default core to the agent + parent.parent.updateMeshCore(function () { parent.sendMeshAgentCore(user, domain, node._id, 'default'); }); + } else if (command.type == 'clear') { + // Clear the mesh agent core on the mesh agent + parent.sendMeshAgentCore(user, domain, node._id, 'clear'); + } else if (command.type == 'recovery') { + // Send the recovery core to the agent + parent.sendMeshAgentCore(user, domain, node._id, 'recovery'); + } else if ((command.type == 'custom') && (common.validateString(command.path, 1, 2048) == true)) { + // Send a mesh agent core to the mesh agent + var file = parent.getServerFilePath(user, domain, command.path); + if (file != null) { + fs.readFile(file.fullpath, 'utf8', function (err, data) { + if (err != null) { + data = common.IntToStr(0) + data; // Add the 4 bytes encoding type & flags (Set to 0 for raw) + parent.sendMeshAgentCore(user, domain, node._id, 'custom', data); + } + }); } } }); @@ -2490,23 +2358,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use } case 'agentdisconnect': { - if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid if (common.validateInt(command.disconnectMode) == false) return; // Check disconnect mode - // Change the device - db.Get(command.nodeid, function (err, nodes) { - if ((nodes == null) || (nodes.length != 1)) return; - var node = nodes[0]; + // Get the node and the rights for this node + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) { + if ((node == null) || (((rights & MESHRIGHT_AGENTCONSOLE) == 0) && (user.siteadmin != 0xFFFFFFFF))) return; - // Get the mesh for this device - mesh = parent.meshes[node.meshid]; - if (mesh) { - // Check if this user has rights to do this - if (((parent.GetMeshRights(user, mesh) & MESHRIGHT_AGENTCONSOLE) == 0) && (user.siteadmin != 0xFFFFFFFF)) return; - - // Force mesh agent disconnection - parent.forceMeshAgentDisconnect(user, domain, command.nodeid, command.disconnectMode); - } + // Force mesh agent disconnection + parent.forceMeshAgentDisconnect(user, domain, node._id, command.disconnectMode); }); break; } @@ -2552,7 +2411,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use mesh = parent.meshes[command.meshid]; if (mesh == null) { err = 'Unknown device group'; } // Check if the group exists else if (mesh.mtype != 2) { err = 'Invalid group type'; } // Check if this is the correct group type - else if (parent.GetMeshRights(user, mesh) == 0) { err = 'Not allowed'; } // Check if this user has rights to do this + else if (parent.IsMeshViewable(user, mesh) == false) { err = 'Not allowed'; } // Check if this user has rights to do this } } } catch (ex) { err = 'Validation exception: ' + ex; } @@ -2574,24 +2433,16 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use { // Argument validation if (common.validateString(command.msg, 1, 4096) == false) break; // Check event - if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid - var splitid = command.nodeid.split('/'); - if ((splitid.length != 3) || (splitid[1] != domain.id)) return; // Invalid domain, operation only valid for current domain - var idtype = splitid[0]; - if ((idtype != 'node')) return; - // Check if this user has rights on this id to set notes - db.Get(command.nodeid, function (err, nodes) { - if ((nodes == null) || (nodes.length == 1)) { - if (parent.GetMeshRights(user, nodes[0].meshid) != 0) { - // Add an event for this device - var targets = ['*', 'server-users', user._id, nodes[0].meshid]; - var event = { etype: 'node', userid: user._id, username: user.name, nodeid: nodes[0]._id, action: 'manual', msg: decodeURIComponent(command.msg), domain: domain.id }; - parent.parent.DispatchEvent(targets, obj, event); - } - } + // Get the node and the rights for this node + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) { + if (rights == 0) return; + + // Add an event for this device + var targets = ['*', 'server-users', user._id, nodes[0].meshid]; + var event = { etype: 'node', userid: user._id, username: user.name, nodeid: nodes[0]._id, action: 'manual', msg: decodeURIComponent(command.msg), domain: domain.id }; + parent.parent.DispatchEvent(targets, obj, event); }); - break; } case 'setNotes': @@ -2604,16 +2455,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if ((idtype != 'user') && (idtype != 'mesh') && (idtype != 'node')) return; if (idtype == 'node') { - // Check if this user has rights on this id to set notes - db.Get(command.id, function (err, nodes) { // TODO: Make a NodeRights(user) method that also does not do a db call if agent is connected (???) - if ((nodes == null) || (nodes.length == 1)) { - if ((parent.GetMeshRights(user, nodes[0].meshid) & MESHRIGHT_SETNOTES) != 0) { - // Set the id's notes - if (common.validateString(command.notes, 1) == false) { - db.Remove('nt' + command.id); // Delete the note for this node - } else { - db.Set({ _id: 'nt' + command.id, type: 'note', value: command.notes }); // Set the note for this node - } + // Get the node and the rights for this node + parent.GetNodeWithRights(domain, user, command.id, function (node, rights, visible) { + if ((rights & MESHRIGHT_SETNOTES) != 0) { + // Set the id's notes + if (common.validateString(command.notes, 1) == false) { + db.Remove('nt' + node._id); // Delete the note for this node + } else { + db.Set({ _id: 'nt' + node._id, type: 'note', value: command.notes }); // Set the note for this node } } }); @@ -2893,44 +2742,26 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use case 'getClip': { if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid - // Get the device - db.Get(command.nodeid, function (err, nodes) { - if ((nodes == null) || (nodes.length != 1)) return; - var node = nodes[0]; + // Get the node and the rights for this node + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) { + if ((rights & MESHRIGHT_AGENTCONSOLE) == 0) return; - // Get the mesh for this device - mesh = parent.meshes[node.meshid]; - if (mesh) { - // Check if this user has "remote" rights to do this - var meshrights = parent.GetMeshRights(user, mesh); - if ((meshrights & MESHRIGHT_AGENTCONSOLE) == 0) return; - - // Ask for clipboard data from agent - var agent = parent.wsagents[node._id]; - if (agent != null) { try { agent.send(JSON.stringify({ action: 'getClip' })); } catch (ex) { } } - } + // Ask for clipboard data from agent + var agent = parent.wsagents[node._id]; + if (agent != null) { try { agent.send(JSON.stringify({ action: 'getClip' })); } catch (ex) { } } }); break; } case 'setClip': { - if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid if (common.validateString(command.data, 1, 65535) == false) break; // Check - // Get the device - db.Get(command.nodeid, function (err, nodes) { - if ((nodes == null) || (nodes.length != 1)) return; - var node = nodes[0]; + // Get the node and the rights for this node + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) { + if ((rights & MESHRIGHT_AGENTCONSOLE) == 0) return; - // Get the mesh for this device - mesh = parent.meshes[node.meshid]; - if (mesh) { - // Check if this user has "remote" rights to do this - if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_AGENTCONSOLE) == 0) return; - - // Send clipboard data to the agent - var agent = parent.wsagents[node._id]; - if (agent != null) { try { agent.send(JSON.stringify({ action: 'setClip', data: command.data })); } catch (ex) { } } - } + // Send clipboard data to the agent + var agent = parent.wsagents[node._id]; + if (agent != null) { try { agent.send(JSON.stringify({ action: 'setClip', data: command.data })); } catch (ex) { } } }); break; } @@ -2951,25 +2782,17 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if ((idtype != 'user') && (idtype != 'mesh') && (idtype != 'node')) return; if (idtype == 'node') { - // Get the device - db.Get(command.id, function (err, nodes) { - if ((nodes == null) || (nodes.length != 1)) return; - var node = nodes[0]; + // Get the node and the rights for this node + parent.GetNodeWithRights(domain, user, command.id, function (node, rights, visible) { + if (visible == false) return; - // Get the mesh for this device - mesh = parent.meshes[node.meshid]; - if (mesh) { - // Check if this user has rights to do this - if (parent.GetMeshRights(user, mesh) == 0) return; - - // Get the notes about this node - db.Get('nt' + command.id, function (err, notes) { - try { - if ((notes == null) || (notes.length != 1)) { ws.send(JSON.stringify({ action: 'getNotes', id: command.id, notes: null })); return; } - ws.send(JSON.stringify({ action: 'getNotes', id: command.id, notes: notes[0].value })); - } catch (ex) { } - }); - } + // Get the notes about this node + db.Get('nt' + command.id, function (err, notes) { + try { + if ((notes == null) || (notes.length != 1)) { ws.send(JSON.stringify({ action: 'getNotes', id: command.id, notes: null })); return; } + ws.send(JSON.stringify({ action: 'getNotes', id: command.id, notes: notes[0].value })); + } catch (ex) { } + }); }); } else if (idtype == 'mesh') { // Get the mesh for this device @@ -3037,6 +2860,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use break; } case 'sendmqttmsg': { + if (parent.parent.mqttbroker == null) { err = 'MQTT not supported on this server'; }; // MQTT not available if (common.validateArray(command.nodeids, 1) == false) { err = 'Invalid nodeids'; }; // Check nodeid's if (common.validateString(command.topic, 1, 64) == false) { err = 'Invalid topic'; } // Check the topic if (common.validateString(command.msg, 1, 4096) == false) { err = 'Invalid msg'; } // Check the message @@ -3047,30 +2871,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use break; } - // TODO: We can optimize this a lot. - // - We should get a full list of all MAC's to wake first. - // - We should try to only have one agent per subnet (using Gateway MAC) send a wake-on-lan. + // Send the MQTT message for (i in command.nodeids) { - nodeid = command.nodeids[i]; - var wakeActions = 0; - if (common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid - if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain - // Get the device - db.Get(nodeid, function (err, nodes) { - if ((nodes == null) || (nodes.length != 1)) return; - var node = nodes[0]; - - // Get the mesh for this device - mesh = parent.meshes[node.meshid]; - if (mesh) { - // Check if this user has rights to do this - if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_WAKEDEVICE) != 0) { - // If this device is connected on MQTT, send a wake action. - if (parent.parent.mqttbroker != null) { parent.parent.mqttbroker.publish(node._id, command.topic, command.msg); } - } - } - }); - } + // Get the node and the rights for this node + parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) { + // If this device is connected on MQTT, send a wake action. + if (rights != 0) { parent.parent.mqttbroker.publish(node._id, command.topic, command.msg); } + }); } break; @@ -3083,43 +2890,33 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Handle any errors if (err != null) { if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'getmqttlogin', responseid: command.responseid, result: err })); } catch (ex) { } } break; } - var nodeid = command.nodeid; - if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain - // Get the device - db.Get(nodeid, function (err, nodes) { - if ((nodes == null) || (nodes.length != 1)) { if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'getmqttlogin', responseid: command.responseid, result: 'Invalid node id' })); } catch (ex) { } return; } } - var node = nodes[0]; + // Get the node and the rights for this node + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) { + // Check if this user has rights to do this + if (rights == 0xFFFFFFFF) { + var token = parent.parent.mqttbroker.generateLogin(node.meshid, node._id); + var r = { action: 'getmqttlogin', responseid: command.responseid, nodeid: node._id, user: token.user, pass: token.pass }; + const serverName = parent.getWebServerName(domain); - // Get the device group for this node - var mesh = parent.meshes[node.meshid]; - if (mesh) { - // Check if this user has rights to do this - if ((parent.GetMeshRights(user, mesh) == 0xFFFFFFFF)) { - var token = parent.parent.mqttbroker.generateLogin(mesh._id, node._id); - var r = { action: 'getmqttlogin', responseid: command.responseid, nodeid: node._id, user: token.user, pass: token.pass }; - const serverName = parent.getWebServerName(domain); - - // Add MPS URL - if (parent.parent.mpsserver != null) { - r.mpsCertHashSha384 = parent.parent.certificateOperations.getCertHash(parent.parent.mpsserver.certificates.mps.cert); - r.mpsCertHashSha1 = parent.parent.certificateOperations.getCertHashSha1(parent.parent.mpsserver.certificates.mps.cert); - r.mpsUrl = 'mqtts://' + serverName + ':' + ((args.mpsaliasport != null) ? args.mpsaliasport : args.mpsport) + '/'; - } - - // Add WS URL - var xdomain = (domain.dns == null) ? domain.id : ''; - if (xdomain != '') xdomain += "/"; - var httpsPort = ((args.aliasport == null) ? args.port : args.aliasport); // Use HTTPS alias port is specified - r.wsUrl = "ws" + (args.notls ? '' : 's') + "://" + serverName + ":" + httpsPort + "/" + xdomain + "mqtt.ashx"; - r.wsTrustedCert = parent.isTrustedCert(domain); - - try { ws.send(JSON.stringify(r)); } catch (ex) { } - } else { - if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'getmqttlogin', responseid: command.responseid, result: 'Unable to perform this operation' })); } catch (ex) { } } - } + // Add MPS URL + if (parent.parent.mpsserver != null) { + r.mpsCertHashSha384 = parent.parent.certificateOperations.getCertHash(parent.parent.mpsserver.certificates.mps.cert); + r.mpsCertHashSha1 = parent.parent.certificateOperations.getCertHashSha1(parent.parent.mpsserver.certificates.mps.cert); + r.mpsUrl = 'mqtts://' + serverName + ':' + ((args.mpsaliasport != null) ? args.mpsaliasport : args.mpsport) + '/'; } - }); - } + + // Add WS URL + var xdomain = (domain.dns == null) ? domain.id : ''; + if (xdomain != '') xdomain += '/'; + var httpsPort = ((args.aliasport == null) ? args.port : args.aliasport); // Use HTTPS alias port is specified + r.wsUrl = 'ws' + (args.notls ? '' : 's') + '://' + serverName + ':' + httpsPort + '/' + xdomain + 'mqtt.ashx'; + r.wsTrustedCert = parent.isTrustedCert(domain); + + try { ws.send(JSON.stringify(r)); } catch (ex) { } + } else { + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'getmqttlogin', responseid: command.responseid, result: 'Unable to perform this operation' })); } catch (ex) { } } + } + }); break; } case 'amt': { @@ -3136,23 +2933,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use } else if (command.mode == 3) { if (parent.parent.apfserver.apfConnections[command.nodeid] == null) break; } - var nodeid = command.nodeid; - if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain - // Get the device - db.Get(nodeid, function (err, nodes) { - if ((nodes == null) || (nodes.length != 1)) return; - var node = nodes[0]; - // Get the mesh for this device - var mesh = parent.meshes[node.meshid]; - if (mesh) { - // Check if this user has rights to do this - if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_REMOTECONTROL) != 0) { // "Remote Control permission" - handleAmtCommand(command, node); - } - } - }); - } + // Get the node and the rights for this node + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) { + if ((rights & MESHRIGHT_REMOTECONTROL) == 0) return; + handleAmtCommand(command, node); + }); break; } case 'distributeCore': { @@ -3304,15 +3090,15 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Read all files recursively try { - files.filetree.f[user._id].f = readFilesRec(parent.path.join(parent.filespath, domainx + "/user-" + usersplit[2])); + files.filetree.f[user._id].f = readFilesRec(parent.path.join(parent.filespath, domainx + '/user-' + usersplit[2])); } catch (e) { // TODO: We may want to fake this file structure until it's needed. // Got an error, try to create all the folders and try again... try { fs.mkdirSync(parent.filespath); } catch (e) { } try { fs.mkdirSync(parent.path.join(parent.filespath, domainx)); } catch (e) { } - try { fs.mkdirSync(parent.path.join(parent.filespath, domainx + "/user-" + usersplit[2])); } catch (e) { } - try { fs.mkdirSync(parent.path.join(parent.filespath, domainx + "/user-" + usersplit[2] + "/Public")); } catch (e) { } - try { files.filetree.f[user._id].f = readFilesRec(parent.path.join(parent.filespath, domainx + "/user-" + usersplit[2])); } catch (e) { } + try { fs.mkdirSync(parent.path.join(parent.filespath, domainx + '/user-' + usersplit[2])); } catch (e) { } + try { fs.mkdirSync(parent.path.join(parent.filespath, domainx + '/user-' + usersplit[2] + '/Public')); } catch (e) { } + try { files.filetree.f[user._id].f = readFilesRec(parent.path.join(parent.filespath, domainx + '/user-' + usersplit[2])); } catch (e) { } } // Add files for each mesh // TODO: Get all meshes including groups!! @@ -3326,7 +3112,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Read all files recursively try { - files.filetree.f[mesh._id].f = readFilesRec(parent.path.join(parent.filespath, domainx + "/mesh-" + meshsplit[2])); + files.filetree.f[mesh._id].f = readFilesRec(parent.path.join(parent.filespath, domainx + '/mesh-' + meshsplit[2])); } catch (e) { files.filetree.f[mesh._id].f = {}; // Got an error, return empty folder. We will create the folder only when needed. } @@ -3338,7 +3124,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use try { ws.send(JSON.stringify(files)); } catch (ex) { } } - function EscapeHtml(x) { if (typeof x == "string") return x.replace(/&/g, '&').replace(/>/g, '>').replace(//g, '>').replace(//g, '>').replace(/').replace(/\n/g, '').replace(/\t/g, '  '); if (typeof x == "boolean") return x; if (typeof x == "number") return x; } // Split a string taking into account the quoats. Used for command line parsing @@ -3388,13 +3174,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use node.intelamt.tls, tlsoptions, parent.parent, cmd.mode); var amt = new Amt(wsman); switch (cmd.command) { - case "Get-GeneralSettings": { - amt.Get("AMT_GeneralSettings", function (obj, name, response, status) { + case 'Get-GeneralSettings': { + amt.Get('AMT_GeneralSettings', function (obj, name, response, status) { if (status == 200) { var resp = { action: 'amt', nodeid: cmd.nodeid, command: 'Get-GeneralSettings', value: response.Body } ws.send(JSON.stringify(resp)); } else { - ws.send(JSON.stringify({ "error": error })); + ws.send(JSON.stringify({ 'error': error })); } }); break; diff --git a/mpsserver.js b/mpsserver.js index 69a04c45..2935a598 100644 --- a/mpsserver.js +++ b/mpsserver.js @@ -11,7 +11,7 @@ /*jshint strict:false */ /*jshint -W097 */ /*jshint esversion: 6 */ -"use strict"; +'use strict'; // Construct a Intel AMT MPS server object module.exports.CreateMpsServer = function (parent, db, args, certificates) { @@ -24,9 +24,9 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { var tlsSessionStore = {}; // Store TLS session information for quick resume. var tlsSessionStoreCount = 0; // Number of cached TLS session information in store. const constants = (require('crypto').constants ? require('crypto').constants : require('constants')); // require('constants') is deprecated in Node 11.10, use require('crypto').constants instead. - const common = require("./common.js"); - const net = require("net"); - const tls = require("tls"); + const common = require('./common.js'); + const net = require('net'); + const tls = require('tls'); const MAX_IDLE = 90000; // 90 seconds max idle time, higher than the typical KEEP-ALIVE periode of 60 seconds if (obj.args.mpstlsoffload) { @@ -42,9 +42,9 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { obj.server.listen(args.mpsport, function () { console.log("MeshCentral Intel(R) AMT server running on " + certificates.AmtMpsName + ":" + args.mpsport + ((args.mpsaliasport != null) ? (", alias port " + args.mpsaliasport) : "") + "."); }).on("error", function (err) { console.error("ERROR: MeshCentral Intel(R) AMT server port " + args.mpsport + " is not available."); if (args.exactports) { process.exit(); } }); obj.server.on('tlsClientError', function (err, tlssocket) { if (args.mpsdebug) { var remoteAddress = tlssocket.remoteAddress; if (tlssocket.remoteFamily == 'IPv6') { remoteAddress = '[' + remoteAddress + ']'; } console.log('MPS:Invalid TLS connection from ' + remoteAddress + ':' + tlssocket.remotePort + '.'); } }); - obj.parent.updateServerState("mps-port", args.mpsport); - obj.parent.updateServerState("mps-name", certificates.AmtMpsName); - if (args.mpsaliasport != null) { obj.parent.updateServerState("mps-alias-port", args.mpsaliasport); } + obj.parent.updateServerState('mps-port', args.mpsport); + obj.parent.updateServerState('mps-name', certificates.AmtMpsName); + if (args.mpsaliasport != null) { obj.parent.updateServerState('mps-alias-port', args.mpsaliasport); } const APFProtocol = { UNKNOWN: 0, @@ -202,47 +202,47 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { function onConnection(socket) { connectionCount++; if (obj.args.mpstlsoffload) { - socket.tag = { first: true, clientCert: null, accumulator: "", activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 }; + socket.tag = { first: true, clientCert: null, accumulator: '', activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 }; } else { - socket.tag = { first: true, clientCert: socket.getPeerCertificate(true), accumulator: "", activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 }; + socket.tag = { first: true, clientCert: socket.getPeerCertificate(true), accumulator: '', activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 }; } - socket.setEncoding("binary"); + socket.setEncoding('binary'); parent.debug('mps', "New CIRA connection"); // Setup the CIRA keep alive timer socket.setTimeout(MAX_IDLE); - socket.on("timeout", () => { ciraTimeoutCount++; parent.debug('mps', "CIRA timeout, disconnecting."); try { socket.end(); } catch (e) { } }); + socket.on('timeout', () => { ciraTimeoutCount++; parent.debug('mps', "CIRA timeout, disconnecting."); try { socket.end(); } catch (e) { } }); - socket.addListener("data", function (data) { - if (args.mpsdebug) { var buf = Buffer.from(data, "binary"); console.log("MPS <-- (" + buf.length + "):" + buf.toString('hex')); } // Print out received bytes + socket.addListener('data', function (data) { + if (args.mpsdebug) { var buf = Buffer.from(data, 'binary'); console.log("MPS <-- (" + buf.length + "):" + buf.toString('hex')); } // Print out received bytes socket.tag.accumulator += data; // Detect if this is an HTTPS request, if it is, return a simple answer and disconnect. This is useful for debugging access to the MPS port. if (socket.tag.first == true) { if (socket.tag.accumulator.length < 3) return; //if (!socket.tag.clientCert.subject) { console.log("MPS Connection, no client cert: " + socket.remoteAddress); socket.write('HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nMeshCentral2 MPS server.\r\nNo client certificate given.'); socket.end(); return; } - if (socket.tag.accumulator.substring(0, 3) == "GET") { if (args.mpsdebug) { console.log("MPS Connection, HTTP GET detected: " + socket.remoteAddress); } socket.write("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\nMeshCentral2 MPS server.
Intel® AMT computers should connect here."); socket.end(); return; } + if (socket.tag.accumulator.substring(0, 3) == 'GET') { if (args.mpsdebug) { console.log("MPS Connection, HTTP GET detected: " + socket.remoteAddress); } socket.write("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\nMeshCentral2 MPS server.
Intel® AMT computers should connect here."); socket.end(); return; } // If the MQTT broker is active, look for inbound MQTT connections if (parent.mqttbroker != null) { - var chunk = Buffer.from(socket.tag.accumulator, "binary"); + var chunk = Buffer.from(socket.tag.accumulator, 'binary'); var packet_len = 0; if (chunk.readUInt8(0) == 16) { packet_len = getMQTTPacketLength(chunk); } if (chunk.readUInt8(0) == 16 && (socket.tag.accumulator.length < packet_len)) return; // Minimum MQTT detection // check if it is MQTT, need more initial packet to probe - if (chunk.readUInt8(0) == 16 && ((chunk.slice(4, 8).toString() === "MQTT") || (chunk.slice(5, 9).toString() === "MQTT") - || (chunk.slice(6, 10).toString() === "MQTT") || (chunk.slice(7, 11).toString() === "MQTT"))) { - parent.debug("mps", "MQTT connection detected."); + if (chunk.readUInt8(0) == 16 && ((chunk.slice(4, 8).toString() === 'MQTT') || (chunk.slice(5, 9).toString() === 'MQTT') + || (chunk.slice(6, 10).toString() === 'MQTT') || (chunk.slice(7, 11).toString() === 'MQTT'))) { + parent.debug('mps', "MQTT connection detected."); socket.removeAllListeners("data"); socket.removeAllListeners("close"); socket.setNoDelay(true); socket.serialtunnel = SerialTunnel(); socket.serialtunnel.xtransport = 'mps'; socket.serialtunnel.xip = socket.remoteAddress; - socket.on("data", function (b) { socket.serialtunnel.updateBuffer(Buffer.from(b, "binary")) }); - socket.serialtunnel.forwardwrite = function (b) { socket.write(b, "binary") } - socket.on("close", function () { socket.serialtunnel.emit("end"); }); + socket.on('data', function (b) { socket.serialtunnel.updateBuffer(Buffer.from(b, 'binary')) }); + socket.serialtunnel.forwardwrite = function (b) { socket.write(b, 'binary') } + socket.on('close', function () { socket.serialtunnel.emit('end'); }); // Pass socket wrapper to the MQTT broker parent.mqttbroker.handle(socket.serialtunnel); @@ -533,7 +533,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { var request = data.substring(5, 5 + requestLen); //var wantResponse = data.charCodeAt(5 + requestLen); - if (request == "tcpip-forward") { + if (request == 'tcpip-forward') { var addrLen = common.ReadInt(data, 6 + requestLen); if (len < 14 + requestLen + addrLen) return 0; var addr = data.substring(10 + requestLen, 10 + requestLen + addrLen); @@ -545,7 +545,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { return 14 + requestLen + addrLen; } - if (request == "cancel-tcpip-forward") { + if (request == 'cancel-tcpip-forward') { var addrLen = common.ReadInt(data, 6 + requestLen); if (len < 14 + requestLen + addrLen) return 0; var addr = data.substring(10 + requestLen, 10 + requestLen + addrLen); @@ -557,7 +557,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { return 14 + requestLen + addrLen; } - if (request == "udp-send-to@amt.intel.com") { + if (request == 'udp-send-to@amt.intel.com') { var addrLen = common.ReadInt(data, 6 + requestLen); if (len < 26 + requestLen + addrLen) return 0; var addr = data.substring(10 + requestLen, 10 + requestLen + addrLen); @@ -748,14 +748,14 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { } } - socket.addListener("close", function () { + socket.addListener('close', function () { socketClosedCount++; parent.debug('mps', 'CIRA connection closed'); try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { } obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2); }); - socket.addListener("error", function () { + socket.addListener('error', function () { socketErrorCount++; //console.log("MPS Error: " + socket.remoteAddress); }); @@ -802,7 +802,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { */ function SendChannelOpen(socket, direct, channelid, windowsize, target, targetport, source, sourceport) { - var connectionType = ((direct == true) ? "direct-tcpip" : "forwarded-tcpip"); + var connectionType = ((direct == true) ? 'direct-tcpip' : 'forwarded-tcpip'); if ((target == null) || (target == null)) target = ''; // TODO: Reports of target being undefined that causes target.length to fail. This is a hack. Write(socket, String.fromCharCode(APFProtocol.CHANNEL_OPEN) + common.IntToStr(connectionType.length) + connectionType + common.IntToStr(channelid) + common.IntToStr(windowsize) + common.IntToStr(-1) + common.IntToStr(target.length) + target + common.IntToStr(targetport) + common.IntToStr(source.length) + source + common.IntToStr(sourceport)); } @@ -837,11 +837,11 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { function Write(socket, data) { if (args.mpsdebug) { // Print out sent bytes - var buf = Buffer.from(data, "binary"); + var buf = Buffer.from(data, 'binary'); console.log('MPS --> (' + buf.length + '):' + buf.toString('hex')); socket.write(buf); } else { - socket.write(Buffer.from(data, "binary")); + socket.write(Buffer.from(data, 'binary')); } } diff --git a/multiserver.js b/multiserver.js index 9a939984..cf758533 100644 --- a/multiserver.js +++ b/multiserver.js @@ -11,7 +11,7 @@ /*jshint strict:false */ /*jshint -W097 */ /*jshint esversion: 6 */ -"use strict"; +'use strict'; // Construct a Mesh Multi-Server object. This is used for MeshCentral-to-MeshCentral communication. module.exports.CreateMultiServer = function (parent, args) { @@ -552,14 +552,10 @@ module.exports.CreateMultiServer = function (parent, args) { if (msg.fromNodeid != null) { msg.nodeid = msg.fromNodeid; delete msg.fromNodeid; } var cmdstr = JSON.stringify(msg); for (userid in obj.parent.webserver.wssessions) { // Find all connected users for this mesh and send the message - var user = obj.parent.webserver.users[userid]; - if (user) { - var rights = user.links[msg.meshid]; - if (rights != null) { // TODO: Look at what rights are needed for message routing - var sessions = obj.parent.webserver.wssessions[userid]; - // Send the message to all users on this server - for (i in sessions) { sessions[i].send(cmdstr); } - } + if (parent.webserver.GetMeshRights(userid, msg.meshid) != 0) { // TODO: Look at what rights are needed for message routing + var sessions = obj.parent.webserver.wssessions[userid]; + // Send the message to all users on this server + for (i in sessions) { sessions[i].send(cmdstr); } } } } diff --git a/package.json b/package.json index f0ca0343..4a175977 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.4.6-p", + "version": "0.4.6-q", "keywords": [ "Remote Management", "Intel AMT", diff --git a/public/images/icons16.png b/public/images/icons16.png index d6704294..a1a0a1c9 100644 Binary files a/public/images/icons16.png and b/public/images/icons16.png differ diff --git a/public/images/icons256-7-1.png b/public/images/icons256-7-1.png new file mode 100644 index 00000000..efeaf016 Binary files /dev/null and b/public/images/icons256-7-1.png differ diff --git a/public/images/icons50.png b/public/images/icons50.png index ca16c28c..9da4f723 100644 Binary files a/public/images/icons50.png and b/public/images/icons50.png differ diff --git a/public/images/icons64.png b/public/images/icons64.png index d8d25ceb..261d43ca 100644 Binary files a/public/images/icons64.png and b/public/images/icons64.png differ diff --git a/public/styles/style.css b/public/styles/style.css index 75d4b26b..83d28311 100644 --- a/public/styles/style.css +++ b/public/styles/style.css @@ -1155,6 +1155,14 @@ a { border: none; } +.i7 { + background: url(../images/icons50.png) -300px 0px; + height: 50px; + width: 50px; + cursor: pointer; + border: none; +} + .j1 { background: url(../images/icons16.png) 0px 0px; height: 16px; @@ -1203,6 +1211,14 @@ a { border: none; } +.j7 { + background: url(../images/icons16.png) -96px 0px; + height: 16px; + width: 16px; + cursor: pointer; + border: none; +} + .relayIcon16 { background: url(../images/icon-relay.png); height: 16px; @@ -1339,7 +1355,7 @@ a { float: left; } -.si0 { +.si1 { background: url(../images/icons16.png) 0px 0px; height: 16px; width: 16px; @@ -1347,7 +1363,7 @@ a { float: left; } -.si1 { +.si2 { background: url(../images/icons16.png) -16px 0px; height: 16px; width: 16px; @@ -1355,7 +1371,7 @@ a { float: left; } -.si2 { +.si3 { background: url(../images/icons16.png) -32px 0px; height: 16px; width: 16px; @@ -1363,7 +1379,7 @@ a { float: left; } -.si3 { +.si4 { background: url(../images/icons16.png) -48px 0px; height: 16px; width: 16px; @@ -1371,7 +1387,7 @@ a { float: left; } -.si4 { +.si5 { background: url(../images/icons16.png) -64px 0px; height: 16px; width: 16px; @@ -1379,7 +1395,7 @@ a { float: left; } -.si5 { +.si6 { background: url(../images/icons16.png) -80px 0px; height: 16px; width: 16px; @@ -1387,6 +1403,14 @@ a { float: left; } +.si7 { + background: url(../images/icons16.png) -96px 0px; + height: 16px; + width: 16px; + border: none; + float: left; +} + .mi { background: url(../images/meshicon50.png) 0px 0px; height: 50px; diff --git a/translate/translate.json b/translate/translate.json index 9610c1b2..116266a3 100644 --- a/translate/translate.json +++ b/translate/translate.json @@ -8,8 +8,7 @@ "nl": "0", "xloc": [ "default.handlebars->container->masthead->7->notificationCount", - "default-mobile.handlebars->9->229", - { "$ref": "#" } + "default-mobile.handlebars->9->229" ] }, { @@ -5610,7 +5609,7 @@ { "en": "free", "fr": "libre", - "pt": "Livre", + "pt": "livre", "ja": "無料", "cs": "volné", "nl": "vrij", diff --git a/views/default-mobile.handlebars b/views/default-mobile.handlebars index 70e05bcc..74f56512 100644 --- a/views/default-mobile.handlebars +++ b/views/default-mobile.handlebars @@ -1327,7 +1327,7 @@ count++; // Mesh rights - var meshrights = meshes[i].links[userinfo._id].rights; + var meshrights = GetMeshRights(meshes[i]); var rights = "Partial Rights"; if (meshrights == 0xFFFFFFFF) rights = "Full Administrator"; else if (meshrights == 0) rights = "No Rights"; @@ -1576,8 +1576,7 @@ if (desktop && !xxdialogMode && xxcurrentView == 10) { // Check what keys we are allows to send if (currentNode != null) { - var mesh = meshes[currentNode.meshid]; - var meshrights = mesh.links[userinfo._id].rights; + var meshrights = GetMeshRights(currentNode.meshid); var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0))); if (inputAllowed == false) return false; var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0))); @@ -1594,8 +1593,7 @@ if (desktop && !xxdialogMode && xxcurrentView == 10) { // Check what keys we are allows to send if (currentNode != null) { - var mesh = meshes[currentNode.meshid]; - var meshrights = mesh.links[userinfo._id].rights; + var meshrights = GetMeshRights(currentNode.meshid); var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0))); if (inputAllowed == false) return false; var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0))); @@ -1612,8 +1610,7 @@ if (desktop && !xxdialogMode && xxcurrentView == 10) { // Check what keys we are allows to send if (currentNode != null) { - var mesh = meshes[currentNode.meshid]; - var meshrights = mesh.links[userinfo._id].rights; + var meshrights = GetMeshRights(currentNode.meshid); var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0))); if (inputAllowed == false) return false; var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0))); @@ -1655,9 +1652,7 @@ // Go thru the list of nodes and display them for (var i in nodes) { if (nodes[i].v == false) continue; - var mesh2 = meshes[nodes[i].meshid], meshlinks = mesh2.links[userinfo._id]; - if (meshlinks == null) continue; - var meshrights = meshlinks.rights; + //var meshrights = GetNodeRights(nodes[i]); if (sort == 0) { // Mesh header @@ -1668,7 +1663,7 @@ if (meshes[nodes[i].meshid].mtype == 1) { extra = '' + ", Intel® AMT only" + ''; } if (current != null) { if (c == 2) { r += '
'; } if (r != '') { r += ''; } } r += '
'; - //r += getMeshActions(mesh2, meshrights); + //r += getMeshActions(meshes[nodes[i].meshid], meshrights); r += '' + EscapeHtml(meshes[nodes[i].meshid].name) + '' + extra + '
'; current = nodes[i].meshid; displayedMeshes[current] = 1; @@ -1726,20 +1721,17 @@ // Display all empty meshes, we need to do this because users can add devices to these at any time. if (sort == 0) { for (var i in meshes) { - var mesh = meshes[i], meshlink = mesh.links[userinfo._id]; - if (meshlink != null) { - var meshrights = meshlink.rights; - if (displayedMeshes[mesh._id] == null) { - if ((current != '') && (r != '')) { r += ''; } - r += '
'; - //r += getMeshActions(mesh, meshrights); - r += '' + EscapeHtml(mesh.name) + '
'; - if (mesh.mtype == 1) { r += '
' + "No Intel® AMT devices in this group"; } - if (mesh.mtype == 2) { r += '
' + "No devices in this group"; } - r += '.
'; - current = mesh._id; - count++; - } + var mesh = meshes[i]; + if (IsMeshViewable(mesh)) { + if ((current != '') && (r != '')) { r += ''; } + r += '
'; + //r += getMeshActions(mesh, meshrights); + r += '' + EscapeHtml(mesh.name) + '
'; + if (mesh.mtype == 1) { r += '
' + "No Intel® AMT devices in this group"; } + if (mesh.mtype == 2) { r += '
' + "No devices in this group"; } + r += '.
'; + current = mesh._id; + count++; } } } @@ -1813,11 +1805,6 @@ gotoDevice(nodeid, xxcurrentView, true); } - function getNodeRights(nodeid) { - var node = getNodeFromId(nodeid), mesh = meshes[node.meshid]; - return mesh.links[userinfo._id].rights; - } - var currentDevicePanel = 0; var currentNode; var powerTimelineNode = null; @@ -1837,7 +1824,7 @@ if (node == null) { goBack(); return; } var mesh = meshes[node.meshid]; if (mesh == null) { goBack(); return; } - var meshrights = mesh.links[userinfo._id].rights; + var meshrights = GetMeshRights(mesh); if (!currentNode || currentNode._id != node._id || refresh == true) { currentNode = node; @@ -2008,8 +1995,7 @@ } function setupDeviceMenu(op, obj) { - var meshrights = 0; - if (currentNode) { meshrights = meshes[currentNode.meshid].links[userinfo._id].rights; } + var meshrights = GetNodeRights(currentNode); if (op != null) { currentDevicePanel = op; } QV('p10general', currentDevicePanel == 0); QV('p10desktop', currentDevicePanel == 1); // Show if we have remote control rights or desktop view only rights @@ -2027,7 +2013,7 @@ function deviceActionFunction() { if (xxdialogMode) return; - var meshrights = meshes[currentNode.meshid].links[userinfo._id].rights; + var meshrights = GetNodeRights(currentNode); var x = "Select an operation to perform on this device." + '

'; var y = ''); x += addHtmlValue("Password", ''); @@ -2166,9 +2152,8 @@ function p10showiconselector() { if (xxdialogMode) return; - var mesh = meshes[currentNode.meshid]; - var meshrights = mesh.links[userinfo._id].rights; - if ((meshrights & 4) == 0) return; + var rights = GetNodeRights(currentNode); + if ((rights & 4) == 0) return; var x = '
'; x += '
'; @@ -2245,7 +2230,7 @@ var mesh = meshes[currentNode.meshid]; var deskState = 0; if (desktop != null) { deskState = desktop.State; } - var meshrights = mesh.links[userinfo._id].rights; + var meshrights = GetNodeRights(currentNode); // Show the right buttons QV('disconnectbutton1', (deskState != 0)); @@ -3096,7 +3081,7 @@ if (currentMesh == null) return; QH('p20meshName', EscapeHtml(currentMesh.name)); var meshtype = format("Unknown #{0}", currentMesh.mtype); - var meshrights = currentMesh.links[userinfo._id].rights; + var meshrights = GetMeshRights(currentMesh); if (currentMesh.mtype == 1) meshtype = "Intel® AMT only, no agent"; if (currentMesh.mtype == 2) meshtype = "Managed using a software agent"; @@ -3229,7 +3214,7 @@ } function p20validateAddMeshUserDialog() { - var meshrights = currentMesh.links[userinfo._id].rights; + var meshrights = GetMeshRights(currentMesh); var nc = !Q('p20fulladmin').checked; QE('p20fulladmin', meshrights == 0xFFFFFFFF); QE('p20editmesh', nc && (meshrights == 0xFFFFFFFF)); @@ -3278,7 +3263,7 @@ function p20viewuser(userid) { if (xxdialogMode) return; userid = decodeURIComponent(userid); - var r = [], cmeshrights = currentMesh.links[userinfo._id].rights, meshrights = currentMesh.links[userid].rights; + var r = [], cmeshrights = GetMeshRights(currentMesh), meshrights = GetMeshRights(currentMesh, userid); if (meshrights == 0xFFFFFFFF) r.push("Full Administrator"); else { if ((meshrights & 1) != 0) r.push("Edit Device Group"); if ((meshrights & 2) != 0) r.push("Manage Device Group Users"); @@ -3361,6 +3346,49 @@ if (((b & 8) || x) && f) f(x, t); } + // + // Access Control Functions + // These must match server + // + + // Get the right of a user on a given device group + function GetMeshRights(mesh, user) { + if (mesh == null) { return 0; } + if (user == null) { user = userinfo._id; } + if (typeof mesh == 'string') { mesh = meshes[mesh] } + if ((mesh == null) || (mesh.links == null)) { return 0; } + var rights = mesh.links[user]; + if (rights == null) { return 0; } + return rights.rights; + } + + // Returns true if the user can view the given device group + function IsMeshViewable(mesh, user) { + if (mesh == null) { return 0; } + if (user == null) { user = userinfo._id; } + if (typeof mesh == 'string') { mesh = meshes[mesh] } + if ((mesh == null) || (mesh.links == null)) { return false; } + var rights = mesh.links[user]; + if (rights == null) { return false; } + return true; + } + + // Return the user rights for a given node + function GetNodeRights(node, user) { + if (node == null) { return 0; } + if (user == null) { user = userinfo._id; } + if (typeof node == 'string') { node = getNodeFromId(node); if (node == null) { return 0; } } + var mesh = meshes[node.meshid]; + if ((mesh == null) || (mesh.links == null)) { return 0; } + var meshlinks = mesh.links[user]; + if (meshlinks == null) { return 0; } + return meshlinks.rights; + } + + // + // Generic Methods + // + function putstore(name, val) { try { if ((typeof (localStorage) === 'undefined') || (localStorage.getItem(name) == val)) return; if (val == null) { localStorage.removeItem(name); } else { localStorage.setItem(name, val); } } catch (e) { } if (name[0] != '_') { var s = {}; for (var i = 0, len = localStorage.length; i < len; ++i) { var k = localStorage.key(i); if (k[0] != '_') { s[k] = localStorage.getItem(k); } } meshserver.send({ action: 'userWebState', state: JSON.stringify(s) }); } } function getstore(name, val) { try { if (typeof (localStorage) === 'undefined') return val; var v = localStorage.getItem(name); if ((v == null) || (v == null)) return val; return v; } catch (e) { return val; } } function center() { QS('dialog').left = ((((getDocWidth() - 300) / 2)) + 'px'); deskAdjust(); deskAdjust(); /*drawDeviceTimeline();*/ } diff --git a/views/default.handlebars b/views/default.handlebars index 01c98e38..c8341864 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -2587,8 +2587,7 @@ if (!xxdialogMode && xxcurrentView == 11 && desktop && Q('DeskControl').checked) { // Check what keys we are allows to send if (currentNode != null) { - var mesh = meshes[currentNode.meshid]; - var meshrights = mesh.links[userinfo._id].rights; + var meshrights = GetMeshRights(currentNode.meshid); var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0))); if (inputAllowed == false) return false; var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0))); @@ -2647,8 +2646,7 @@ if (!xxdialogMode && xxcurrentView == 11 && desktop && Q('DeskControl').checked) { // Check what keys we are allows to send if (currentNode != null) { - var mesh = meshes[currentNode.meshid]; - var meshrights = mesh.links[userinfo._id].rights; + var meshrights = GetMeshRights(currentNode.meshid); var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0))); if (inputAllowed == false) return false; var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0))); @@ -2682,8 +2680,7 @@ if (!xxdialogMode && xxcurrentView == 11 && desktop && Q('DeskControl').checked) { // Check what keys we are allows to send if (currentNode != null) { - var mesh = meshes[currentNode.meshid]; - var meshrights = mesh.links[userinfo._id].rights; + var meshrights = GetMeshRights(currentNode.meshid); var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0))); if (inputAllowed == false) return false; var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0))); @@ -2783,10 +2780,9 @@ for (var i in nodes) { var node = nodes[i]; if (node.v == false) continue; - var mesh2 = meshes[node.meshid], meshlinks = mesh2.links[userinfo._id]; - if (meshlinks == null) continue; - var meshrights = meshlinks.rights; + var mesh2 = meshes[node.meshid]; if ((view == 3) && (mesh2.mtype == 1)) continue; + var meshrights = GetNodeRights(node); if (sort == 0) { // Mesh header if (node.meshid != current) { @@ -2948,37 +2944,34 @@ if ((sort == 0) && (Q('SearchInput').value == '') && (view < 3)) { var deviceHeaderId2 = deviceHeaderId; for (var i in meshes) { - var mesh = meshes[i], meshlink = mesh.links[userinfo._id]; - if (meshlink != null) { - var meshrights = meshlink.rights; - if (displayedMeshes[mesh._id] == null) { - if ((current != '') && (r != '')) { r += '
'; } - r += '
'; + var mesh = meshes[i], meshrights = GetMeshRights(mesh); + if (displayedMeshes[mesh._id] == null) { + if ((current != '') && (r != '')) { r += '
'; } + r += ''; - if (mesh.mtype == 1) { - r += ''; - r += ''; // End collapsing area - - current = mesh._id; - count++; + r += '' + EscapeHtml(mesh.name) + ''; + r += getMeshActions(mesh, meshrights); + r += ''; + if (mesh.mtype == 1) { + r += ''; + r += ''; // End collapsing area + + current = mesh._id; + count++; } } } @@ -3107,8 +3100,8 @@ function toggleKvmDevice(node) { if (typeof node == 'string') { node = getNodeFromId(node); } // Convert nodeid to node if needed - var mesh = meshes[node.meshid], meshrights = mesh.links[userinfo._id].rights; - if ((meshrights & 8) || (meshrights & 256)) { // Requires remote control rights or desktop view only rights + var rights = GetNodeRights(node); + if ((rights & 8) || (rights & 256)) { // Requires remote control rights or desktop view only rights //var conn = 0; //if ((node.conn & 1) != 0) { conn = 1; } else if ((node.conn & 6) != 0) { conn = 2; } // Check what type of connect we can do (Agent vs AMT) if (node.conn & 1) { connectMultiDesktop(node, 1); } @@ -3133,8 +3126,8 @@ for (var i in nodes) { var node = nodes[i], nodeid = nodes[i]._id; if ((multiDesktop[nodeid] == null) && ((multiDesktopFilter.length == 0) || (multiDesktopFilter.indexOf('devid_' + nodeid) >= 0))) { - var mesh = meshes[node.meshid], meshrights = mesh.links[userinfo._id].rights; - if ((meshrights & 8) || (meshrights & 256)) { // Requires remote control rights or desktop view only rights + var rights = GetNodeRights(node); + if ((rights & 8) || (rights & 256)) { // Requires remote control rights or desktop view only rights //var conn = 0; //if ((node.conn & 1) != 0) { conn = 1; } else if ((node.conn & 6) != 0) { conn = 2; } // Check what type of connect we can do (Agent vs AMT) if ((node.conn & 1) && (node.v == true)) { count++; } @@ -3646,9 +3639,8 @@ // Display the "Uninstall Agent" option if allowed and we selected connected devices. for (var i in nodeids) { var node = getNodeFromId(nodeids[i]); - var mesh = meshes[node.meshid]; - var meshrights = mesh.links[userinfo._id].rights; - if (((node.conn & 1) != 0) && ((meshrights & 32768) != 0)) { addedOptions += ''; break; } + var rights = GetNodeRights(node); + if (((node.conn & 1) != 0) && ((rights & 32768) != 0)) { addedOptions += ''; break; } } var x = "Select an operation to perform on all selected devices. Actions will be performed only with proper rights." + '

'; @@ -3790,19 +3782,18 @@ var nodeid = contextelement.children[1].attributes.onclick.value; var node = getNodeFromId(nodeid.substring(12, nodeid.length - 18)); var mesh = meshes[node.meshid]; - var meshlinks = mesh.links[userinfo._id]; - var meshrights = meshlinks.rights; - var consoleRights = ((meshrights & 16) != 0); + var rights = GetNodeRights(node); + var consoleRights = ((rights & 16) != 0); // Check if we have terminal and file access - var terminalAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 512) == 0)); - var fileAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 1024) == 0)); + var terminalAccess = ((rights == 0xFFFFFFFF) || ((rights & 512) == 0)); + var fileAccess = ((rights == 0xFFFFFFFF) || ((rights & 1024) == 0)); - QV('cxdesktop', ((mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 1) != 0) || (node.intelamt && (node.intelamt.state == 2))) && ((meshrights & 8) || (meshrights & 256))); - QV('cxterminal', ((mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 2) != 0) || (node.intelamt && (node.intelamt.state == 2))) && (meshrights & 8) && terminalAccess); - QV('cxfiles', ((mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 4) != 0))) && (meshrights & 8) && fileAccess); - QV('cxevents', (node.intelamt != null) && ((node.intelamt.state == 2) || (node.conn & 2)) && (meshrights & 8)); - QV('cxconsole', (consoleRights && (mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 8) != 0))) && (meshrights & 8)); + QV('cxdesktop', ((mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 1) != 0) || (node.intelamt && (node.intelamt.state == 2))) && ((rights & 8) || (rights & 256))); + QV('cxterminal', ((mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 2) != 0) || (node.intelamt && (node.intelamt.state == 2))) && (rights & 8) && terminalAccess); + QV('cxfiles', ((mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 4) != 0))) && (rights & 8) && fileAccess); + QV('cxevents', (node.intelamt != null) && ((node.intelamt.state == 2) || (node.conn & 2)) && (rights & 8)); + QV('cxconsole', (consoleRights && (mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 8) != 0))) && (rights & 8)); } return haltEvent(event); @@ -4488,11 +4479,6 @@ gotoDevice(nodeid, xxcurrentView, true); } - function getNodeRights(nodeid) { - var node = getNodeFromId(nodeid), mesh = meshes[node.meshid]; - return mesh.links[userinfo._id].rights; - } - var currentNode; var powerTimelineNode = null; var powerTimelineReq = null; @@ -4515,7 +4501,7 @@ //disconnectAllKvmFunction(); var node = getNodeFromId(nodeid); var mesh = meshes[node.meshid]; - var meshrights = mesh.links[userinfo._id].rights; + var meshrights = GetNodeRights(node); if (!currentNode || currentNode._id != node._id || refresh == true) { currentNode = node; @@ -4857,13 +4843,13 @@ function deviceActionFunction() { if (xxdialogMode) return; - var meshrights = meshes[currentNode.meshid].links[userinfo._id].rights; + var rights = GetNodeRights(currentNode); var x = "Select an operation to perform on this device." + '

'; var y = ''; x += addHtmlValue("Operation", y); setDialogMode(2, "Device Action", 3, deviceActionFunctionEx, x); @@ -4975,7 +4961,7 @@ function editDeviceAmtSettings(nodeid, func, arg) { if (xxdialogMode) return; - var x = '', node = getNodeFromId(nodeid), buttons = 3, meshrights = getNodeRights(nodeid); + var x = '', node = getNodeFromId(nodeid), buttons = 3, meshrights = GetNodeRights(node); if ((meshrights & 4) == 0) return; x += addHtmlValue("Username", ''); x += addHtmlValue("Password", ''); @@ -5049,7 +5035,7 @@ // List all available alternative groups var y = ''; @@ -5195,17 +5181,16 @@ function p10showiconselector() { if (xxdialogMode) return; - var mesh = meshes[currentNode.meshid]; - var meshrights = mesh.links[userinfo._id].rights; - if ((meshrights & 4) == 0) return; + if ((GetNodeRights(currentNode) & 4) == 0) return; - var x = '
'; + var x = '
'; x += '
'; x += '
'; x += '
'; x += '
'; x += '
'; - x += '


'; + x += '
'; + x += '


'; setDialogMode(2, "Icon Selection", 0, null, x); QV('id_dialogclose', true); } @@ -5292,14 +5277,14 @@ var mesh = meshes[currentNode.meshid]; var deskState = 0; if (desktop != null) { deskState = desktop.State; } - var meshrights = mesh.links[userinfo._id].rights; + var rights = GetNodeRights(currentNode); // Show the right buttons QV('disconnectbutton1span', (deskState != 0)); - QV('connectbutton1span', (deskState == 0) && ((meshrights & 8) || (meshrights & 256)) && (mesh.mtype == 2) && (currentNode.agent.caps & 1)); + QV('connectbutton1span', (deskState == 0) && ((rights & 8) || (rights & 256)) && (mesh.mtype == 2) && (currentNode.agent.caps & 1)); QV('connectbutton1hspan', (deskState == 0) && - (meshrights & 8) && + (rights & 8) && ((mesh.mtype == 1) || ((currentNode.intelamt != null) && (currentNode.intelamt.state == 2) && @@ -5314,7 +5299,7 @@ QV('d7meshkvm', (webRtcDesktop) || ((mesh.mtype == 2) && (currentNode.agent.caps & 1) && ((deskState == false) || (desktop.contype == 1)))); // Enable buttons - var inputAllowed = (meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) == 0)); + var inputAllowed = (rights == 0xFFFFFFFF) || (((rights & 8) != 0) && ((rights & 256) == 0) && ((rights & 4096) == 0)); var online = ((currentNode.conn & 1) != 0); // If Agent (1) connected, enable remote desktop QE('connectbutton1', online); var hwonline = ((currentNode.conn & 6) != 0); // If CIRA (2) or AMT (4) connected, enable hardware terminal @@ -5330,8 +5315,8 @@ QE('deskkeys', deskState == 3); // Display this only if we have Chat & Notify permissions - QV('DeskChatButton', ((meshrights & 16384) != 0) && (browserfullscreen == false) && (inputAllowed) && (mesh.mtype == 2) && online); - QV('DeskNotifyButton', ((meshrights & 16384) != 0) && (browserfullscreen == false) && (currentNode.agent) && (currentNode.agent.id < 5) && (inputAllowed) && (mesh.mtype == 2) && online); + QV('DeskChatButton', ((rights & 16384) != 0) && (browserfullscreen == false) && (inputAllowed) && (mesh.mtype == 2) && online); + QV('DeskNotifyButton', ((rights & 16384) != 0) && (browserfullscreen == false) && (currentNode.agent) && (currentNode.agent.id < 5) && (inputAllowed) && (mesh.mtype == 2) && online); QV('DeskToolsButton', (inputAllowed) && (mesh.mtype == 2) && online); QV('DeskOpenWebButton', (browserfullscreen == false) && (inputAllowed) && (mesh.mtype == 2) && online); @@ -5339,7 +5324,7 @@ QV('DeskControlSpan', inputAllowed) QV('deskActionsBtn', (browserfullscreen == false)); QV('deskActionsSettings', (browserfullscreen == false)); - if (meshrights & 8) { Q('DeskControl').checked = (getstore('DeskControl', 1) == 1); } else { Q('DeskControl').checked = false; } + if (rights & 8) { Q('DeskControl').checked = (getstore('DeskControl', 1) == 1); } else { Q('DeskControl').checked = false; } if (online == false) QV('DeskTools', false); } @@ -6966,8 +6951,8 @@ consoleNode = currentNode; var mesh = meshes[consoleNode.meshid]; - var meshrights = mesh.links[userinfo._id].rights; - if ((meshrights & 16) != 0) { + var rights = GetNodeRights(currentNode); + if ((rights & 16) != 0) { if (consoleNode.consoleText == null) { consoleNode.consoleText = ''; } if (samenode == false) { QH('p15agentConsoleText', consoleNode.consoleText); @@ -7443,8 +7428,7 @@ count++; // Mesh rights - var meshrights = 0; - if (meshes[i].links[userinfo._id]) { meshrights = meshes[i].links[userinfo._id].rights; } + var meshrights = GetMeshRights(meshes[i]); var rights = "Partial Rights"; if (meshrights == 0xFFFFFFFF) rights = "Full Administrator"; else if (meshrights == 0) rights = "No Rights"; @@ -7511,8 +7495,7 @@ if (currentMesh == null) return; QH('p20meshName', EscapeHtml(currentMesh.name)); var meshtype = format("Unknown #{0}", currentMesh.mtype); - var meshrights = 0; - try { meshrights = currentMesh.links[userinfo._id].rights; } catch (ex) { } + var meshrights = GetMeshRights(currentMesh); if (currentMesh.mtype == 1) meshtype = "Intel® AMT only, no agent"; if (currentMesh.mtype == 2) meshtype = "Managed using a software agent"; @@ -7580,10 +7563,9 @@ if (meshrights & 1) { x += '
'; } x += '

'; - var currentMeshLinks = currentMesh.links[userinfo._id]; - if (currentMeshLinks && ((currentMeshLinks.rights & 2) != 0)) { x += ' ' + "Add Users" + ''; } + if (meshrights & 2) { x += ' ' + "Add Users" + ''; } - if ((meshrights & 4) != 0) { + if (meshrights & 4) { if (currentMesh.mtype == 1) { x += ' ' + "Install CIRA" + ''; x += ' ' + "Install local" + ''; @@ -7838,7 +7820,7 @@ Q('dp20username').focus(); } else { setDialogMode(2, "Edit User Device Group Permissions", 7, p20showAddMeshUserDialogEx, x, userid); - var cmeshrights = currentMesh.links[userinfo._id].rights, meshrights = currentMesh.links[userid].rights; + var cmeshrights = GetMeshRights(currentMesh), meshrights = GetMeshRights(currentMesh, userid); if (meshrights == 0xFFFFFFFF) { Q('p20fulladmin').checked = true; } else { @@ -7877,7 +7859,7 @@ } function p20validateAddMeshUserDialog() { - var meshrights = currentMesh.links[userinfo._id].rights; + var meshrights = GetMeshRights(currentMesh); var ok = true; if (Q('dp20username')) { var xusers = Q('dp20username').value.split(','); @@ -7964,7 +7946,7 @@ function p20viewuser(userid) { if (xxdialogMode) return; var xuserid = decodeURIComponent(userid); - var cmeshrights = currentMesh.links[userinfo._id].rights, meshrights = currentMesh.links[xuserid].rights; + var cmeshrights = GetMeshRights(currentMesh), meshrights = GetMeshRights(currentMesh, xuserid); if (((userinfo._id) != xuserid) && (cmeshrights == 0xFFFFFFFF || (((cmeshrights & 2) != 0) && (meshrights != 0xFFFFFFFF)))) { p20showAddMeshUserDialog(userid); } else { @@ -10063,7 +10045,49 @@ if (pname == null) { Q('p43iframe').src = ''; } else { QH('p43title', title); Q('p43iframe').src = '/pluginadmin.ashx?pin=' + pname; go(43); } } + // + // Access Control Functions + // These must match server + // + + // Get the right of a user on a given device group + function GetMeshRights(mesh, user) { + if (mesh == null) { return 0; } + if (user == null) { user = userinfo._id; } + if (typeof mesh == 'string') { mesh = meshes[mesh] } + if ((mesh == null) || (mesh.links == null)) { return 0; } + var rights = mesh.links[user]; + if (rights == null) { return 0; } + return rights.rights; + } + + // Returns true if the user can view the given device group + function IsMeshViewable(mesh, user) { + if (mesh == null) { return 0; } + if (user == null) { user = userinfo._id; } + if (typeof mesh == 'string') { mesh = meshes[mesh] } + if ((mesh == null) || (mesh.links == null)) { return false; } + var rights = mesh.links[user]; + if (rights == null) { return false; } + return true; + } + + // Return the user rights for a given node + function GetNodeRights(node, user) { + if (node == null) { return 0; } + if (user == null) { user = userinfo._id; } + if (typeof node == 'string') { node = getNodeFromId(node); if (node == null) { return 0; } } + var mesh = meshes[node.meshid]; + if ((mesh == null) || (mesh.links == null)) { return 0; } + var meshlinks = mesh.links[user]; + if (meshlinks == null) { return 0; } + return meshlinks.rights; + } + + // // Generic methods + // + function joinPaths() { var x = []; for (var i in arguments) { var w = arguments[i]; if ((w != null) && (w != '')) { while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); } while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); } x.push(w); } } return x.join('/'); } function putstore(name, val) { try { diff --git a/webserver.js b/webserver.js index be1822c1..6a08e054 100644 --- a/webserver.js +++ b/webserver.js @@ -2065,7 +2065,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if ((splitpath.length < 3) || (splitpath[0] != 'user' && splitpath[0] != 'mesh') || (splitpath[1] != domain.id)) return null; // Basic validation var objid = splitpath[0] + '/' + splitpath[1] + '/' + splitpath[2]; if (splitpath[0] == 'user' && (objid != user._id)) return null; // User validation, only self allowed - if (splitpath[0] == 'mesh') { var link = user.links[objid]; if ((link == null) || (link.rights == null) || ((link.rights & 32) == 0)) { return null; } } // Check mesh server file rights + if (splitpath[0] == 'mesh') { if ((obj.GetMeshRights(user, objid) & 32) == 0) { return null; } } // Check mesh server file rights if (splitpath[1] != '') { serverpath += '-' + splitpath[1]; } // Add the domain if needed serverpath += ('/' + splitpath[0] + '-' + splitpath[2]); for (var i = 3; i < splitpath.length; i++) { if (obj.common.IsFilenameValid(splitpath[i]) == true) { serverpath += '/' + splitpath[i]; filename = splitpath[i]; } else { return null; } } // Check that each folder is correct @@ -2265,8 +2265,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (!node.intelamt) { console.log('ERR: Not AMT node'); try { ws.close(); } catch (e) { } return; } // Disconnect websocket // Check if this user has permission to manage this computer - var meshlinks = user.links[node.meshid]; - if ((!meshlinks) || (!meshlinks.rights) || ((meshlinks.rights & MESHRIGHT_REMOTECONTROL) == 0)) { console.log('ERR: Access denied (2)'); try { ws.close(); } catch (e) { } return; } + if ((obj.GetMeshRights(user, node.meshid) & MESHRIGHT_REMOTECONTROL) == 0) { console.log('ERR: Access denied (2)'); try { ws.close(); } catch (e) { } return; } // Check what connectivity is available for this node var state = parent.GetConnectivityState(req.query.host); @@ -2998,9 +2997,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // If required, check if this user has rights to do this if ((obj.parent.config.settings != null) && ((obj.parent.config.settings.lockagentdownload == true) || (domain.lockagentdownload == true))) { - var user = obj.users[req.session.userid]; - if ((user == null) || (mesh.links[user._id] == null) || ((mesh.links[user._id].rights & 1) == 0)) { res.sendStatus(401); return; } - if (domain.id != mesh.domain) { res.sendStatus(401); return; } + if ((domain.id != mesh.domain) || ((obj.GetMeshRights(req.session.userid, mesh) & 1) == 0)) { res.sendStatus(401); return; } } var meshidhex = Buffer.from(req.query.meshid.replace(/\@/g, '+').replace(/\$/g, '/'), 'base64').toString('hex').toUpperCase(); @@ -3168,9 +3165,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // If required, check if this user has rights to do this if ((obj.parent.config.settings != null) && ((obj.parent.config.settings.lockagentdownload == true) || (domain.lockagentdownload == true))) { - var user = obj.users[req.session.userid]; - if ((user == null) || (mesh.links[user._id] == null) || ((mesh.links[user._id].rights & 1) == 0)) { res.sendStatus(401); return; } - if (domain.id != mesh.domain) { res.sendStatus(401); return; } + if ((domain.id != mesh.domain) || ((obj.GetMeshRights(req.session.userid, mesh) & 1) == 0)) { res.sendStatus(401); return; } } var meshidhex = Buffer.from(req.query.meshid.replace(/\@/g, '+').replace(/\$/g, '/'), 'base64').toString('hex').toUpperCase(); @@ -3257,9 +3252,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // If needed, check if this user has rights to do this if ((obj.parent.config.settings != null) && ((obj.parent.config.settings.lockagentdownload == true) || (domain.lockagentdownload == true))) { - var user = obj.users[req.session.userid]; - if ((user == null) || (mesh.links[user._id] == null) || ((mesh.links[user._id].rights & 1) == 0)) { res.sendStatus(401); return; } - if (domain.id != mesh.domain) { res.sendStatus(401); return; } + if ((domain.id != mesh.domain) || ((obj.GetMeshRights(req.session.userid, mesh) & 1) == 0)) { res.sendStatus(401); return; } } var meshidhex = Buffer.from(req.query.id.replace(/\@/g, '+').replace(/\$/g, '/'), 'base64').toString('hex').toUpperCase(); @@ -3297,9 +3290,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { var node = docs[0]; // Check if we have right to this node - var rights = 0; - for (var i in user.links) { if (i == node.meshid) { rights = user.links[i].rights; } } - if (rights == 0) { res.sendStatus(401); return; } + if (obj.GetMeshRights(user, node.meshid) == 0) { res.sendStatus(401); return; } // Get the list of power events and send them res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'text/csv', 'Content-Disposition': 'attachment; filename="powerevents.csv"' }); @@ -3772,8 +3763,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (agent == null) return; // Check we have agent rights - var rights = user.links[agent.dbMeshKey].rights; - if ((rights != null) && ((rights & MESHRIGHT_AGENTCONSOLE) != 0) || (user.siteadmin == 0xFFFFFFFF)) { agent.close(disconnectMode); } + if (((obj.GetMeshRights(user, agent.dbMeshKey) & MESHRIGHT_AGENTCONSOLE) != 0) || (user.siteadmin == 0xFFFFFFFF)) { agent.close(disconnectMode); } }; // Send the core module to the mesh agent @@ -3787,8 +3777,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (agent == null) return; // Check we have agent rights - var rights = user.links[agent.dbMeshKey].rights; - if ((rights != null) && ((rights & MESHRIGHT_AGENTCONSOLE) != 0) || (user.siteadmin == 0xFFFFFFFF)) { + if (((obj.GetMeshRights(user, agent.dbMeshKey) & MESHRIGHT_AGENTCONSOLE) != 0) || (user.siteadmin == 0xFFFFFFFF)) { if (coretype == 'clear') { // Clear the mesh agent core agent.agentCoreCheck = 1000; // Tell the agent object we are using a custom core. @@ -3907,6 +3896,32 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { } }; + // + // Access Control Functions + // + + // Return the node and rights for a given nodeid + obj.GetNodeWithRights = function (domain, user, nodeid, func) { + // Perform user pre-validation + if ((user == null) || (nodeid == null)) { func(null, 0, false); return; } // Invalid user + if (typeof user == 'string') { user = obj.users[user]; } + if ((user == null) || (user.links == null)) { func(null, 0, false); return; } // No rights + + // Perform node pre-validation + if (obj.common.validateString(nodeid, 0, 128) == false) { func(null, 0, false); return; } // Invalid nodeid + const snode = nodeid.split('/'); + if ((snode.length != 3) || (snode[0] != 'node')) { func(null, 0, false); return; } // Invalid nodeid + if ((domain != null) && (snode[1] != domain.id)) { func(null, 0, false); return; } // Invalid domain + + // Check that we have permissions for this node. + db.Get(nodeid, function (err, nodes) { + if ((nodes == null) || (nodes.length != 1)) { func(null, 0, false); return; } // No such nodeid + var rights = user.links[nodes[0].meshid]; + if (rights == null) { func(null, 0, false); return; } // No rights to this mesh + func(nodes[0], rights.rights, true); + }); + } + // Returns a list of all meshes that this user has some rights too obj.GetAllMeshWithRights = function (user, rights) { if (typeof user == 'string') { user = obj.users[user]; } @@ -4076,9 +4091,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (typeof command.sessionid != 'string') return; var splitsessionid = command.sessionid.split('/'); // Check that we are in the same domain and the user has rights over this node. - if ((splitsessionid[0] == 'user') && (splitsessionid[1] == domainid)) { + if ((splitsessionid.length == 4) && (splitsessionid[0] == 'user') && (splitsessionid[1] == domainid)) { // Check if this user has rights to get this message - //if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 16) == 0)) return; // TODO!!!!!!!!!!!!!!!!!!!!! + if (obj.GetMeshRights(splitsessionid[0] + '/' + splitsessionid[1] + '/' + splitsessionid[2], meshid) == 0) return; // TODO: Check if this is ok // See if the session is connected. If so, go ahead and send this message to the target node var ws = obj.wssessions2[command.sessionid]; @@ -4101,7 +4116,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // Check that we are in the same domain and the user has rights over this node. if ((splituserid[0] == 'user') && (splituserid[1] == domainid)) { // Check if this user has rights to get this message - //if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 16) == 0)) return; // TODO!!!!!!!!!!!!!!!!!!!!! + if (obj.GetMeshRights(command.userid, meshid) == 0) return; // TODO: Check if this is ok // See if the session is connected var sessions = obj.wssessions[command.userid]; @@ -4120,16 +4135,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { } else { // Route this command to the mesh command.nodeid = nodeid; var cmdstr = JSON.stringify(command); - for (var userid in obj.wssessions) { // Find all connected users for this mesh and send the message - var user = obj.users[userid]; - if ((user != null) && (user.links != null)) { - var rights = user.links[meshid]; - if (rights != null) { // TODO: Look at what rights are needed for message routing - var xsessions = obj.wssessions[userid]; - // Send the message to all users on this server - for (i in xsessions) { try { xsessions[i].send(cmdstr); } catch (e) { } } - } - } + if (obj.GetMeshRights(userid, meshid) == 0) return; // TODO: Check if this is ok + + // Find all connected users for this mesh and send the message + for (var userid in obj.wssessions) { + var xsessions = obj.wssessions[userid]; + // Send the message to all users on this server + for (i in xsessions) { try { xsessions[i].send(cmdstr); } catch (e) { } } } // Send the message to all users of other servers
'; - // Collapsing header & start collapsing area - deviceHeaderId2++; - var collapsed = CollapsedGroups[mesh._id]; - r += ''; // Collapse action + // Collapsing header & start collapsing area + deviceHeaderId2++; + var collapsed = CollapsedGroups[mesh._id]; + r += ''; // Collapse action - r += '' + EscapeHtml(mesh.name) + ''; - r += getMeshActions(mesh, meshrights); - r += '
' + "No Intel® AMT devices in this mesh"; - if ((meshrights & 4) != 0) { r += ', ' + "add one" + ''; } - } - if (mesh.mtype == 2) { - r += '
'; - r += '
'; // Open collapse div - r += '
' + "No devices in this group"; - if ((meshrights & 4) != 0) { r += ', ' + "add one" + ''; } - } - r += '.
' + "No Intel® AMT devices in this mesh"; + if ((meshrights & 4) != 0) { r += ', ' + "add one" + ''; } } + if (mesh.mtype == 2) { + r += '
'; + r += '
'; // Open collapse div + r += '
' + "No devices in this group"; + if ((meshrights & 4) != 0) { r += ', ' + "add one" + ''; } + } + r += '.