More access control improvements, RPi icon.

Ylian Saint-Hilaire committed Dec 27, 2019 at 15:18 UTC c3efdb39c1a7bd6f9e02368704c838ebd2436e45
16 files changed +719 -853
agents/meshcore.js
+2 -5
@@ -1919,13 +1919,10 @@ function createMeshCore(agent) {
1919 switch (cmd) {
1920 case 'help': { // Displays available commands
1921 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';
1922 - if (process.platform == 'win32')
1923 - {
1924 - availcommands += ',safemode,wpfhwacceleration';
1925 - }
1922 + if (process.platform == 'win32') { availcommands += ',safemode,wpfhwacceleration'; }
1923 availcommands = availcommands.split(',').sort();
1924 while (availcommands.length > 0) {
1928 - if (f.length > 100) { fin += (f + ',\r\n'); f = ''; }
1925 + if (f.length > 90) { fin += (f + ',\r\n'); f = ''; }
1926 f += (((f != '') ? ', ' : ' ') + availcommands.shift());
1927 }
1928 if (f != '') { fin += f; }
interceptor.js
+42 -42
@@ -12,10 +12,10 @@
12 /*jshint node: true */
13 /*jshint strict: false */
14 /*jshint esversion: 6 */
15 -"use strict";
15 +'use strict';
16
17 -const crypto = require("crypto");
18 -const common = require("./common.js");
17 +const crypto = require('crypto');
18 +const common = require('./common.js');
19
20 var HttpInterceptorAuthentications = {};
21 //var RedirInterceptorAuthentications = {};
@@ -28,8 +28,8 @@ module.exports.CreateHttpInterceptor = function (args) {
28 obj.randomValueHex = function (len) { return crypto.randomBytes(Math.ceil(len / 2)).toString('hex').slice(0, len); };
29
30 obj.args = args;
31 - obj.amt = { acc: "", mode: 0, count: 0, error: false }; // mode: 0:Header, 1:LengthBody, 2:ChunkedBody, 3:UntilClose
32 - obj.ws = { acc: "", mode: 0, count: 0, error: false, authCNonce: obj.randomValueHex(10), authCNonceCount: 1 };
31 + obj.amt = { acc: '', mode: 0, count: 0, error: false }; // mode: 0:Header, 1:LengthBody, 2:ChunkedBody, 3:UntilClose
32 + obj.ws = { acc: '', mode: 0, count: 0, error: false, authCNonce: obj.randomValueHex(10), authCNonceCount: 1 };
33 obj.blockAmtStorage = false;
34
35 // Private method
@@ -38,7 +38,7 @@ module.exports.CreateHttpInterceptor = function (args) {
38 // Process data coming from Intel AMT
39 obj.processAmtData = function (data) {
40 obj.amt.acc += data; // Add data to accumulator
41 - data = "";
41 + data = '';
42 var datalen = 0;
43 do {
44 datalen = data.length;
@@ -53,7 +53,7 @@ module.exports.CreateHttpInterceptor = function (args) {
53 if (obj.amt.mode == 0) { // Header Mode
54 // Decode the HTTP header
55 headerend = obj.amt.acc.indexOf('\r\n\r\n');
56 - if (headerend < 0) return "";
56 + if (headerend < 0) return '';
57 var headerlines = obj.amt.acc.substring(0, headerend).split('\r\n');
58 obj.amt.acc = obj.amt.acc.substring(headerend + 4);
59 obj.amt.directive = headerlines[0].split(' ');
@@ -98,7 +98,7 @@ module.exports.CreateHttpInterceptor = function (args) {
98 } else if (obj.amt.mode == 2) { // Chunked Body Mode
99 // Send data one chunk at a time
100 headerend = obj.amt.acc.indexOf('\r\n');
101 - if (headerend < 0) return "";
101 + if (headerend < 0) return '';
102 var chunksize = parseInt(obj.amt.acc.substring(0, headerend), 16);
103 if ((chunksize == 0) && (obj.amt.acc.length >= headerend + 4)) {
104 // Send the ending chunk (NOTE: We do not support trailing headers)
@@ -114,16 +114,16 @@ module.exports.CreateHttpInterceptor = function (args) {
114 }
115 } else if (obj.amt.mode == 3) { // Until Close Mode
116 r = obj.amt.acc;
117 - obj.amt.acc = "";
117 + obj.amt.acc = '';
118 return r;
119 }
120 - return "";
120 + return '';
121 };
122
123 // Process data coming from the Browser
124 obj.processBrowserData = function (data) {
125 obj.ws.acc += data; // Add data to accumulator
126 - data = "";
126 + data = '';
127 var datalen = 0;
128 do {
129 datalen = data.length;
@@ -138,7 +138,7 @@ module.exports.CreateHttpInterceptor = function (args) {
138 if (obj.ws.mode == 0) { // Header Mode
139 // Decode the HTTP header
140 headerend = obj.ws.acc.indexOf('\r\n\r\n');
141 - if (headerend < 0) return "";
141 + if (headerend < 0) return '';
142 var headerlines = obj.ws.acc.substring(0, headerend).split('\r\n');
143 obj.ws.acc = obj.ws.acc.substring(headerend + 4);
144 obj.ws.directive = headerlines[0].split(' ');
@@ -199,7 +199,7 @@ module.exports.CreateHttpInterceptor = function (args) {
199 } else if (obj.amt.mode == 2) { // Chunked Body Mode
200 // Send data one chunk at a time
201 headerend = obj.amt.acc.indexOf('\r\n');
202 - if (headerend < 0) return "";
202 + if (headerend < 0) return '';
203 var chunksize = parseInt(obj.amt.acc.substring(0, headerend), 16);
204 if (isNaN(chunksize)) { // TODO: Check this path
205 // Chunk is not in this batch, move one
@@ -226,10 +226,10 @@ module.exports.CreateHttpInterceptor = function (args) {
226 }
227 } else if (obj.ws.mode == 3) { // Until Close Mode
228 r = obj.ws.acc;
229 - obj.ws.acc = "";
229 + obj.ws.acc = '';
230 return r;
231 }
232 - return "";
232 + return '';
233 };
234
235 // Parse authentication values from the HTTP header
@@ -249,9 +249,9 @@ module.exports.CreateHttpInterceptor = function (args) {
249
250 // Compute the MD5 digest hash for a set of values
251 obj.ComputeDigesthash = function (username, password, realm, method, path, qop, nonce, nc, cnonce) {
252 - var ha1 = crypto.createHash('md5').update(username + ":" + realm + ":" + password).digest("hex");
253 - var ha2 = crypto.createHash('md5').update(method + ":" + path).digest("hex");
254 - return crypto.createHash('md5').update(ha1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + ha2).digest("hex");
252 + var ha1 = crypto.createHash('md5').update(username + ':' + realm + ':' + password).digest('hex');
253 + var ha2 = crypto.createHash('md5').update(method + ':' + path).digest('hex');
254 + return crypto.createHash('md5').update(ha1 + ':' + nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + ha2).digest('hex');
255 };
256
257 return obj;
@@ -266,8 +266,8 @@ module.exports.CreateRedirInterceptor = function (args) {
266 obj.randomValueHex = function (len) { return crypto.randomBytes(Math.ceil(len / 2)).toString('hex').slice(0, len); };
267
268 obj.args = args;
269 - obj.amt = { acc: "", mode: 0, count: 0, error: false, direct: false };
270 - obj.ws = { acc: "", mode: 0, count: 0, error: false, direct: false, authCNonce: obj.randomValueHex(10), authCNonceCount: 1 };
269 + obj.amt = { acc: '', mode: 0, count: 0, error: false, direct: false };
270 + obj.ws = { acc: '', mode: 0, count: 0, error: false, direct: false, authCNonce: obj.randomValueHex(10), authCNonceCount: 1 };
271
272 obj.RedirectCommands = { StartRedirectionSession: 0x10, StartRedirectionSessionReply: 0x11, EndRedirectionSession: 0x12, AuthenticateSession: 0x13, AuthenticateSessionReply: 0x14 };
273 obj.StartRedirectionSessionReplyStatus = { SUCCESS: 0, TYPE_UNKNOWN: 1, BUSY: 2, UNSUPPORTED: 3, ERROR: 0xFF };
@@ -280,7 +280,7 @@ module.exports.CreateRedirInterceptor = function (args) {
280 // Process data coming from Intel AMT
281 obj.processAmtData = function (data) {
282 obj.amt.acc += data; // Add data to accumulator
283 - data = "";
283 + data = '';
284 var datalen = 0;
285 do { datalen = data.length; data += obj.processAmtDataEx(); } while (datalen != data.length); // Process as much data as possible
286 return data;
@@ -298,11 +298,11 @@ module.exports.CreateRedirInterceptor = function (args) {
298 //console.log(obj.amt.acc.charCodeAt(0));
299 switch (obj.amt.acc.charCodeAt(0)) {
300 case obj.RedirectCommands.StartRedirectionSessionReply: {
301 - if (obj.amt.acc.length < 4) return "";
301 + if (obj.amt.acc.length < 4) return '';
302 if (obj.amt.acc.charCodeAt(1) == obj.StartRedirectionSessionReplyStatus.SUCCESS) {
303 - if (obj.amt.acc.length < 13) return "";
303 + if (obj.amt.acc.length < 13) return '';
304 var oemlen = obj.amt.acc.charCodeAt(12);
305 - if (obj.amt.acc.length < 13 + oemlen) return "";
305 + if (obj.amt.acc.length < 13 + oemlen) return '';
306 r = obj.amt.acc.substring(0, 13 + oemlen);
307 obj.amt.acc = obj.amt.acc.substring(13 + oemlen);
308 return r;
@@ -310,9 +310,9 @@ module.exports.CreateRedirInterceptor = function (args) {
310 break;
311 }
312 case obj.RedirectCommands.AuthenticateSessionReply: {
313 - if (obj.amt.acc.length < 9) return "";
313 + if (obj.amt.acc.length < 9) return '';
314 var l = common.ReadIntX(obj.amt.acc, 5);
315 - if (obj.amt.acc.length < 9 + l) return "";
315 + if (obj.amt.acc.length < 9 + l) return '';
316 var authstatus = obj.amt.acc.charCodeAt(1);
317 var authType = obj.amt.acc.charCodeAt(4);
318
@@ -337,17 +337,17 @@ module.exports.CreateRedirInterceptor = function (args) {
337 }
338 default: {
339 obj.amt.error = true;
340 - return "";
340 + return '';
341 }
342 }
343 }
344 - return "";
344 + return '';
345 };
346
347 // Process data coming from the Browser
348 obj.processBrowserData = function (data) {
349 obj.ws.acc += data; // Add data to accumulator
350 - data = "";
350 + data = '';
351 var datalen = 0;
352 do { datalen = data.length; data += obj.processBrowserDataEx(); } while (datalen != data.length); // Process as much data as possible
353 return data;
@@ -356,39 +356,39 @@ module.exports.CreateRedirInterceptor = function (args) {
356 // Process data coming from the Browser in the accumulator
357 obj.processBrowserDataEx = function () {
358 var r;
359 - if (obj.ws.acc.length == 0) return "";
359 + if (obj.ws.acc.length == 0) return '';
360 if (obj.ws.direct == true) {
361 var data = obj.ws.acc;
362 - obj.ws.acc = "";
362 + obj.ws.acc = '';
363 return data;
364 } else {
365 switch (obj.ws.acc.charCodeAt(0)) {
366 case obj.RedirectCommands.StartRedirectionSession: {
367 - if (obj.ws.acc.length < 8) return "";
367 + if (obj.ws.acc.length < 8) return '';
368 r = obj.ws.acc.substring(0, 8);
369 obj.ws.acc = obj.ws.acc.substring(8);
370 return r;
371 }
372 case obj.RedirectCommands.EndRedirectionSession: {
373 - if (obj.ws.acc.length < 4) return "";
373 + if (obj.ws.acc.length < 4) return '';
374 r = obj.ws.acc.substring(0, 4);
375 obj.ws.acc = obj.ws.acc.substring(4);
376 return r;
377 }
378 case obj.RedirectCommands.AuthenticateSession: {
379 - if (obj.ws.acc.length < 9) return "";
379 + if (obj.ws.acc.length < 9) return '';
380 var l = common.ReadIntX(obj.ws.acc, 5);
381 - if (obj.ws.acc.length < 9 + l) return "";
381 + if (obj.ws.acc.length < 9 + l) return '';
382
383 var authType = obj.ws.acc.charCodeAt(4);
384 if (authType == obj.AuthenticationType.DIGEST && obj.args.user && obj.args.pass) {
385 - var authurl = "/RedirectionService";
385 + var authurl = '/RedirectionService';
386 if (obj.amt.digestRealm) {
387 // Replace this authentication digest with a server created one
388 // We have everything we need to authenticate
389 var nc = obj.ws.authCNonceCount;
390 obj.ws.authCNonceCount++;
391 - 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);
391 + 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);
392
393 // Replace this authentication digest with a server created one
394 // We have everything we need to authenticate
@@ -434,18 +434,18 @@ module.exports.CreateRedirInterceptor = function (args) {
434 }
435 default: {
436 obj.ws.error = true;
437 - return "";
437 + return '';
438 }
439 }
440 }
441 - return "";
441 + return '';
442 };
443
444 // Compute the MD5 digest hash for a set of values
445 obj.ComputeDigesthash = function (username, password, realm, method, path, qop, nonce, nc, cnonce) {
446 - var ha1 = crypto.createHash('md5').update(username + ":" + realm + ":" + password).digest("hex");
447 - var ha2 = crypto.createHash('md5').update(method + ":" + path).digest("hex");
448 - return crypto.createHash('md5').update(ha1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + ha2).digest("hex");
446 + var ha1 = crypto.createHash('md5').update(username + ':' + realm + ':' + password).digest('hex');
447 + var ha2 = crypto.createHash('md5').update(method + ':' + path).digest('hex');
448 + return crypto.createHash('md5').update(ha1 + ':' + nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + ha2).digest('hex');
449 };
450
451 return obj;
meshrelay.js
+2 -2
@@ -80,7 +80,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
80 var agent = parent.wsagents[command.nodeid];
81 if (agent != null) {
82 // Check if we have permission to send a message to that node
83 - rights = user.links[agent.dbMeshKey];
83 + rights = user.links[agent.dbMeshKey]; // TODO: Need to include user group / node rights
84 mesh = parent.meshes[agent.dbMeshKey];
85 if ((rights != null) && (mesh != null) || ((rights & 16) != 0)) { // TODO: 16 is console permission, may need more gradular permission checking
86 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
98 var routing = parent.parent.GetRoutingServerId(command.nodeid, 1); // 1 = MeshAgent routing type
99 if (routing != null) {
100 // Check if we have permission to send a message to that node
101 - rights = user.links[routing.meshid];
101 + rights = user.links[routing.meshid]; // TODO: Need to include user groups / node rights
102 mesh = parent.meshes[routing.meshid];
103 if (rights != null || ((rights & 16) != 0)) { // TODO: 16 is console permission, may need more gradular permission checking
104 if (ws.sessionId) { command.fromSessionid = ws.sessionId; } // Set the session id, required for responses.
meshuser.js
+377 -591
@@ -152,11 +152,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
152 var agent = parent.wsagents[command.nodeid];
153 if (agent != null) {
154 // Check if we have permission to send a message to that node
155 - var meshrights = parent.GetMeshRights(user, agent.dbMeshKey);
155 + var meshrights = parent.GetMeshRights(user, agent.dbMeshKey); // TODO: We will need to get the rights for this specific node.
156 var mesh = parent.meshes[agent.dbMeshKey];
157 if ((mesh != null) && ((meshrights & MESHRIGHT_REMOTECONTROL) || (meshrights & MESHRIGHT_REMOTEVIEWONLY))) { // 8 is remote control permission, 256 is desktop read only
158 command.sessionid = ws.sessionId; // Set the session id, required for responses
159 - command.rights = meshrights; // Add user rights flags to the message
159 + command.rights = meshrights; // Add user rights flags to the message
160 command.consent = mesh.consent; // Add user consent
161 if (typeof domain.userconsentflags == 'number') { command.consent |= domain.userconsentflags; } // Add server required consent flags
162 command.username = user.name; // Add user name
@@ -503,101 +503,74 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
503 }
504 case 'powertimeline':
505 {
506 - // Perform pre-validation
507 - if (common.validateString(command.nodeid, 0, 128) == false) break;
508 - var snode = command.nodeid.split('/');
509 - if ((snode.length != 3) || (snode[1] != domain.id)) break;
510 -
511 - // Check that we have permissions for this node.
512 - db.Get(command.nodeid, function (err, nodes) {
513 - if (nodes == null || nodes.length != 1) return;
514 - const node = nodes[0];
515 -
516 - if (parent.GetMeshRights(user, node.meshid) != 0) {
517 - // Query the database for the power timeline for a given node
518 - // The result is a compacted array: [ startPowerState, startTimeUTC, powerState ] + many[ deltaTime, powerState ]
519 - db.getPowerTimeline(command.nodeid, function (err, docs) {
520 - if ((err == null) && (docs != null) && (docs.length > 0)) {
521 - var timeline = [], time = null, previousPower;
522 - for (i in docs) {
523 - var doc = docs[i], j = parseInt(i);
524 - doc.time = Date.parse(doc.time);
525 - if (time == null) { // First element
526 - // Skip all starting power 0 events.
527 - if ((doc.power == 0) && ((doc.oldPower == null) || (doc.oldPower == 0))) continue;
528 - time = doc.time;
529 - if (doc.oldPower) { timeline.push(doc.oldPower, time / 1000, doc.power); } else { timeline.push(0, time / 1000, doc.power); }
530 - } else if (previousPower != doc.power) { // Delta element
531 - // If this event is of a short duration (2 minutes or less), skip it.
532 - if ((docs.length > (j + 1)) && ((Date.parse(docs[j + 1].time) - doc.time) < 120000)) continue;
533 - timeline.push((doc.time - time) / 1000, doc.power);
534 - time = doc.time;
535 - }
536 - previousPower = doc.power;
506 + // Get the node and the rights for this node
507 + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
508 + if (visible == false) return;
509 + // Query the database for the power timeline for a given node
510 + // The result is a compacted array: [ startPowerState, startTimeUTC, powerState ] + many[ deltaTime, powerState ]
511 + db.getPowerTimeline(node._id, function (err, docs) {
512 + if ((err == null) && (docs != null) && (docs.length > 0)) {
513 + var timeline = [], time = null, previousPower;
514 + for (i in docs) {
515 + var doc = docs[i], j = parseInt(i);
516 + doc.time = Date.parse(doc.time);
517 + if (time == null) { // First element
518 + // Skip all starting power 0 events.
519 + if ((doc.power == 0) && ((doc.oldPower == null) || (doc.oldPower == 0))) continue;
520 + time = doc.time;
521 + if (doc.oldPower) { timeline.push(doc.oldPower, time / 1000, doc.power); } else { timeline.push(0, time / 1000, doc.power); }
522 + } else if (previousPower != doc.power) { // Delta element
523 + // If this event is of a short duration (2 minutes or less), skip it.
524 + if ((docs.length > (j + 1)) && ((Date.parse(docs[j + 1].time) - doc.time) < 120000)) continue;
525 + timeline.push((doc.time - time) / 1000, doc.power);
526 + time = doc.time;
527 }
538 - try { ws.send(JSON.stringify({ action: 'powertimeline', nodeid: command.nodeid, timeline: timeline, tag: command.tag })); } catch (ex) { }
539 - } else {
540 - // No records found, send current state if we have it
541 - var state = parent.parent.GetConnectivityState(command.nodeid);
542 - 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) { } }
528 + previousPower = doc.power;
529 }
544 - });
545 - }
530 + try { ws.send(JSON.stringify({ action: 'powertimeline', nodeid: node._id, timeline: timeline, tag: command.tag })); } catch (ex) { }
531 + } else {
532 + // No records found, send current state if we have it
533 + var state = parent.parent.GetConnectivityState(command.nodeid);
534 + 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) { } }
535 + }
536 + });
537 });
538 break;
539 }
540 case 'getsysinfo':
541 {
551 - // Perform pre-validation
552 - if (common.validateString(command.nodeid, 0, 128) == false) break;
553 - var snode = command.nodeid.split('/');
554 - if ((snode.length != 3) || (snode[1] != domain.id)) break;
555 -
556 - // Check that we have permissions for this node.
557 - db.Get(command.nodeid, function (err, nodes) {
558 - if (nodes == null || nodes.length != 1) return;
559 - const node = nodes[0];
560 -
561 - if (parent.GetMeshRights(user, node.meshid) != 0) {
562 - // Query the database system information
563 - db.Get('si' + command.nodeid, function (err, docs) {
564 - if ((docs != null) && (docs.length > 0)) {
565 - var doc = docs[0];
566 - doc.action = 'getsysinfo';
567 - doc.nodeid = command.nodeid;
568 - doc.tag = command.tag;
569 - delete doc.type;
570 - delete doc.domain;
571 - delete doc._id;
572 - try { ws.send(JSON.stringify(doc)); } catch (ex) { }
573 - } else {
574 - try { ws.send(JSON.stringify({ action: 'getsysinfo', nodeid: command.nodeid, tag: command.tag, noinfo: true })); } catch (ex) { }
575 - }
576 - });
577 - }
542 + // Get the node and the rights for this node
543 + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
544 + if (visible == false) return;
545 + // Query the database system information
546 + db.Get('si' + command.nodeid, function (err, docs) {
547 + if ((docs != null) && (docs.length > 0)) {
548 + var doc = docs[0];
549 + doc.action = 'getsysinfo';
550 + doc.nodeid = node._id;
551 + doc.tag = command.tag;
552 + delete doc.type;
553 + delete doc.domain;
554 + delete doc._id;
555 + try { ws.send(JSON.stringify(doc)); } catch (ex) { }
556 + } else {
557 + try { ws.send(JSON.stringify({ action: 'getsysinfo', nodeid: node._id, tag: command.tag, noinfo: true })); } catch (ex) { }
558 + }
559 + });
560 });
561 break;
562 }
563 case 'lastconnect':
564 {
583 - // Perform pre-validation
584 - if (common.validateString(command.nodeid, 0, 128) == false) return;
585 - var snode = command.nodeid.split('/');
586 - if ((snode.length != 3) || (snode[1] != domain.id)) break;
587 -
588 - // Check that we have permissions for this node.
589 - db.Get(command.nodeid, function (err, nodes) {
590 - if (nodes == null || nodes.length != 1) return;
591 - const node = nodes[0];
592 -
593 - if (parent.GetMeshRights(user, node.meshid) != 0) {
594 - // Query the database for the last time this node connected
595 - db.Get('lc' + command.nodeid, function (err, docs) {
596 - if ((docs != null) && (docs.length > 0)) {
597 - try { ws.send(JSON.stringify({ action: 'lastconnect', nodeid: command.nodeid, time: docs[0].time, addr: docs[0].addr })); } catch (ex) { }
598 - }
599 - });
600 - }
565 + // Get the node and the rights for this node
566 + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
567 + if (visible == false) return;
568 + // Query the database for the last time this node connected
569 + db.Get('lc' + command.nodeid, function (err, docs) {
570 + if ((docs != null) && (docs.length > 0)) {
571 + try { ws.send(JSON.stringify({ action: 'lastconnect', nodeid: command.nodeid, time: docs[0].time, addr: docs[0].addr })); } catch (ex) { }
572 + }
573 + });
574 });
575 break;
576 }
@@ -963,31 +936,27 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
936 try { ws.send(JSON.stringify({ action: 'events', events: docs, user: command.user, tag: command.tag })); } catch (ex) { }
937 });
938 }
966 - } else if (common.validateString(command.nodeid, 0, 128) == true) { // Device filtered events
939 + } else if (command.nodeid != null) { // Device filtered events
940 // Check that the user has access to this nodeid
968 - db.Get(command.nodeid, function (err, nodes) {
969 - if ((nodes == null) || (nodes.length != 1)) return;
970 - const node = nodes[0];
971 -
972 - var meshrights = parent.GetMeshRights(user, node.meshid);
973 - if (meshrights != 0) {
974 - // Put a limit on the number of returned entries if present
975 - var limit = 10000;
976 - if (common.validateInt(command.limit, 1, 60000) == true) { limit = command.limit; }
977 -
978 - if ((meshrights & MESHRIGHT_LIMITEVENTS) != 0) {
979 - // Send the list of most recent events for this nodeid that only apply to us, up to 'limit' count
980 - db.GetNodeEventsSelfWithLimit(command.nodeid, domain.id, user._id, limit, function (err, docs) {
981 - if (err != null) return;
982 - try { ws.send(JSON.stringify({ action: 'events', events: docs, nodeid: command.nodeid, tag: command.tag })); } catch (ex) { }
983 - });
984 - } else {
985 - // Send the list of most recent events for this nodeid, up to 'limit' count
986 - db.GetNodeEventsWithLimit(command.nodeid, domain.id, limit, function (err, docs) {
987 - if (err != null) return;
988 - try { ws.send(JSON.stringify({ action: 'events', events: docs, nodeid: command.nodeid, tag: command.tag })); } catch (ex) { }
989 - });
990 - }
941 + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
942 + if (node == null) return;
943 +
944 + // Put a limit on the number of returned entries if present
945 + var limit = 10000;
946 + if (common.validateInt(command.limit, 1, 60000) == true) { limit = command.limit; }
947 +
948 + if ((rights & MESHRIGHT_LIMITEVENTS) != 0) {
949 + // Send the list of most recent events for this nodeid that only apply to us, up to 'limit' count
950 + db.GetNodeEventsSelfWithLimit(node._id, domain.id, user._id, limit, function (err, docs) {
951 + if (err != null) return;
952 + try { ws.send(JSON.stringify({ action: 'events', events: docs, nodeid: node._id, tag: command.tag })); } catch (ex) { }
953 + });
954 + } else {
955 + // Send the list of most recent events for this nodeid, up to 'limit' count
956 + db.GetNodeEventsWithLimit(node._id, domain.id, limit, function (err, docs) {
957 + if (err != null) return;
958 + try { ws.send(JSON.stringify({ action: 'events', events: docs, nodeid: node._id, tag: command.tag })); } catch (ex) { }
959 + });
960 }
961 });
962 } else {
@@ -997,7 +966,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
966 // All events
967 var exGroupFilter2 = [], filter = [], filter2 = user.subscriptions;
968
1000 - // Remove MeshID's that we do not have rights to see events for (TODO: user groups)
969 + // Add all meshes for groups this user is part of
970 + // TODO (UserGroups)
971 +
972 + // Remove MeshID's that we do not have rights to see events for
973 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); } }
974 for (var i in filter2) { if (exGroupFilter2.indexOf(filter2[i]) == -1) { filter.push(filter2[i]); } }
975
@@ -1198,7 +1170,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
1170 }
1171 }
1172
1201 - // TODO: Remove user groups??
1173 + // TODO (UserGroups): Remove user groups??
1174
1175 db.Remove('ws' + deluser._id); // Remove user web state
1176 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
1479 if (common.validateString(command.meshid, 1, 1024) == false) { err = 'Invalid group identifier'; } // Check the meshid
1480 else if (command.meshid.indexOf('/') == -1) { command.meshid = 'mesh/' + domain.id + '/' + command.meshid; }
1481 if (common.validateInt(command.notify) == false) { err = 'Invalid notification flags'; }
1510 - if (parent.GetMeshRights(user, command.meshid) == 0) err = 'Access denied';
1482 + if (parent.IsMeshViewable(user, command.meshid) == false) err = 'Access denied';
1483 } catch (ex) { err = 'Validation exception: ' + ex; }
1484
1485 // Handle any errors
1486 if (err != null) { if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'changemeshnotify', responseid: command.responseid, result: err })); } catch (ex) { } } break; }
1487
1488 // Change the notification (TODO: Add user group support, not sure how to do this here)
1517 - if (command.notify == 0) {
1518 - delete user.links[command.meshid].notify;
1519 - } else {
1520 - user.links[command.meshid].notify = command.notify;
1489 + // TODO (UserGroups)
1490 + if (user.links[command.meshid]) {
1491 + if (command.notify == 0) {
1492 + delete user.links[command.meshid].notify;
1493 + } else {
1494 + user.links[command.meshid].notify = command.notify;
1495 + }
1496 }
1497
1498 // Save the user
@@ -1674,33 +1649,23 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
1649 }
1650 }
1651
1677 - // Setup a user-to-node session
1678 - if (common.validateString(command.nodeid, 1, 2048)) {
1679 - 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??
1680 -
1681 - // Get the device
1682 - db.Get(command.nodeid, function (err, nodes) {
1683 - if ((nodes == null) || (nodes.length != 1)) return;
1684 - var node = nodes[0];
1685 -
1686 - // Get the mesh for this device
1687 - mesh = parent.meshes[node.meshid];
1688 - if (mesh) {
1689 - // Check if this user has rights to do this
1690 - if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_CHATNOTIFY) == 0) return;
1652 + // User-to-device chat is not support in LAN-only mode yet. We need the agent to replace the IP address of the server??
1653 + if (args.lanonly == true) { return; }
1654
1692 - // Create the server url
1693 - var httpsPort = ((args.aliasport == null) ? args.port : args.aliasport); // Use HTTPS alias port is specified
1694 - var xdomain = (domain.dns == null) ? domain.id : '';
1695 - if (xdomain != '') xdomain += "/";
1696 - var url = "http" + (args.notls ? '' : 's') + "://" + parent.getWebServerName(domain) + ":" + httpsPort + "/" + xdomain + "messenger?id=meshmessenger/" + encodeURIComponent(command.nodeid) + "/" + encodeURIComponent(user._id) + "&title=" + encodeURIComponent(user.name);
1655 + // Setup a user-to-node session
1656 + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
1657 + // Check if this user has rights to do this
1658 + if ((rights & MESHRIGHT_CHATNOTIFY) == 0) return;
1659
1698 - // Create the notification message
1699 - routeCommandToNode({ "action": "openUrl", "nodeid": command.nodeid, "userid": user._id, "username": user.name, "url": url });
1700 - }
1701 - });
1702 - }
1660 + // Create the server url
1661 + var httpsPort = ((args.aliasport == null) ? args.port : args.aliasport); // Use HTTPS alias port is specified
1662 + var xdomain = (domain.dns == null) ? domain.id : '';
1663 + if (xdomain != '') xdomain += "/";
1664 + var url = "http" + (args.notls ? '' : 's') + "://" + parent.getWebServerName(domain) + ":" + httpsPort + "/" + xdomain + "messenger?id=meshmessenger/" + encodeURIComponent(command.nodeid) + "/" + encodeURIComponent(user._id) + "&title=" + encodeURIComponent(user.name);
1665
1666 + // Create the notification message
1667 + routeCommandToNode({ "action": "openUrl", "nodeid": command.nodeid, "userid": user._id, "username": user.name, "url": url });
1668 + });
1669 break;
1670 }
1671 case 'serverversion':
@@ -2084,20 +2049,17 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2049
2050 // For each nodeid, change the group
2051 for (var i = 0; i < command.nodeids.length; i++) {
2087 - db.Get(command.nodeids[i], function (err, nodes) {
2088 - if ((nodes == null) || (nodes.length != 1)) return;
2089 - const node = nodes[0];
2090 -
2052 + // Get the node and the rights for this node
2053 + parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) {
2054 // Check if already in the right mesh
2092 - if (node.meshid == command.meshid) return;
2055 + if ((node == null) || (node.meshid == command.meshid)) return;
2056
2057 // Make sure both source and target mesh are the same type
2058 try { if (parent.meshes[node.meshid].mtype != parent.meshes[command.meshid].mtype) return; } catch (e) { return; };
2059
2060 // Make sure that we have rights on both source and destination mesh
2098 - const sourceMeshRights = parent.GetMeshRights(user, node.meshid);
2061 const targetMeshRights = parent.GetMeshRights(user, command.meshid);
2100 - if (((sourceMeshRights & MESHRIGHT_MANAGECOMPUTERS) == 0) || ((targetMeshRights & MESHRIGHT_MANAGECOMPUTERS) == 0)) return;
2062 + if (((rights & MESHRIGHT_MANAGECOMPUTERS) == 0) || ((targetMeshRights & MESHRIGHT_MANAGECOMPUTERS) == 0)) return;
2063
2064 // Perform the switch, start by saving the node with the new meshid.
2065 const oldMeshId = node.meshid;
@@ -2139,146 +2101,103 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2101 case 'removedevices':
2102 {
2103 if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
2142 -
2104 for (i in command.nodeids) {
2144 - nodeid = command.nodeids[i];
2145 - if (common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid
2146 - if ((nodeid.split('/').length != 3) || (nodeid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
2147 -
2148 - // Get the device
2149 - db.Get(nodeid, function (err, nodes) {
2150 - if ((nodes == null) || (nodes.length != 1)) return;
2151 - var node = nodes[0];
2152 -
2153 - // Get the mesh for this device
2154 - mesh = parent.meshes[node.meshid];
2155 - if (mesh) {
2156 - // Check if this user has rights to do this
2157 - if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_MANAGECOMPUTERS) == 0) return;
2158 -
2159 - // Delete this node including network interface information, events and timeline
2160 - db.Remove(node._id); // Remove node with that id
2161 - db.Remove('if' + node._id); // Remove interface information
2162 - db.Remove('nt' + node._id); // Remove notes
2163 - db.Remove('lc' + node._id); // Remove last connect time
2164 - db.Remove('si' + node._id); // Remove system information
2165 - db.RemoveSMBIOS(node._id); // Remove SMBios data
2166 - db.RemoveAllNodeEvents(node._id); // Remove all events for this node
2167 - db.removeAllPowerEventsForNode(node._id); // Remove all power events for this node
2168 - db.Get('ra' + obj.dbNodeKey, function (err, nodes) {
2169 - if ((nodes != null) && (nodes.length == 1)) { db.Remove('da' + nodes[0].daid); } // Remove diagnostic agent to real agent link
2170 - db.Remove('ra' + node._id); // Remove real agent to diagnostic agent link
2171 - });
2105 + // Get the node and the rights for this node
2106 + parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) {
2107 + // Check we have the rights to delete this device
2108 + if ((rights & MESHRIGHT_MANAGECOMPUTERS) == 0) return;
2109 +
2110 + // Delete this node including network interface information, events and timeline
2111 + db.Remove(node._id); // Remove node with that id
2112 + db.Remove('if' + node._id); // Remove interface information
2113 + db.Remove('nt' + node._id); // Remove notes
2114 + db.Remove('lc' + node._id); // Remove last connect time
2115 + db.Remove('si' + node._id); // Remove system information
2116 + db.RemoveSMBIOS(node._id); // Remove SMBios data
2117 + db.RemoveAllNodeEvents(node._id); // Remove all events for this node
2118 + db.removeAllPowerEventsForNode(node._id); // Remove all power events for this node
2119 + db.Get('ra' + obj.dbNodeKey, function (err, nodes) {
2120 + if ((nodes != null) && (nodes.length == 1)) { db.Remove('da' + nodes[0].daid); } // Remove diagnostic agent to real agent link
2121 + db.Remove('ra' + node._id); // Remove real agent to diagnostic agent link
2122 + });
2123
2173 - // Event node deletion
2174 - 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 };
2175 - // TODO: We can't use the changeStream for node delete because we will not know the meshid the device was in.
2176 - //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.
2177 - parent.parent.DispatchEvent(['*', node.meshid], obj, event);
2178 -
2179 - // Disconnect all connections if needed
2180 - var state = parent.parent.GetConnectivityState(nodeid);
2181 - if ((state != null) && (state.connectivity != null)) {
2182 - if ((state.connectivity & 1) != 0) { parent.wsagents[nodeid].close(); } // Disconnect mesh agent
2183 - if ((state.connectivity & 2) != 0) { parent.parent.mpsserver.close(parent.parent.mpsserver.ciraConnections[nodeid]); } // Disconnect CIRA connection
2184 - }
2124 + // Event node deletion
2125 + 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 };
2126 + // TODO: We can't use the changeStream for node delete because we will not know the meshid the device was in.
2127 + //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.
2128 + parent.parent.DispatchEvent(['*', node.meshid], obj, event);
2129 +
2130 + // Disconnect all connections if needed
2131 + var state = parent.parent.GetConnectivityState(nodeid);
2132 + if ((state != null) && (state.connectivity != null)) {
2133 + if ((state.connectivity & 1) != 0) { parent.wsagents[nodeid].close(); } // Disconnect mesh agent
2134 + if ((state.connectivity & 2) != 0) { parent.parent.mpsserver.close(parent.parent.mpsserver.ciraConnections[nodeid]); } // Disconnect CIRA connection
2135 }
2136 });
2137 }
2188 -
2138 break;
2139 }
2140 case 'wakedevices':
2141 {
2193 - if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
2142 // TODO: We can optimize this a lot.
2143 // - We should get a full list of all MAC's to wake first.
2144 // - We should try to only have one agent per subnet (using Gateway MAC) send a wake-on-lan.
2145 + if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
2146 for (i in command.nodeids) {
2198 - nodeid = command.nodeids[i];
2199 - var wakeActions = 0;
2200 - if (common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid
2201 - if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain
2202 - // Get the device
2203 - db.Get(nodeid, function (err, nodes) {
2204 - if ((nodes == null) || (nodes.length != 1)) return;
2205 - var node = nodes[0];
2206 -
2207 - // Get the mesh for this device
2208 - mesh = parent.meshes[node.meshid];
2209 - if (mesh) {
2210 -
2211 - // Check if this user has rights to do this
2212 - if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_WAKEDEVICE) != 0) {
2213 -
2214 - // If this device is connected on MQTT, send a wake action.
2215 - if (parent.parent.mqttbroker != null) { parent.parent.mqttbroker.publish(node._id, 'powerAction', 'wake'); }
2216 -
2217 - // Get the device interface information
2218 - db.Get('if' + node._id, function (err, nodeifs) {
2219 - if ((nodeifs != null) && (nodeifs.length == 1)) {
2220 - var nodeif = nodeifs[0];
2221 - var macs = [];
2222 - for (var i in nodeif.netif) { if (nodeif.netif[i].mac) { macs.push(nodeif.netif[i].mac); } }
2223 -
2224 - // Have the server send a wake-on-lan packet (Will not work in WAN-only)
2225 - if (parent.parent.meshScanner != null) { parent.parent.meshScanner.wakeOnLan(macs); wakeActions++; }
2226 -
2227 - // Get the list of mesh this user as access to
2228 - var targetMeshes = [];
2229 - for (i in user.links) { targetMeshes.push(i); } // TODO: Include used security groups!!
2230 -
2231 - // Go thru all the connected agents and send wake-on-lan on all the ones in the target mesh list
2232 - for (i in parent.wsagents) {
2233 - var agent = parent.wsagents[i];
2234 - if ((targetMeshes.indexOf(agent.dbMeshKey) >= 0) && (agent.authenticated == 2)) {
2235 - //console.log('Asking agent ' + agent.dbNodeKey + ' to wake ' + macs.join(','));
2236 - try { agent.send(JSON.stringify({ action: 'wakeonlan', macs: macs })); } catch (ex) { }
2237 - wakeActions++;
2238 - }
2239 - }
2240 - }
2241 - });
2242 -
2147 + // Get the node and the rights for this node
2148 + parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) {
2149 + // Check we have the rights to delete this device
2150 + if ((rights & MESHRIGHT_WAKEDEVICE) == 0) return;
2151 +
2152 + // If this device is connected on MQTT, send a wake action.
2153 + if (parent.parent.mqttbroker != null) { parent.parent.mqttbroker.publish(node._id, 'powerAction', 'wake'); }
2154 +
2155 + // Get the device interface information
2156 + db.Get('if' + node._id, function (err, nodeifs) {
2157 + if ((nodeifs != null) && (nodeifs.length == 1)) {
2158 + var macs = [], nodeif = nodeifs[0];
2159 + for (var i in nodeif.netif) { if (nodeif.netif[i].mac) { macs.push(nodeif.netif[i].mac); } }
2160 +
2161 + // Have the server send a wake-on-lan packet (Will not work in WAN-only)
2162 + if (parent.parent.meshScanner != null) { parent.parent.meshScanner.wakeOnLan(macs); }
2163 +
2164 + // Get the list of mesh this user as access to
2165 + var targetMeshes = [];
2166 + for (i in user.links) { targetMeshes.push(i); } // TODO: Include used security groups!!
2167 +
2168 + // Go thru all the connected agents and send wake-on-lan on all the ones in the target mesh list
2169 + for (i in parent.wsagents) {
2170 + var agent = parent.wsagents[i];
2171 + if ((targetMeshes.indexOf(agent.dbMeshKey) >= 0) && (agent.authenticated == 2)) {
2172 + //console.log('Asking agent ' + agent.dbNodeKey + ' to wake ' + macs.join(','));
2173 + try { agent.send(JSON.stringify({ action: 'wakeonlan', macs: macs })); } catch (ex) { }
2174 + }
2175 }
2176 }
2177 });
2246 - }
2178 + });
2179 // Confirm we may be doing something (TODO)
2180 try { ws.send(JSON.stringify({ action: 'wakedevices' })); } catch (ex) { }
2181 }
2250 -
2182 break;
2183 }
2184 case 'uninstallagent':
2185 {
2186 if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
2187 for (i in command.nodeids) {
2257 - nodeid = command.nodeids[i];
2258 - if (common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid
2259 - if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain
2260 - // Get the device
2261 - db.Get(nodeid, function (err, nodes) {
2262 - if ((nodes == null) || (nodes.length != 1)) return;
2263 - var node = nodes[0];
2264 -
2265 - // Get the mesh for this device
2266 - mesh = parent.meshes[node.meshid];
2267 - if (mesh) {
2268 - // Check if this user has rights to do this
2269 - if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_UNINSTALL) != 0) {
2270 - // Send uninstall command to connected agent
2271 - var agent = parent.wsagents[node._id];
2272 - if (agent != null) {
2273 - //console.log('Asking agent ' + agent.dbNodeKey + ' to uninstall.');
2274 - try { agent.send(JSON.stringify({ action: 'uninstallagent' })); } catch (ex) { }
2275 - }
2276 - }
2277 - }
2278 - });
2279 - }
2188 + // Get the node and the rights for this node
2189 + parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) {
2190 + // Check we have the rights to delete this device
2191 + if ((rights & MESHRIGHT_UNINSTALL) == 0) return;
2192 +
2193 + // Send uninstall command to connected agent
2194 + const agent = parent.wsagents[node._id];
2195 + if (agent != null) {
2196 + //console.log('Asking agent ' + agent.dbNodeKey + ' to uninstall.');
2197 + try { agent.send(JSON.stringify({ action: 'uninstallagent' })); } catch (ex) { }
2198 + }
2199 + });
2200 }
2281 -
2201 break;
2202 }
2203 case 'poweraction':
@@ -2286,34 +2205,21 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2205 if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
2206 if (common.validateInt(command.actiontype, 2, 4) == false) break; // Check actiontype
2207 for (i in command.nodeids) {
2289 - nodeid = command.nodeids[i];
2290 - var powerActions = 0;
2291 - if (common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid
2292 - if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain
2293 - // Get the device
2294 - db.Get(nodeid, function (err, nodes) {
2295 - if ((nodes == null) || (nodes.length != 1)) return;
2296 - var node = nodes[0];
2297 -
2298 - // Get the mesh for this device
2299 - mesh = parent.meshes[node.meshid];
2300 - if (mesh) {
2301 - // If this device is connected on MQTT, send a power action.
2302 - if (parent.parent.mqttbroker != null) { parent.parent.mqttbroker.publish(nodeid, 'powerAction', ['', '', 'poweroff', 'reset', 'sleep'][command.actiontype]); }
2303 -
2304 - // Check if this user has rights to do this
2305 - if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_REMOTECONTROL) != 0) { // "Remote Control permission"
2306 - // Get this device
2307 - var agent = parent.wsagents[node._id];
2308 - if (agent != null) {
2309 - // Send the power command
2310 - try { agent.send(JSON.stringify({ action: 'poweraction', actiontype: command.actiontype })); } catch (ex) { }
2311 - powerActions++;
2312 - }
2313 - }
2314 - }
2315 - });
2316 - }
2208 + // Get the node and the rights for this node
2209 + parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) {
2210 + // Check we have the rights to delete this device
2211 + if ((rights & MESHRIGHT_REMOTECONTROL) == 0) return;
2212 +
2213 + // If this device is connected on MQTT, send a power action.
2214 + if (parent.parent.mqttbroker != null) { parent.parent.mqttbroker.publish(node._id, 'powerAction', ['', '', 'poweroff', 'reset', 'sleep'][command.actiontype]); }
2215 +
2216 + // Get this device and send the power command
2217 + const agent = parent.wsagents[node._id];
2218 + if (agent != null) {
2219 + try { agent.send(JSON.stringify({ action: 'poweraction', actiontype: command.actiontype })); } catch (ex) { }
2220 + }
2221 + });
2222 +
2223 // Confirm we may be doing something (TODO)
2224 try { ws.send(JSON.stringify({ action: 'poweraction' })); } catch (ex) { }
2225 }
@@ -2325,30 +2231,17 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2231 if (common.validateString(command.title, 1, 512) == false) break; // Check title
2232 if (common.validateString(command.msg, 1, 4096) == false) break; // Check message
2233 for (i in command.nodeids) {
2328 - nodeid = command.nodeids[i];
2329 - var powerActions = 0;
2330 - if (common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid
2331 - if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain
2332 - // Get the device
2333 - db.Get(nodeid, function (err, nodes) {
2334 - if ((nodes == null) || (nodes.length != 1)) return;
2335 - var node = nodes[0];
2336 -
2337 - // Get the mesh for this device
2338 - mesh = parent.meshes[node.meshid];
2339 - if (mesh) {
2340 - // Check if this user has rights to do this
2341 - if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_CHATNOTIFY) != 0) {
2342 - // Get this device
2343 - var agent = parent.wsagents[node._id];
2344 - if (agent != null) {
2345 - // Send the power command
2346 - try { agent.send(JSON.stringify({ action: 'toast', title: command.title, msg: command.msg, sessionid: ws.sessionId, username: user.name, userid: user._id })); } catch (ex) { }
2347 - }
2348 - }
2349 - }
2350 - });
2351 - }
2234 + // Get the node and the rights for this node
2235 + parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) {
2236 + // Check we have the rights to delete this device
2237 + if ((rights & MESHRIGHT_CHATNOTIFY) == 0) return;
2238 +
2239 + // Get this device and send toast command
2240 + const agent = parent.wsagents[node._id];
2241 + if (agent != null) {
2242 + try { agent.send(JSON.stringify({ action: 'toast', title: command.title, msg: command.msg, sessionid: ws.sessionId, username: user.name, userid: user._id })); } catch (ex) { }
2243 + }
2244 + });
2245 }
2246 break;
2247 }
@@ -2358,24 +2251,16 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2251 if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
2252 if ((command.nodeid.split('/').length != 3) || (command.nodeid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
2253
2361 - // Get the device
2362 - db.Get(command.nodeid, function (err, nodes) {
2363 - if ((nodes == null) || (nodes.length != 1)) { try { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: command.nodeid, netif: null })); } catch (ex) { } return; }
2364 - var node = nodes[0];
2365 -
2366 - // Get the mesh for this device
2367 - mesh = parent.meshes[node.meshid];
2368 - if (mesh) {
2369 - // Check if this user has rights to do this
2370 - if (parent.GetMeshRights(user, mesh) == 0) { try { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: command.nodeid, netif: null })); } catch (ex) { } return; }
2254 + // Get the node and the rights for this node
2255 + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
2256 + if (visible == false) return;
2257
2372 - // Get network information about this node
2373 - db.Get('if' + command.nodeid, function (err, netinfos) {
2374 - if ((netinfos == null) || (netinfos.length != 1)) { try { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: command.nodeid, netif: null })); } catch (ex) { } return; }
2375 - var netinfo = netinfos[0];
2376 - try { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: command.nodeid, updateTime: netinfo.updateTime, netif: netinfo.netif })); } catch (ex) { }
2377 - });
2378 - }
2258 + // Get network information about this node
2259 + db.Get('if' + node._id, function (err, netinfos) {
2260 + if ((netinfos == null) || (netinfos.length != 1)) { try { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: node._id, netif: null })); } catch (ex) { } return; }
2261 + var netinfo = netinfos[0];
2262 + try { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: node._id, updateTime: netinfo.updateTime, netif: netinfo.netif })); } catch (ex) { }
2263 + });
2264 });
2265 break;
2266 }
@@ -2383,106 +2268,89 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2268 {
2269 // Argument validation
2270 if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
2386 - if ((command.nodeid.split('/').length != 3) || (command.nodeid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
2271 if ((command.userloc) && (command.userloc.length != 2) && (command.userloc.length != 0)) return;
2272
2389 - // Change the device
2390 - db.Get(command.nodeid, function (err, nodes) {
2391 - if ((nodes == null) || (nodes.length != 1)) return;
2392 - var node = nodes[0];
2273 + // Get the node and the rights for this node
2274 + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
2275 + if ((rights & MESHRIGHT_MANAGECOMPUTERS) == 0) return;
2276 + var mesh = parent.meshes[node.meshid];
2277
2394 - // Get the mesh for this device
2395 - mesh = parent.meshes[node.meshid];
2396 - if (mesh) {
2397 - // Check if this user has rights to do this
2398 - if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_MANAGECOMPUTERS) == 0) return;
2399 -
2400 - // Ready the node change event
2401 - var changes = [], event = { etype: 'node', userid: user._id, username: user.name, action: 'changenode', nodeid: node._id, domain: domain.id };
2402 - change = 0;
2403 - event.msg = ": ";
2404 -
2405 - // If we are in WAN-only mode, host is not used
2406 - if ((args.wanonly == true) && (command.host)) { delete command.host; }
2407 -
2408 - // Look for a change
2409 - if (command.icon && (command.icon != node.icon)) { change = 1; node.icon = command.icon; changes.push('icon'); }
2410 - if (command.name && (command.name != node.name)) { change = 1; node.name = command.name; changes.push('name'); }
2411 - if (command.host && (command.host != node.host)) { change = 1; node.host = command.host; changes.push('host'); }
2412 - if (domain.geolocation && command.userloc && ((node.userloc == null) || (command.userloc[0] != node.userloc[0]) || (command.userloc[1] != node.userloc[1]))) {
2413 - change = 1;
2414 - if ((command.userloc.length == 0) && (node.userloc)) {
2415 - delete node.userloc;
2416 - changes.push('location removed');
2417 - } else {
2418 - command.userloc.push((Math.floor((new Date()) / 1000)));
2419 - node.userloc = command.userloc.join(',');
2420 - changes.push('location');
2421 - }
2422 - }
2423 - if (command.desc != null && (command.desc != node.desc)) { change = 1; node.desc = command.desc; changes.push('description'); }
2424 - if (command.intelamt != null) {
2425 - 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'); }
2426 - if (command.intelamt.tls && (command.intelamt.tls != node.intelamt.tls)) { change = 1; node.intelamt.tls = command.intelamt.tls; changes.push('Intel AMT TLS'); }
2278 + // Ready the node change event
2279 + var changes = [], event = { etype: 'node', userid: user._id, username: user.name, action: 'changenode', nodeid: node._id, domain: domain.id };
2280 + change = 0;
2281 + event.msg = ': ';
2282 +
2283 + // If we are in WAN-only mode, host is not used
2284 + if ((args.wanonly == true) && (command.host)) { delete command.host; }
2285 +
2286 + // Look for a change
2287 + if (command.icon && (command.icon != node.icon)) { change = 1; node.icon = command.icon; changes.push('icon'); }
2288 + if (command.name && (command.name != node.name)) { change = 1; node.name = command.name; changes.push('name'); }
2289 + if (command.host && (command.host != node.host)) { change = 1; node.host = command.host; changes.push('host'); }
2290 + if (domain.geolocation && command.userloc && ((node.userloc == null) || (command.userloc[0] != node.userloc[0]) || (command.userloc[1] != node.userloc[1]))) {
2291 + change = 1;
2292 + if ((command.userloc.length == 0) && (node.userloc)) {
2293 + delete node.userloc;
2294 + changes.push('location removed');
2295 + } else {
2296 + command.userloc.push((Math.floor((new Date()) / 1000)));
2297 + node.userloc = command.userloc.join(',');
2298 + changes.push('location');
2299 }
2428 - if (command.tags) { // Node grouping tag, this is a array of strings that can't be empty and can't contain a comma
2429 - var ok = true, group2 = [];
2430 - if (common.validateString(command.tags, 0, 4096) == true) { command.tags = command.tags.split(','); }
2431 - 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); } }
2432 - group2.sort();
2433 - if (node.tags != group2) { node.tags = group2; change = 1; }
2434 - } else if ((command.tags === '') && node.tags) { delete node.tags; change = 1; }
2300 + }
2301 + if (command.desc != null && (command.desc != node.desc)) { change = 1; node.desc = command.desc; changes.push('description'); }
2302 + if (command.intelamt != null) {
2303 + 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'); }
2304 + if (command.intelamt.tls && (command.intelamt.tls != node.intelamt.tls)) { change = 1; node.intelamt.tls = command.intelamt.tls; changes.push('Intel AMT TLS'); }
2305 + }
2306 + if (command.tags) { // Node grouping tag, this is a array of strings that can't be empty and can't contain a comma
2307 + var ok = true, group2 = [];
2308 + if (common.validateString(command.tags, 0, 4096) == true) { command.tags = command.tags.split(','); }
2309 + 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); } }
2310 + group2.sort();
2311 + if (node.tags != group2) { node.tags = group2; change = 1; }
2312 + } else if ((command.tags === '') && node.tags) { delete node.tags; change = 1; }
2313 +
2314 + if (change == 1) {
2315 + // Save the node
2316 + db.Set(node);
2317
2436 - if (change == 1) {
2437 - // Save the node
2438 - db.Set(node);
2439 -
2440 - // Event the node change. Only do this if the database will not do it.
2441 - event.msg = 'Changed device ' + node.name + ' from group ' + mesh.name + ': ' + changes.join(', ');
2442 - event.node = parent.CloneSafeNode(node);
2443 - 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.
2444 - parent.parent.DispatchEvent(['*', node.meshid, user._id], obj, event);
2445 - }
2318 + // Event the node change. Only do this if the database will not do it.
2319 + event.msg = 'Changed device ' + node.name + ' from group ' + mesh.name + ': ' + changes.join(', ');
2320 + event.node = parent.CloneSafeNode(node);
2321 + 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.
2322 + parent.parent.DispatchEvent(['*', node.meshid, user._id], obj, event);
2323 }
2324 });
2325 break;
2326 }
2327 case 'uploadagentcore':
2328 {
2452 - if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
2329 if (common.validateString(command.type, 1, 40) == false) break; // Check path
2330
2455 - // Change the device
2456 - db.Get(command.nodeid, function (err, nodes) {
2457 - if ((nodes == null) || (nodes.length != 1)) return;
2458 - var node = nodes[0];
2459 -
2460 - // Get the mesh for this device
2461 - mesh = parent.meshes[node.meshid];
2462 - if (mesh) {
2463 - // Check if this user has rights to do this
2464 - if (((parent.GetMeshRights(user, mesh) & MESHRIGHT_AGENTCONSOLE) == 0) && (user.siteadmin != 0xFFFFFFFF)) { return; }
2465 -
2466 - if (command.type == 'default') {
2467 - // Send the default core to the agent
2468 - parent.parent.updateMeshCore(function () { parent.sendMeshAgentCore(user, domain, command.nodeid, 'default'); });
2469 - } else if (command.type == 'clear') {
2470 - // Clear the mesh agent core on the mesh agent
2471 - parent.sendMeshAgentCore(user, domain, command.nodeid, 'clear');
2472 - } else if (command.type == 'recovery') {
2473 - // Send the recovery core to the agent
2474 - parent.sendMeshAgentCore(user, domain, command.nodeid, 'recovery');
2475 - } else if ((command.type == 'custom') && (common.validateString(command.path, 1, 2048) == true)) {
2476 - // Send a mesh agent core to the mesh agent
2477 - var file = parent.getServerFilePath(user, domain, command.path);
2478 - if (file != null) {
2479 - fs.readFile(file.fullpath, 'utf8', function (err, data) {
2480 - if (err != null) {
2481 - data = common.IntToStr(0) + data; // Add the 4 bytes encoding type & flags (Set to 0 for raw)
2482 - parent.sendMeshAgentCore(user, domain, command.nodeid, 'custom', data);
2483 - }
2484 - });
2485 - }
2331 + // Get the node and the rights for this node
2332 + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
2333 + if ((node == null) || (((rights & MESHRIGHT_AGENTCONSOLE) == 0) && (user.siteadmin != 0xFFFFFFFF))) return;
2334 +
2335 + if (command.type == 'default') {
2336 + // Send the default core to the agent
2337 + parent.parent.updateMeshCore(function () { parent.sendMeshAgentCore(user, domain, node._id, 'default'); });
2338 + } else if (command.type == 'clear') {
2339 + // Clear the mesh agent core on the mesh agent
2340 + parent.sendMeshAgentCore(user, domain, node._id, 'clear');
2341 + } else if (command.type == 'recovery') {
2342 + // Send the recovery core to the agent
2343 + parent.sendMeshAgentCore(user, domain, node._id, 'recovery');
2344 + } else if ((command.type == 'custom') && (common.validateString(command.path, 1, 2048) == true)) {
2345 + // Send a mesh agent core to the mesh agent
2346 + var file = parent.getServerFilePath(user, domain, command.path);
2347 + if (file != null) {
2348 + fs.readFile(file.fullpath, 'utf8', function (err, data) {
2349 + if (err != null) {
2350 + data = common.IntToStr(0) + data; // Add the 4 bytes encoding type & flags (Set to 0 for raw)
2351 + parent.sendMeshAgentCore(user, domain, node._id, 'custom', data);
2352 + }
2353 + });
2354 }
2355 }
2356 });
@@ -2490,23 +2358,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2358 }
2359 case 'agentdisconnect':
2360 {
2493 - if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
2361 if (common.validateInt(command.disconnectMode) == false) return; // Check disconnect mode
2362
2496 - // Change the device
2497 - db.Get(command.nodeid, function (err, nodes) {
2498 - if ((nodes == null) || (nodes.length != 1)) return;
2499 - var node = nodes[0];
2363 + // Get the node and the rights for this node
2364 + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
2365 + if ((node == null) || (((rights & MESHRIGHT_AGENTCONSOLE) == 0) && (user.siteadmin != 0xFFFFFFFF))) return;
2366
2501 - // Get the mesh for this device
2502 - mesh = parent.meshes[node.meshid];
2503 - if (mesh) {
2504 - // Check if this user has rights to do this
2505 - if (((parent.GetMeshRights(user, mesh) & MESHRIGHT_AGENTCONSOLE) == 0) && (user.siteadmin != 0xFFFFFFFF)) return;
2506 -
2507 - // Force mesh agent disconnection
2508 - parent.forceMeshAgentDisconnect(user, domain, command.nodeid, command.disconnectMode);
2509 - }
2367 + // Force mesh agent disconnection
2368 + parent.forceMeshAgentDisconnect(user, domain, node._id, command.disconnectMode);
2369 });
2370 break;
2371 }
@@ -2552,7 +2411,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2411 mesh = parent.meshes[command.meshid];
2412 if (mesh == null) { err = 'Unknown device group'; } // Check if the group exists
2413 else if (mesh.mtype != 2) { err = 'Invalid group type'; } // Check if this is the correct group type
2555 - else if (parent.GetMeshRights(user, mesh) == 0) { err = 'Not allowed'; } // Check if this user has rights to do this
2414 + else if (parent.IsMeshViewable(user, mesh) == false) { err = 'Not allowed'; } // Check if this user has rights to do this
2415 }
2416 }
2417 } catch (ex) { err = 'Validation exception: ' + ex; }
@@ -2574,24 +2433,16 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2433 {
2434 // Argument validation
2435 if (common.validateString(command.msg, 1, 4096) == false) break; // Check event
2577 - if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
2578 - var splitid = command.nodeid.split('/');
2579 - if ((splitid.length != 3) || (splitid[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
2580 - var idtype = splitid[0];
2581 - if ((idtype != 'node')) return;
2436
2583 - // Check if this user has rights on this id to set notes
2584 - db.Get(command.nodeid, function (err, nodes) {
2585 - if ((nodes == null) || (nodes.length == 1)) {
2586 - if (parent.GetMeshRights(user, nodes[0].meshid) != 0) {
2587 - // Add an event for this device
2588 - var targets = ['*', 'server-users', user._id, nodes[0].meshid];
2589 - var event = { etype: 'node', userid: user._id, username: user.name, nodeid: nodes[0]._id, action: 'manual', msg: decodeURIComponent(command.msg), domain: domain.id };
2590 - parent.parent.DispatchEvent(targets, obj, event);
2591 - }
2592 - }
2593 - });
2437 + // Get the node and the rights for this node
2438 + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
2439 + if (rights == 0) return;
2440
2441 + // Add an event for this device
2442 + var targets = ['*', 'server-users', user._id, nodes[0].meshid];
2443 + var event = { etype: 'node', userid: user._id, username: user.name, nodeid: nodes[0]._id, action: 'manual', msg: decodeURIComponent(command.msg), domain: domain.id };
2444 + parent.parent.DispatchEvent(targets, obj, event);
2445 + });
2446 break;
2447 }
2448 case 'setNotes':
@@ -2604,16 +2455,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2455 if ((idtype != 'user') && (idtype != 'mesh') && (idtype != 'node')) return;
2456
2457 if (idtype == 'node') {
2607 - // Check if this user has rights on this id to set notes
2608 - 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 (???)
2609 - if ((nodes == null) || (nodes.length == 1)) {
2610 - if ((parent.GetMeshRights(user, nodes[0].meshid) & MESHRIGHT_SETNOTES) != 0) {
2611 - // Set the id's notes
2612 - if (common.validateString(command.notes, 1) == false) {
2613 - db.Remove('nt' + command.id); // Delete the note for this node
2614 - } else {
2615 - db.Set({ _id: 'nt' + command.id, type: 'note', value: command.notes }); // Set the note for this node
2616 - }
2458 + // Get the node and the rights for this node
2459 + parent.GetNodeWithRights(domain, user, command.id, function (node, rights, visible) {
2460 + if ((rights & MESHRIGHT_SETNOTES) != 0) {
2461 + // Set the id's notes
2462 + if (common.validateString(command.notes, 1) == false) {
2463 + db.Remove('nt' + node._id); // Delete the note for this node
2464 + } else {
2465 + db.Set({ _id: 'nt' + node._id, type: 'note', value: command.notes }); // Set the note for this node
2466 }
2467 }
2468 });
@@ -2893,44 +2742,26 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2742 case 'getClip': {
2743 if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
2744
2896 - // Get the device
2897 - db.Get(command.nodeid, function (err, nodes) {
2898 - if ((nodes == null) || (nodes.length != 1)) return;
2899 - var node = nodes[0];
2900 -
2901 - // Get the mesh for this device
2902 - mesh = parent.meshes[node.meshid];
2903 - if (mesh) {
2904 - // Check if this user has "remote" rights to do this
2905 - var meshrights = parent.GetMeshRights(user, mesh);
2906 - if ((meshrights & MESHRIGHT_AGENTCONSOLE) == 0) return;
2745 + // Get the node and the rights for this node
2746 + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
2747 + if ((rights & MESHRIGHT_AGENTCONSOLE) == 0) return;
2748
2908 - // Ask for clipboard data from agent
2909 - var agent = parent.wsagents[node._id];
2910 - if (agent != null) { try { agent.send(JSON.stringify({ action: 'getClip' })); } catch (ex) { } }
2911 - }
2749 + // Ask for clipboard data from agent
2750 + var agent = parent.wsagents[node._id];
2751 + if (agent != null) { try { agent.send(JSON.stringify({ action: 'getClip' })); } catch (ex) { } }
2752 });
2753 break;
2754 }
2755 case 'setClip': {
2916 - if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
2756 if (common.validateString(command.data, 1, 65535) == false) break; // Check
2757
2919 - // Get the device
2920 - db.Get(command.nodeid, function (err, nodes) {
2921 - if ((nodes == null) || (nodes.length != 1)) return;
2922 - var node = nodes[0];
2758 + // Get the node and the rights for this node
2759 + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
2760 + if ((rights & MESHRIGHT_AGENTCONSOLE) == 0) return;
2761
2924 - // Get the mesh for this device
2925 - mesh = parent.meshes[node.meshid];
2926 - if (mesh) {
2927 - // Check if this user has "remote" rights to do this
2928 - if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_AGENTCONSOLE) == 0) return;
2929 -
2930 - // Send clipboard data to the agent
2931 - var agent = parent.wsagents[node._id];
2932 - if (agent != null) { try { agent.send(JSON.stringify({ action: 'setClip', data: command.data })); } catch (ex) { } }
2933 - }
2762 + // Send clipboard data to the agent
2763 + var agent = parent.wsagents[node._id];
2764 + if (agent != null) { try { agent.send(JSON.stringify({ action: 'setClip', data: command.data })); } catch (ex) { } }
2765 });
2766 break;
2767 }
@@ -2951,25 +2782,17 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2782 if ((idtype != 'user') && (idtype != 'mesh') && (idtype != 'node')) return;
2783
2784 if (idtype == 'node') {
2954 - // Get the device
2955 - db.Get(command.id, function (err, nodes) {
2956 - if ((nodes == null) || (nodes.length != 1)) return;
2957 - var node = nodes[0];
2785 + // Get the node and the rights for this node
2786 + parent.GetNodeWithRights(domain, user, command.id, function (node, rights, visible) {
2787 + if (visible == false) return;
2788
2959 - // Get the mesh for this device
2960 - mesh = parent.meshes[node.meshid];
2961 - if (mesh) {
2962 - // Check if this user has rights to do this
2963 - if (parent.GetMeshRights(user, mesh) == 0) return;
2964 -
2965 - // Get the notes about this node
2966 - db.Get('nt' + command.id, function (err, notes) {
2967 - try {
2968 - if ((notes == null) || (notes.length != 1)) { ws.send(JSON.stringify({ action: 'getNotes', id: command.id, notes: null })); return; }
2969 - ws.send(JSON.stringify({ action: 'getNotes', id: command.id, notes: notes[0].value }));
2970 - } catch (ex) { }
2971 - });
2972 - }
2789 + // Get the notes about this node
2790 + db.Get('nt' + command.id, function (err, notes) {
2791 + try {
2792 + if ((notes == null) || (notes.length != 1)) { ws.send(JSON.stringify({ action: 'getNotes', id: command.id, notes: null })); return; }
2793 + ws.send(JSON.stringify({ action: 'getNotes', id: command.id, notes: notes[0].value }));
2794 + } catch (ex) { }
2795 + });
2796 });
2797 } else if (idtype == 'mesh') {
2798 // Get the mesh for this device
@@ -3037,6 +2860,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2860 break;
2861 }
2862 case 'sendmqttmsg': {
2863 + if (parent.parent.mqttbroker == null) { err = 'MQTT not supported on this server'; }; // MQTT not available
2864 if (common.validateArray(command.nodeids, 1) == false) { err = 'Invalid nodeids'; }; // Check nodeid's
2865 if (common.validateString(command.topic, 1, 64) == false) { err = 'Invalid topic'; } // Check the topic
2866 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
2871 break;
2872 }
2873
3050 - // TODO: We can optimize this a lot.
3051 - // - We should get a full list of all MAC's to wake first.
3052 - // - We should try to only have one agent per subnet (using Gateway MAC) send a wake-on-lan.
2874 + // Send the MQTT message
2875 for (i in command.nodeids) {
3054 - nodeid = command.nodeids[i];
3055 - var wakeActions = 0;
3056 - if (common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid
3057 - if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain
3058 - // Get the device
3059 - db.Get(nodeid, function (err, nodes) {
3060 - if ((nodes == null) || (nodes.length != 1)) return;
3061 - var node = nodes[0];
3062 -
3063 - // Get the mesh for this device
3064 - mesh = parent.meshes[node.meshid];
3065 - if (mesh) {
3066 - // Check if this user has rights to do this
3067 - if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_WAKEDEVICE) != 0) {
3068 - // If this device is connected on MQTT, send a wake action.
3069 - if (parent.parent.mqttbroker != null) { parent.parent.mqttbroker.publish(node._id, command.topic, command.msg); }
3070 - }
3071 - }
3072 - });
3073 - }
2876 + // Get the node and the rights for this node
2877 + parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) {
2878 + // If this device is connected on MQTT, send a wake action.
2879 + if (rights != 0) { parent.parent.mqttbroker.publish(node._id, command.topic, command.msg); }
2880 + });
2881 }
2882
2883 break;
@@ -3083,43 +2890,33 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2890 // Handle any errors
2891 if (err != null) { if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'getmqttlogin', responseid: command.responseid, result: err })); } catch (ex) { } } break; }
2892
3086 - var nodeid = command.nodeid;
3087 - if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain
3088 - // Get the device
3089 - db.Get(nodeid, function (err, nodes) {
3090 - 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; } }
3091 - var node = nodes[0];
3092 -
3093 - // Get the device group for this node
3094 - var mesh = parent.meshes[node.meshid];
3095 - if (mesh) {
3096 - // Check if this user has rights to do this
3097 - if ((parent.GetMeshRights(user, mesh) == 0xFFFFFFFF)) {
3098 - var token = parent.parent.mqttbroker.generateLogin(mesh._id, node._id);
3099 - var r = { action: 'getmqttlogin', responseid: command.responseid, nodeid: node._id, user: token.user, pass: token.pass };
3100 - const serverName = parent.getWebServerName(domain);
3101 -
3102 - // Add MPS URL
3103 - if (parent.parent.mpsserver != null) {
3104 - r.mpsCertHashSha384 = parent.parent.certificateOperations.getCertHash(parent.parent.mpsserver.certificates.mps.cert);
3105 - r.mpsCertHashSha1 = parent.parent.certificateOperations.getCertHashSha1(parent.parent.mpsserver.certificates.mps.cert);
3106 - r.mpsUrl = 'mqtts://' + serverName + ':' + ((args.mpsaliasport != null) ? args.mpsaliasport : args.mpsport) + '/';
3107 - }
3108 -
3109 - // Add WS URL
3110 - var xdomain = (domain.dns == null) ? domain.id : '';
3111 - if (xdomain != '') xdomain += "/";
3112 - var httpsPort = ((args.aliasport == null) ? args.port : args.aliasport); // Use HTTPS alias port is specified
3113 - r.wsUrl = "ws" + (args.notls ? '' : 's') + "://" + serverName + ":" + httpsPort + "/" + xdomain + "mqtt.ashx";
3114 - r.wsTrustedCert = parent.isTrustedCert(domain);
3115 -
3116 - try { ws.send(JSON.stringify(r)); } catch (ex) { }
3117 - } else {
3118 - if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'getmqttlogin', responseid: command.responseid, result: 'Unable to perform this operation' })); } catch (ex) { } }
3119 - }
3120 - }
3121 - });
3122 - }
2893 + // Get the node and the rights for this node
2894 + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
2895 + // Check if this user has rights to do this
2896 + if (rights == 0xFFFFFFFF) {
2897 + var token = parent.parent.mqttbroker.generateLogin(node.meshid, node._id);
2898 + var r = { action: 'getmqttlogin', responseid: command.responseid, nodeid: node._id, user: token.user, pass: token.pass };
2899 + const serverName = parent.getWebServerName(domain);
2900 +
2901 + // Add MPS URL
2902 + if (parent.parent.mpsserver != null) {
2903 + r.mpsCertHashSha384 = parent.parent.certificateOperations.getCertHash(parent.parent.mpsserver.certificates.mps.cert);
2904 + r.mpsCertHashSha1 = parent.parent.certificateOperations.getCertHashSha1(parent.parent.mpsserver.certificates.mps.cert);
2905 + r.mpsUrl = 'mqtts://' + serverName + ':' + ((args.mpsaliasport != null) ? args.mpsaliasport : args.mpsport) + '/';
2906 + }
2907 +
2908 + // Add WS URL
2909 + var xdomain = (domain.dns == null) ? domain.id : '';
2910 + if (xdomain != '') xdomain += '/';
2911 + var httpsPort = ((args.aliasport == null) ? args.port : args.aliasport); // Use HTTPS alias port is specified
2912 + r.wsUrl = 'ws' + (args.notls ? '' : 's') + '://' + serverName + ':' + httpsPort + '/' + xdomain + 'mqtt.ashx';
2913 + r.wsTrustedCert = parent.isTrustedCert(domain);
2914 +
2915 + try { ws.send(JSON.stringify(r)); } catch (ex) { }
2916 + } else {
2917 + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'getmqttlogin', responseid: command.responseid, result: 'Unable to perform this operation' })); } catch (ex) { } }
2918 + }
2919 + });
2920 break;
2921 }
2922 case 'amt': {
@@ -3136,23 +2933,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2933 } else if (command.mode == 3) {
2934 if (parent.parent.apfserver.apfConnections[command.nodeid] == null) break;
2935 }
3139 - var nodeid = command.nodeid;
3140 - if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain
3141 - // Get the device
3142 - db.Get(nodeid, function (err, nodes) {
3143 - if ((nodes == null) || (nodes.length != 1)) return;
3144 - var node = nodes[0];
2936
3146 - // Get the mesh for this device
3147 - var mesh = parent.meshes[node.meshid];
3148 - if (mesh) {
3149 - // Check if this user has rights to do this
3150 - if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_REMOTECONTROL) != 0) { // "Remote Control permission"
3151 - handleAmtCommand(command, node);
3152 - }
3153 - }
3154 - });
3155 - }
2937 + // Get the node and the rights for this node
2938 + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
2939 + if ((rights & MESHRIGHT_REMOTECONTROL) == 0) return;
2940 + handleAmtCommand(command, node);
2941 + });
2942 break;
2943 }
2944 case 'distributeCore': {
@@ -3304,15 +3090,15 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
3090
3091 // Read all files recursively
3092 try {
3307 - files.filetree.f[user._id].f = readFilesRec(parent.path.join(parent.filespath, domainx + "/user-" + usersplit[2]));
3093 + files.filetree.f[user._id].f = readFilesRec(parent.path.join(parent.filespath, domainx + '/user-' + usersplit[2]));
3094 } catch (e) {
3095 // TODO: We may want to fake this file structure until it's needed.
3096 // Got an error, try to create all the folders and try again...
3097 try { fs.mkdirSync(parent.filespath); } catch (e) { }
3098 try { fs.mkdirSync(parent.path.join(parent.filespath, domainx)); } catch (e) { }
3313 - try { fs.mkdirSync(parent.path.join(parent.filespath, domainx + "/user-" + usersplit[2])); } catch (e) { }
3314 - try { fs.mkdirSync(parent.path.join(parent.filespath, domainx + "/user-" + usersplit[2] + "/Public")); } catch (e) { }
3315 - try { files.filetree.f[user._id].f = readFilesRec(parent.path.join(parent.filespath, domainx + "/user-" + usersplit[2])); } catch (e) { }
3099 + try { fs.mkdirSync(parent.path.join(parent.filespath, domainx + '/user-' + usersplit[2])); } catch (e) { }
3100 + try { fs.mkdirSync(parent.path.join(parent.filespath, domainx + '/user-' + usersplit[2] + '/Public')); } catch (e) { }
3101 + try { files.filetree.f[user._id].f = readFilesRec(parent.path.join(parent.filespath, domainx + '/user-' + usersplit[2])); } catch (e) { }
3102 }
3103
3104 // 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
3112
3113 // Read all files recursively
3114 try {
3329 - files.filetree.f[mesh._id].f = readFilesRec(parent.path.join(parent.filespath, domainx + "/mesh-" + meshsplit[2]));
3115 + files.filetree.f[mesh._id].f = readFilesRec(parent.path.join(parent.filespath, domainx + '/mesh-' + meshsplit[2]));
3116 } catch (e) {
3117 files.filetree.f[mesh._id].f = {}; // Got an error, return empty folder. We will create the folder only when needed.
3118 }
@@ -3338,7 +3124,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
3124 try { ws.send(JSON.stringify(files)); } catch (ex) { }
3125 }
3126
3341 - function EscapeHtml(x) { if (typeof x == "string") return x.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;'); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
3127 + function EscapeHtml(x) { if (typeof x == 'string') return x.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;'); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
3128 //function EscapeHtmlBreaks(x) { if (typeof x == "string") return x.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;').replace(/\r/g, '<br />').replace(/\n/g, '').replace(/\t/g, '&nbsp;&nbsp;'); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
3129
3130 // 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
3174 node.intelamt.tls, tlsoptions, parent.parent, cmd.mode);
3175 var amt = new Amt(wsman);
3176 switch (cmd.command) {
3391 - case "Get-GeneralSettings": {
3392 - amt.Get("AMT_GeneralSettings", function (obj, name, response, status) {
3177 + case 'Get-GeneralSettings': {
3178 + amt.Get('AMT_GeneralSettings', function (obj, name, response, status) {
3179 if (status == 200) {
3180 var resp = { action: 'amt', nodeid: cmd.nodeid, command: 'Get-GeneralSettings', value: response.Body }
3181 ws.send(JSON.stringify(resp));
3182 } else {
3397 - ws.send(JSON.stringify({ "error": error }));
3183 + ws.send(JSON.stringify({ 'error': error }));
3184 }
3185 });
3186 break;
mpsserver.js
+29 -29
@@ -11,7 +11,7 @@
11 /*jshint strict:false */
12 /*jshint -W097 */
13 /*jshint esversion: 6 */
14 -"use strict";
14 +'use strict';
15
16 // Construct a Intel AMT MPS server object
17 module.exports.CreateMpsServer = function (parent, db, args, certificates) {
@@ -24,9 +24,9 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
24 var tlsSessionStore = {}; // Store TLS session information for quick resume.
25 var tlsSessionStoreCount = 0; // Number of cached TLS session information in store.
26 const constants = (require('crypto').constants ? require('crypto').constants : require('constants')); // require('constants') is deprecated in Node 11.10, use require('crypto').constants instead.
27 - const common = require("./common.js");
28 - const net = require("net");
29 - const tls = require("tls");
27 + const common = require('./common.js');
28 + const net = require('net');
29 + const tls = require('tls');
30 const MAX_IDLE = 90000; // 90 seconds max idle time, higher than the typical KEEP-ALIVE periode of 60 seconds
31
32 if (obj.args.mpstlsoffload) {
@@ -42,9 +42,9 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
42
43 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(); } });
44 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 + '.'); } });
45 - obj.parent.updateServerState("mps-port", args.mpsport);
46 - obj.parent.updateServerState("mps-name", certificates.AmtMpsName);
47 - if (args.mpsaliasport != null) { obj.parent.updateServerState("mps-alias-port", args.mpsaliasport); }
45 + obj.parent.updateServerState('mps-port', args.mpsport);
46 + obj.parent.updateServerState('mps-name', certificates.AmtMpsName);
47 + if (args.mpsaliasport != null) { obj.parent.updateServerState('mps-alias-port', args.mpsaliasport); }
48
49 const APFProtocol = {
50 UNKNOWN: 0,
@@ -202,47 +202,47 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
202 function onConnection(socket) {
203 connectionCount++;
204 if (obj.args.mpstlsoffload) {
205 - socket.tag = { first: true, clientCert: null, accumulator: "", activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 };
205 + socket.tag = { first: true, clientCert: null, accumulator: '', activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 };
206 } else {
207 - socket.tag = { first: true, clientCert: socket.getPeerCertificate(true), accumulator: "", activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 };
207 + socket.tag = { first: true, clientCert: socket.getPeerCertificate(true), accumulator: '', activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 };
208 }
209 - socket.setEncoding("binary");
209 + socket.setEncoding('binary');
210 parent.debug('mps', "New CIRA connection");
211
212 // Setup the CIRA keep alive timer
213 socket.setTimeout(MAX_IDLE);
214 - socket.on("timeout", () => { ciraTimeoutCount++; parent.debug('mps', "CIRA timeout, disconnecting."); try { socket.end(); } catch (e) { } });
214 + socket.on('timeout', () => { ciraTimeoutCount++; parent.debug('mps', "CIRA timeout, disconnecting."); try { socket.end(); } catch (e) { } });
215
216 - socket.addListener("data", function (data) {
217 - if (args.mpsdebug) { var buf = Buffer.from(data, "binary"); console.log("MPS <-- (" + buf.length + "):" + buf.toString('hex')); } // Print out received bytes
216 + socket.addListener('data', function (data) {
217 + if (args.mpsdebug) { var buf = Buffer.from(data, 'binary'); console.log("MPS <-- (" + buf.length + "):" + buf.toString('hex')); } // Print out received bytes
218 socket.tag.accumulator += data;
219
220 // 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.
221 if (socket.tag.first == true) {
222 if (socket.tag.accumulator.length < 3) return;
223 //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; }
224 - 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\n<!DOCTYPE html><html><head><meta charset=\"UTF-8\"></head><body>MeshCentral2 MPS server.<br />Intel&reg; AMT computers should connect here.</body></html>"); socket.end(); return; }
224 + 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\n<!DOCTYPE html><html><head><meta charset=\"UTF-8\"></head><body>MeshCentral2 MPS server.<br />Intel&reg; AMT computers should connect here.</body></html>"); socket.end(); return; }
225
226 // If the MQTT broker is active, look for inbound MQTT connections
227 if (parent.mqttbroker != null) {
228 - var chunk = Buffer.from(socket.tag.accumulator, "binary");
228 + var chunk = Buffer.from(socket.tag.accumulator, 'binary');
229 var packet_len = 0;
230 if (chunk.readUInt8(0) == 16) { packet_len = getMQTTPacketLength(chunk); }
231 if (chunk.readUInt8(0) == 16 && (socket.tag.accumulator.length < packet_len)) return; // Minimum MQTT detection
232
233 // check if it is MQTT, need more initial packet to probe
234 - if (chunk.readUInt8(0) == 16 && ((chunk.slice(4, 8).toString() === "MQTT") || (chunk.slice(5, 9).toString() === "MQTT")
235 - || (chunk.slice(6, 10).toString() === "MQTT") || (chunk.slice(7, 11).toString() === "MQTT"))) {
236 - parent.debug("mps", "MQTT connection detected.");
234 + if (chunk.readUInt8(0) == 16 && ((chunk.slice(4, 8).toString() === 'MQTT') || (chunk.slice(5, 9).toString() === 'MQTT')
235 + || (chunk.slice(6, 10).toString() === 'MQTT') || (chunk.slice(7, 11).toString() === 'MQTT'))) {
236 + parent.debug('mps', "MQTT connection detected.");
237 socket.removeAllListeners("data");
238 socket.removeAllListeners("close");
239 socket.setNoDelay(true);
240 socket.serialtunnel = SerialTunnel();
241 socket.serialtunnel.xtransport = 'mps';
242 socket.serialtunnel.xip = socket.remoteAddress;
243 - socket.on("data", function (b) { socket.serialtunnel.updateBuffer(Buffer.from(b, "binary")) });
244 - socket.serialtunnel.forwardwrite = function (b) { socket.write(b, "binary") }
245 - socket.on("close", function () { socket.serialtunnel.emit("end"); });
243 + socket.on('data', function (b) { socket.serialtunnel.updateBuffer(Buffer.from(b, 'binary')) });
244 + socket.serialtunnel.forwardwrite = function (b) { socket.write(b, 'binary') }
245 + socket.on('close', function () { socket.serialtunnel.emit('end'); });
246
247 // Pass socket wrapper to the MQTT broker
248 parent.mqttbroker.handle(socket.serialtunnel);
@@ -533,7 +533,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
533 var request = data.substring(5, 5 + requestLen);
534 //var wantResponse = data.charCodeAt(5 + requestLen);
535
536 - if (request == "tcpip-forward") {
536 + if (request == 'tcpip-forward') {
537 var addrLen = common.ReadInt(data, 6 + requestLen);
538 if (len < 14 + requestLen + addrLen) return 0;
539 var addr = data.substring(10 + requestLen, 10 + requestLen + addrLen);
@@ -545,7 +545,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
545 return 14 + requestLen + addrLen;
546 }
547
548 - if (request == "cancel-tcpip-forward") {
548 + if (request == 'cancel-tcpip-forward') {
549 var addrLen = common.ReadInt(data, 6 + requestLen);
550 if (len < 14 + requestLen + addrLen) return 0;
551 var addr = data.substring(10 + requestLen, 10 + requestLen + addrLen);
@@ -557,7 +557,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
557 return 14 + requestLen + addrLen;
558 }
559
560 - if (request == "udp-send-to@amt.intel.com") {
560 + if (request == 'udp-send-to@amt.intel.com') {
561 var addrLen = common.ReadInt(data, 6 + requestLen);
562 if (len < 26 + requestLen + addrLen) return 0;
563 var addr = data.substring(10 + requestLen, 10 + requestLen + addrLen);
@@ -748,14 +748,14 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
748 }
749 }
750
751 - socket.addListener("close", function () {
751 + socket.addListener('close', function () {
752 socketClosedCount++;
753 parent.debug('mps', 'CIRA connection closed');
754 try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { }
755 obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2);
756 });
757
758 - socket.addListener("error", function () {
758 + socket.addListener('error', function () {
759 socketErrorCount++;
760 //console.log("MPS Error: " + socket.remoteAddress);
761 });
@@ -802,7 +802,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
802 */
803
804 function SendChannelOpen(socket, direct, channelid, windowsize, target, targetport, source, sourceport) {
805 - var connectionType = ((direct == true) ? "direct-tcpip" : "forwarded-tcpip");
805 + var connectionType = ((direct == true) ? 'direct-tcpip' : 'forwarded-tcpip');
806 if ((target == null) || (target == null)) target = ''; // TODO: Reports of target being undefined that causes target.length to fail. This is a hack.
807 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));
808 }
@@ -837,11 +837,11 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
837 function Write(socket, data) {
838 if (args.mpsdebug) {
839 // Print out sent bytes
840 - var buf = Buffer.from(data, "binary");
840 + var buf = Buffer.from(data, 'binary');
841 console.log('MPS --> (' + buf.length + '):' + buf.toString('hex'));
842 socket.write(buf);
843 } else {
844 - socket.write(Buffer.from(data, "binary"));
844 + socket.write(Buffer.from(data, 'binary'));
845 }
846 }
847
multiserver.js
+5 -9
@@ -11,7 +11,7 @@
11 /*jshint strict:false */
12 /*jshint -W097 */
13 /*jshint esversion: 6 */
14 -"use strict";
14 +'use strict';
15
16 // Construct a Mesh Multi-Server object. This is used for MeshCentral-to-MeshCentral communication.
17 module.exports.CreateMultiServer = function (parent, args) {
@@ -552,14 +552,10 @@ module.exports.CreateMultiServer = function (parent, args) {
552 if (msg.fromNodeid != null) { msg.nodeid = msg.fromNodeid; delete msg.fromNodeid; }
553 var cmdstr = JSON.stringify(msg);
554 for (userid in obj.parent.webserver.wssessions) { // Find all connected users for this mesh and send the message
555 - var user = obj.parent.webserver.users[userid];
556 - if (user) {
557 - var rights = user.links[msg.meshid];
558 - if (rights != null) { // TODO: Look at what rights are needed for message routing
559 - var sessions = obj.parent.webserver.wssessions[userid];
560 - // Send the message to all users on this server
561 - for (i in sessions) { sessions[i].send(cmdstr); }
562 - }
555 + if (parent.webserver.GetMeshRights(userid, msg.meshid) != 0) { // TODO: Look at what rights are needed for message routing
556 + var sessions = obj.parent.webserver.wssessions[userid];
557 + // Send the message to all users on this server
558 + for (i in sessions) { sessions[i].send(cmdstr); }
559 }
560 }
561 }
package.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "name": "meshcentral",
3 - "version": "0.4.6-p",
3 + "version": "0.4.6-q",
4 "keywords": [
5 "Remote Management",
6 "Intel AMT",
public/images/icons16.png
Binary files a/public/images/icons16.png and b/public/images/icons16.png differ
public/images/icons256-7-1.png
Binary files /dev/null and b/public/images/icons256-7-1.png differ
public/images/icons50.png
Binary files a/public/images/icons50.png and b/public/images/icons50.png differ
public/images/icons64.png
Binary files a/public/images/icons64.png and b/public/images/icons64.png differ
public/styles/style.css
+30 -6
@@ -1155,6 +1155,14 @@ a {
1155 border: none;
1156 }
1157
1158 +.i7 {
1159 + background: url(../images/icons50.png) -300px 0px;
1160 + height: 50px;
1161 + width: 50px;
1162 + cursor: pointer;
1163 + border: none;
1164 +}
1165 +
1166 .j1 {
1167 background: url(../images/icons16.png) 0px 0px;
1168 height: 16px;
@@ -1203,6 +1211,14 @@ a {
1211 border: none;
1212 }
1213
1214 +.j7 {
1215 + background: url(../images/icons16.png) -96px 0px;
1216 + height: 16px;
1217 + width: 16px;
1218 + cursor: pointer;
1219 + border: none;
1220 +}
1221 +
1222 .relayIcon16 {
1223 background: url(../images/icon-relay.png);
1224 height: 16px;
@@ -1339,7 +1355,7 @@ a {
1355 float: left;
1356 }
1357
1342 -.si0 {
1358 +.si1 {
1359 background: url(../images/icons16.png) 0px 0px;
1360 height: 16px;
1361 width: 16px;
@@ -1347,7 +1363,7 @@ a {
1363 float: left;
1364 }
1365
1350 -.si1 {
1366 +.si2 {
1367 background: url(../images/icons16.png) -16px 0px;
1368 height: 16px;
1369 width: 16px;
@@ -1355,7 +1371,7 @@ a {
1371 float: left;
1372 }
1373
1358 -.si2 {
1374 +.si3 {
1375 background: url(../images/icons16.png) -32px 0px;
1376 height: 16px;
1377 width: 16px;
@@ -1363,7 +1379,7 @@ a {
1379 float: left;
1380 }
1381
1366 -.si3 {
1382 +.si4 {
1383 background: url(../images/icons16.png) -48px 0px;
1384 height: 16px;
1385 width: 16px;
@@ -1371,7 +1387,7 @@ a {
1387 float: left;
1388 }
1389
1374 -.si4 {
1390 +.si5 {
1391 background: url(../images/icons16.png) -64px 0px;
1392 height: 16px;
1393 width: 16px;
@@ -1379,7 +1395,7 @@ a {
1395 float: left;
1396 }
1397
1382 -.si5 {
1398 +.si6 {
1399 background: url(../images/icons16.png) -80px 0px;
1400 height: 16px;
1401 width: 16px;
@@ -1387,6 +1403,14 @@ a {
1403 float: left;
1404 }
1405
1406 +.si7 {
1407 + background: url(../images/icons16.png) -96px 0px;
1408 + height: 16px;
1409 + width: 16px;
1410 + border: none;
1411 + float: left;
1412 +}
1413 +
1414 .mi {
1415 background: url(../images/meshicon50.png) 0px 0px;
1416 height: 50px;
translate/translate.json
+2 -3
@@ -8,8 +8,7 @@
8 "nl": "0",
9 "xloc": [
10 "default.handlebars->container->masthead->7->notificationCount",
11 - "default-mobile.handlebars->9->229",
12 - { "$ref": "#" }
11 + "default-mobile.handlebars->9->229"
12 ]
13 },
14 {
@@ -5610,7 +5609,7 @@
5609 {
5610 "en": "free",
5611 "fr": "libre",
5613 - "pt": "Livre",
5612 + "pt": "livre",
5613 "ja": "無料",
5614 "cs": "volné",
5615 "nl": "vrij",
views/default-mobile.handlebars
+70 -42
@@ -1327,7 +1327,7 @@
1327 count++;
1328
1329 // Mesh rights
1330 - var meshrights = meshes[i].links[userinfo._id].rights;
1330 + var meshrights = GetMeshRights(meshes[i]);
1331 var rights = "Partial Rights";
1332 if (meshrights == 0xFFFFFFFF) rights = "Full Administrator"; else if (meshrights == 0) rights = "No Rights";
1333
@@ -1576,8 +1576,7 @@
1576 if (desktop && !xxdialogMode && xxcurrentView == 10) {
1577 // Check what keys we are allows to send
1578 if (currentNode != null) {
1579 - var mesh = meshes[currentNode.meshid];
1580 - var meshrights = mesh.links[userinfo._id].rights;
1579 + var meshrights = GetMeshRights(currentNode.meshid);
1580 var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
1581 if (inputAllowed == false) return false;
1582 var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
@@ -1594,8 +1593,7 @@
1593 if (desktop && !xxdialogMode && xxcurrentView == 10) {
1594 // Check what keys we are allows to send
1595 if (currentNode != null) {
1597 - var mesh = meshes[currentNode.meshid];
1598 - var meshrights = mesh.links[userinfo._id].rights;
1596 + var meshrights = GetMeshRights(currentNode.meshid);
1597 var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
1598 if (inputAllowed == false) return false;
1599 var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
@@ -1612,8 +1610,7 @@
1610 if (desktop && !xxdialogMode && xxcurrentView == 10) {
1611 // Check what keys we are allows to send
1612 if (currentNode != null) {
1615 - var mesh = meshes[currentNode.meshid];
1616 - var meshrights = mesh.links[userinfo._id].rights;
1613 + var meshrights = GetMeshRights(currentNode.meshid);
1614 var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
1615 if (inputAllowed == false) return false;
1616 var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
@@ -1655,9 +1652,7 @@
1652 // Go thru the list of nodes and display them
1653 for (var i in nodes) {
1654 if (nodes[i].v == false) continue;
1658 - var mesh2 = meshes[nodes[i].meshid], meshlinks = mesh2.links[userinfo._id];
1659 - if (meshlinks == null) continue;
1660 - var meshrights = meshlinks.rights;
1655 + //var meshrights = GetNodeRights(nodes[i]);
1656
1657 if (sort == 0) {
1658 // Mesh header
@@ -1668,7 +1663,7 @@
1663 if (meshes[nodes[i].meshid].mtype == 1) { extra = '<span style=color:lightgray>' + ", Intel&reg; AMT only" + '</span>'; }
1664 if (current != null) { if (c == 2) { r += '<td><div style=width:301px></div></td>'; } if (r != '') { r += '</tr></table>'; } }
1665 r += '<div class=DevSt style=padding-top:4px><span style=float:right>';
1671 - //r += getMeshActions(mesh2, meshrights);
1666 + //r += getMeshActions(meshes[nodes[i].meshid], meshrights);
1667 r += '</span><span id=MxMESH style=cursor:pointer onclick=goForward("' + nodes[i].meshid + '")>' + EscapeHtml(meshes[nodes[i].meshid].name) + '</span>' + extra + '<span id=DevxHeader' + deviceHeaderId + ' style=color:lightgray></span></div>';
1668 current = nodes[i].meshid;
1669 displayedMeshes[current] = 1;
@@ -1726,20 +1721,17 @@
1721 // Display all empty meshes, we need to do this because users can add devices to these at any time.
1722 if (sort == 0) {
1723 for (var i in meshes) {
1729 - var mesh = meshes[i], meshlink = mesh.links[userinfo._id];
1730 - if (meshlink != null) {
1731 - var meshrights = meshlink.rights;
1732 - if (displayedMeshes[mesh._id] == null) {
1733 - if ((current != '') && (r != '')) { r += '</tr></table>'; }
1734 - r += '<div><div colspan=3 class=DevSt><span style=float:right>';
1735 - //r += getMeshActions(mesh, meshrights);
1736 - r += '</span><span id=MxMESH style=cursor:pointer onclick=goForward("' + mesh._id + '")>' + EscapeHtml(mesh.name) + '</span></div>';
1737 - if (mesh.mtype == 1) { r += '<div style=padding:10px><i>' + "No Intel&reg; AMT devices in this group"; }
1738 - if (mesh.mtype == 2) { r += '<div style=padding:10px><i>' + "No devices in this group"; }
1739 - r += '.</i></div></div>';
1740 - current = mesh._id;
1741 - count++;
1742 - }
1724 + var mesh = meshes[i];
1725 + if (IsMeshViewable(mesh)) {
1726 + if ((current != '') && (r != '')) { r += '</tr></table>'; }
1727 + r += '<div><div colspan=3 class=DevSt><span style=float:right>';
1728 + //r += getMeshActions(mesh, meshrights);
1729 + r += '</span><span id=MxMESH style=cursor:pointer onclick=goForward("' + mesh._id + '")>' + EscapeHtml(mesh.name) + '</span></div>';
1730 + if (mesh.mtype == 1) { r += '<div style=padding:10px><i>' + "No Intel&reg; AMT devices in this group"; }
1731 + if (mesh.mtype == 2) { r += '<div style=padding:10px><i>' + "No devices in this group"; }
1732 + r += '.</i></div></div>';
1733 + current = mesh._id;
1734 + count++;
1735 }
1736 }
1737 }
@@ -1813,11 +1805,6 @@
1805 gotoDevice(nodeid, xxcurrentView, true);
1806 }
1807
1816 - function getNodeRights(nodeid) {
1817 - var node = getNodeFromId(nodeid), mesh = meshes[node.meshid];
1818 - return mesh.links[userinfo._id].rights;
1819 - }
1820 -
1808 var currentDevicePanel = 0;
1809 var currentNode;
1810 var powerTimelineNode = null;
@@ -1837,7 +1824,7 @@
1824 if (node == null) { goBack(); return; }
1825 var mesh = meshes[node.meshid];
1826 if (mesh == null) { goBack(); return; }
1840 - var meshrights = mesh.links[userinfo._id].rights;
1827 + var meshrights = GetMeshRights(mesh);
1828 if (!currentNode || currentNode._id != node._id || refresh == true) {
1829 currentNode = node;
1830
@@ -2008,8 +1995,7 @@
1995 }
1996
1997 function setupDeviceMenu(op, obj) {
2011 - var meshrights = 0;
2012 - if (currentNode) { meshrights = meshes[currentNode.meshid].links[userinfo._id].rights; }
1998 + var meshrights = GetNodeRights(currentNode);
1999 if (op != null) { currentDevicePanel = op; }
2000 QV('p10general', currentDevicePanel == 0);
2001 QV('p10desktop', currentDevicePanel == 1); // Show if we have remote control rights or desktop view only rights
@@ -2027,7 +2013,7 @@
2013
2014 function deviceActionFunction() {
2015 if (xxdialogMode) return;
2030 - var meshrights = meshes[currentNode.meshid].links[userinfo._id].rights;
2016 + var meshrights = GetNodeRights(currentNode);
2017 var x = "Select an operation to perform on this device." + '<br /><br />';
2018 var y = '<select id=d2deviceop style=float:right;width:170px>';
2019 if ((meshrights & 64) != 0) { y += '<option value=100>' + "Wake-up" + '</option>'; } // Wake-up permission
@@ -2117,7 +2103,7 @@
2103
2104 function editDeviceAmtSettings(nodeid, func) {
2105 if (xxdialogMode) return;
2120 - var x = '', node = getNodeFromId(nodeid), buttons = 3, meshrights = getNodeRights(nodeid);
2106 + var x = '', node = getNodeFromId(nodeid), buttons = 3, meshrights = GetNodeRights(node);
2107 if ((meshrights & 4) == 0) return;
2108 x += addHtmlValue("Username", '<input id=dp10username style=width:170px maxlength=32 autocomplete=nope placeholder="admin" onchange=validateDeviceAmtSettings() onkeyup=validateDeviceAmtSettings() />');
2109 x += addHtmlValue("Password", '<input id=dp10password type=password style=width:170px autocomplete=nope maxlength=32 onchange=validateDeviceAmtSettings() onkeyup=validateDeviceAmtSettings() />');
@@ -2166,9 +2152,8 @@
2152
2153 function p10showiconselector() {
2154 if (xxdialogMode) return;
2169 - var mesh = meshes[currentNode.meshid];
2170 - var meshrights = mesh.links[userinfo._id].rights;
2171 - if ((meshrights & 4) == 0) return;
2155 + var rights = GetNodeRights(currentNode);
2156 + if ((rights & 4) == 0) return;
2157
2158 var x = '<table align=center><td>';
2159 x += '<div style=display:inline-block class=i1 onclick=p10setIcon(1)></div>';
@@ -2245,7 +2230,7 @@
2230 var mesh = meshes[currentNode.meshid];
2231 var deskState = 0;
2232 if (desktop != null) { deskState = desktop.State; }
2248 - var meshrights = mesh.links[userinfo._id].rights;
2233 + var meshrights = GetNodeRights(currentNode);
2234
2235 // Show the right buttons
2236 QV('disconnectbutton1', (deskState != 0));
@@ -3096,7 +3081,7 @@
3081 if (currentMesh == null) return;
3082 QH('p20meshName', EscapeHtml(currentMesh.name));
3083 var meshtype = format("Unknown #{0}", currentMesh.mtype);
3099 - var meshrights = currentMesh.links[userinfo._id].rights;
3084 + var meshrights = GetMeshRights(currentMesh);
3085 if (currentMesh.mtype == 1) meshtype = "Intel&reg; AMT only, no agent";
3086 if (currentMesh.mtype == 2) meshtype = "Managed using a software agent";
3087
@@ -3229,7 +3214,7 @@
3214 }
3215
3216 function p20validateAddMeshUserDialog() {
3232 - var meshrights = currentMesh.links[userinfo._id].rights;
3217 + var meshrights = GetMeshRights(currentMesh);
3218 var nc = !Q('p20fulladmin').checked;
3219 QE('p20fulladmin', meshrights == 0xFFFFFFFF);
3220 QE('p20editmesh', nc && (meshrights == 0xFFFFFFFF));
@@ -3278,7 +3263,7 @@
3263 function p20viewuser(userid) {
3264 if (xxdialogMode) return;
3265 userid = decodeURIComponent(userid);
3281 - var r = [], cmeshrights = currentMesh.links[userinfo._id].rights, meshrights = currentMesh.links[userid].rights;
3266 + var r = [], cmeshrights = GetMeshRights(currentMesh), meshrights = GetMeshRights(currentMesh, userid);
3267 if (meshrights == 0xFFFFFFFF) r.push("Full Administrator"); else {
3268 if ((meshrights & 1) != 0) r.push("Edit Device Group");
3269 if ((meshrights & 2) != 0) r.push("Manage Device Group Users");
@@ -3361,6 +3346,49 @@
3346 if (((b & 8) || x) && f) f(x, t);
3347 }
3348
3349 + //
3350 + // Access Control Functions
3351 + // These must match server
3352 + //
3353 +
3354 + // Get the right of a user on a given device group
3355 + function GetMeshRights(mesh, user) {
3356 + if (mesh == null) { return 0; }
3357 + if (user == null) { user = userinfo._id; }
3358 + if (typeof mesh == 'string') { mesh = meshes[mesh] }
3359 + if ((mesh == null) || (mesh.links == null)) { return 0; }
3360 + var rights = mesh.links[user];
3361 + if (rights == null) { return 0; }
3362 + return rights.rights;
3363 + }
3364 +
3365 + // Returns true if the user can view the given device group
3366 + function IsMeshViewable(mesh, user) {
3367 + if (mesh == null) { return 0; }
3368 + if (user == null) { user = userinfo._id; }
3369 + if (typeof mesh == 'string') { mesh = meshes[mesh] }
3370 + if ((mesh == null) || (mesh.links == null)) { return false; }
3371 + var rights = mesh.links[user];
3372 + if (rights == null) { return false; }
3373 + return true;
3374 + }
3375 +
3376 + // Return the user rights for a given node
3377 + function GetNodeRights(node, user) {
3378 + if (node == null) { return 0; }
3379 + if (user == null) { user = userinfo._id; }
3380 + if (typeof node == 'string') { node = getNodeFromId(node); if (node == null) { return 0; } }
3381 + var mesh = meshes[node.meshid];
3382 + if ((mesh == null) || (mesh.links == null)) { return 0; }
3383 + var meshlinks = mesh.links[user];
3384 + if (meshlinks == null) { return 0; }
3385 + return meshlinks.rights;
3386 + }
3387 +
3388 + //
3389 + // Generic Methods
3390 + //
3391 +
3392 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) }); } }
3393 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; } }
3394 function center() { QS('dialog').left = ((((getDocWidth() - 300) / 2)) + 'px'); deskAdjust(); deskAdjust(); /*drawDeviceTimeline();*/ }
views/default.handlebars
+115 -91
@@ -2587,8 +2587,7 @@
2587 if (!xxdialogMode && xxcurrentView == 11 && desktop && Q('DeskControl').checked) {
2588 // Check what keys we are allows to send
2589 if (currentNode != null) {
2590 - var mesh = meshes[currentNode.meshid];
2591 - var meshrights = mesh.links[userinfo._id].rights;
2590 + var meshrights = GetMeshRights(currentNode.meshid);
2591 var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
2592 if (inputAllowed == false) return false;
2593 var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
@@ -2647,8 +2646,7 @@
2646 if (!xxdialogMode && xxcurrentView == 11 && desktop && Q('DeskControl').checked) {
2647 // Check what keys we are allows to send
2648 if (currentNode != null) {
2650 - var mesh = meshes[currentNode.meshid];
2651 - var meshrights = mesh.links[userinfo._id].rights;
2649 + var meshrights = GetMeshRights(currentNode.meshid);
2650 var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
2651 if (inputAllowed == false) return false;
2652 var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
@@ -2682,8 +2680,7 @@
2680 if (!xxdialogMode && xxcurrentView == 11 && desktop && Q('DeskControl').checked) {
2681 // Check what keys we are allows to send
2682 if (currentNode != null) {
2685 - var mesh = meshes[currentNode.meshid];
2686 - var meshrights = mesh.links[userinfo._id].rights;
2683 + var meshrights = GetMeshRights(currentNode.meshid);
2684 var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
2685 if (inputAllowed == false) return false;
2686 var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
@@ -2783,10 +2780,9 @@
2780 for (var i in nodes) {
2781 var node = nodes[i];
2782 if (node.v == false) continue;
2786 - var mesh2 = meshes[node.meshid], meshlinks = mesh2.links[userinfo._id];
2787 - if (meshlinks == null) continue;
2788 - var meshrights = meshlinks.rights;
2783 + var mesh2 = meshes[node.meshid];
2784 if ((view == 3) && (mesh2.mtype == 1)) continue;
2785 + var meshrights = GetNodeRights(node);
2786 if (sort == 0) {
2787 // Mesh header
2788 if (node.meshid != current) {
@@ -2948,37 +2944,34 @@
2944 if ((sort == 0) && (Q('SearchInput').value == '') && (view < 3)) {
2945 var deviceHeaderId2 = deviceHeaderId;
2946 for (var i in meshes) {
2951 - var mesh = meshes[i], meshlink = mesh.links[userinfo._id];
2952 - if (meshlink != null) {
2953 - var meshrights = meshlink.rights;
2954 - if (displayedMeshes[mesh._id] == null) {
2955 - if ((current != '') && (r != '')) { r += '</tr></table>'; }
2956 - r += '<table style=width:100%;padding-top:4px cellpadding=0 cellspacing=0><tr><td colspan=3 class=DevSt>';
2947 + var mesh = meshes[i], meshrights = GetMeshRights(mesh);
2948 + if (displayedMeshes[mesh._id] == null) {
2949 + if ((current != '') && (r != '')) { r += '</tr></table>'; }
2950 + r += '<table style=width:100%;padding-top:4px cellpadding=0 cellspacing=0><tr><td colspan=3 class=DevSt>';
2951
2958 - // Collapsing header & start collapsing area
2959 - deviceHeaderId2++;
2960 - var collapsed = CollapsedGroups[mesh._id];
2961 - r += '<img class=collapseImage id=\"DevxColImg' + deviceHeaderId2 + '\" src=images/c' + ((collapsed === true)?'1':'2') + '.png height=8 width=8 style=margin-left:2px;margin-right:2px;cursor:pointer onclick=toggleCollapseGroup(\"' + deviceHeaderId2 + '\",\"' + mesh._id + '\")></img>'; // Collapse action
2962 -
2963 - r += '<span id=MxMESH style=cursor:pointer onclick=gotoMesh("' + mesh._id + '")>' + EscapeHtml(mesh.name) + '</span><span>';
2964 - r += getMeshActions(mesh, meshrights);
2965 - r += '</span></td></tr><tr>';
2966 - if (mesh.mtype == 1) {
2967 - r += '<td><div style=padding:10px><i>' + "No Intel&reg; AMT devices in this mesh";
2968 - if ((meshrights & 4) != 0) { r += ', <a href=# style=cursor:pointer onclick=\'return addDeviceToMesh(\"' + mesh._id + '\")\'>' + "add one" + '</a>'; }
2969 - }
2970 - if (mesh.mtype == 2) {
2971 - r += '<td>';
2972 - r += '<div id=DevxCol' + deviceHeaderId2 + ((collapsed === true)?' style=display:none':'') + '>'; // Open collapse div
2973 - r += '<div style=padding:10px><i>' + "No devices in this group";
2974 - if ((meshrights & 4) != 0) { r += ', <a href=# style=cursor:pointer onclick=\'return addAgentToMesh(\"' + mesh._id + '\")\'>' + "add one" + '</a>'; }
2975 - }
2976 - r += '.</i></div></td>';
2977 - r += '</div>'; // End collapsing area
2978 -
2979 - current = mesh._id;
2980 - count++;
2952 + // Collapsing header & start collapsing area
2953 + deviceHeaderId2++;
2954 + var collapsed = CollapsedGroups[mesh._id];
2955 + r += '<img class=collapseImage id=\"DevxColImg' + deviceHeaderId2 + '\" src=images/c' + ((collapsed === true)?'1':'2') + '.png height=8 width=8 style=margin-left:2px;margin-right:2px;cursor:pointer onclick=toggleCollapseGroup(\"' + deviceHeaderId2 + '\",\"' + mesh._id + '\")></img>'; // Collapse action
2956 +
2957 + r += '<span id=MxMESH style=cursor:pointer onclick=gotoMesh("' + mesh._id + '")>' + EscapeHtml(mesh.name) + '</span><span>';
2958 + r += getMeshActions(mesh, meshrights);
2959 + r += '</span></td></tr><tr>';
2960 + if (mesh.mtype == 1) {
2961 + r += '<td><div style=padding:10px><i>' + "No Intel&reg; AMT devices in this mesh";
2962 + if ((meshrights & 4) != 0) { r += ', <a href=# style=cursor:pointer onclick=\'return addDeviceToMesh(\"' + mesh._id + '\")\'>' + "add one" + '</a>'; }
2963 + }
2964 + if (mesh.mtype == 2) {
2965 + r += '<td>';
2966 + r += '<div id=DevxCol' + deviceHeaderId2 + ((collapsed === true)?' style=display:none':'') + '>'; // Open collapse div
2967 + r += '<div style=padding:10px><i>' + "No devices in this group";
2968 + if ((meshrights & 4) != 0) { r += ', <a href=# style=cursor:pointer onclick=\'return addAgentToMesh(\"' + mesh._id + '\")\'>' + "add one" + '</a>'; }
2969 }
2970 + r += '.</i></div></td>';
2971 + r += '</div>'; // End collapsing area
2972 +
2973 + current = mesh._id;
2974 + count++;
2975 }
2976 }
2977 }
@@ -3107,8 +3100,8 @@
3100
3101 function toggleKvmDevice(node) {
3102 if (typeof node == 'string') { node = getNodeFromId(node); } // Convert nodeid to node if needed
3110 - var mesh = meshes[node.meshid], meshrights = mesh.links[userinfo._id].rights;
3111 - if ((meshrights & 8) || (meshrights & 256)) { // Requires remote control rights or desktop view only rights
3103 + var rights = GetNodeRights(node);
3104 + if ((rights & 8) || (rights & 256)) { // Requires remote control rights or desktop view only rights
3105 //var conn = 0;
3106 //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)
3107 if (node.conn & 1) { connectMultiDesktop(node, 1); }
@@ -3133,8 +3126,8 @@
3126 for (var i in nodes) {
3127 var node = nodes[i], nodeid = nodes[i]._id;
3128 if ((multiDesktop[nodeid] == null) && ((multiDesktopFilter.length == 0) || (multiDesktopFilter.indexOf('devid_' + nodeid) >= 0))) {
3136 - var mesh = meshes[node.meshid], meshrights = mesh.links[userinfo._id].rights;
3137 - if ((meshrights & 8) || (meshrights & 256)) { // Requires remote control rights or desktop view only rights
3129 + var rights = GetNodeRights(node);
3130 + if ((rights & 8) || (rights & 256)) { // Requires remote control rights or desktop view only rights
3131 //var conn = 0;
3132 //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)
3133 if ((node.conn & 1) && (node.v == true)) { count++; }
@@ -3646,9 +3639,8 @@
3639 // Display the "Uninstall Agent" option if allowed and we selected connected devices.
3640 for (var i in nodeids) {
3641 var node = getNodeFromId(nodeids[i]);
3649 - var mesh = meshes[node.meshid];
3650 - var meshrights = mesh.links[userinfo._id].rights;
3651 - if (((node.conn & 1) != 0) && ((meshrights & 32768) != 0)) { addedOptions += '<option value=104>' + "Uninstall Agent" + '</option>'; break; }
3642 + var rights = GetNodeRights(node);
3643 + if (((node.conn & 1) != 0) && ((rights & 32768) != 0)) { addedOptions += '<option value=104>' + "Uninstall Agent" + '</option>'; break; }
3644 }
3645
3646 var x = "Select an operation to perform on all selected devices. Actions will be performed only with proper rights." + '<br /><br />';
@@ -3790,19 +3782,18 @@
3782 var nodeid = contextelement.children[1].attributes.onclick.value;
3783 var node = getNodeFromId(nodeid.substring(12, nodeid.length - 18));
3784 var mesh = meshes[node.meshid];
3793 - var meshlinks = mesh.links[userinfo._id];
3794 - var meshrights = meshlinks.rights;
3795 - var consoleRights = ((meshrights & 16) != 0);
3785 + var rights = GetNodeRights(node);
3786 + var consoleRights = ((rights & 16) != 0);
3787
3788 // Check if we have terminal and file access
3798 - var terminalAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 512) == 0));
3799 - var fileAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 1024) == 0));
3789 + var terminalAccess = ((rights == 0xFFFFFFFF) || ((rights & 512) == 0));
3790 + var fileAccess = ((rights == 0xFFFFFFFF) || ((rights & 1024) == 0));
3791
3801 - 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)));
3802 - 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);
3803 - QV('cxfiles', ((mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 4) != 0))) && (meshrights & 8) && fileAccess);
3804 - QV('cxevents', (node.intelamt != null) && ((node.intelamt.state == 2) || (node.conn & 2)) && (meshrights & 8));
3805 - QV('cxconsole', (consoleRights && (mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 8) != 0))) && (meshrights & 8));
3792 + 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)));
3793 + 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);
3794 + QV('cxfiles', ((mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 4) != 0))) && (rights & 8) && fileAccess);
3795 + QV('cxevents', (node.intelamt != null) && ((node.intelamt.state == 2) || (node.conn & 2)) && (rights & 8));
3796 + QV('cxconsole', (consoleRights && (mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 8) != 0))) && (rights & 8));
3797 }
3798
3799 return haltEvent(event);
@@ -4488,11 +4479,6 @@
4479 gotoDevice(nodeid, xxcurrentView, true);
4480 }
4481
4491 - function getNodeRights(nodeid) {
4492 - var node = getNodeFromId(nodeid), mesh = meshes[node.meshid];
4493 - return mesh.links[userinfo._id].rights;
4494 - }
4495 -
4482 var currentNode;
4483 var powerTimelineNode = null;
4484 var powerTimelineReq = null;
@@ -4515,7 +4501,7 @@
4501 //disconnectAllKvmFunction();
4502 var node = getNodeFromId(nodeid);
4503 var mesh = meshes[node.meshid];
4518 - var meshrights = mesh.links[userinfo._id].rights;
4504 + var meshrights = GetNodeRights(node);
4505 if (!currentNode || currentNode._id != node._id || refresh == true) {
4506 currentNode = node;
4507
@@ -4857,13 +4843,13 @@
4843
4844 function deviceActionFunction() {
4845 if (xxdialogMode) return;
4860 - var meshrights = meshes[currentNode.meshid].links[userinfo._id].rights;
4846 + var rights = GetNodeRights(currentNode);
4847 var x = "Select an operation to perform on this device." + '<br /><br />';
4848 var y = '<select id=d2deviceop style=float:right;width:250px>';
4863 - if ((meshrights & 64) != 0) { y += '<option value=100>' + "Wake-up" + '</option>'; } // Wake-up permission
4864 - if ((meshrights & 8) != 0) { y += '<option value=4>' + "Sleep" + '</option><option value=3>' + "Reset" + '</option><option value=2>' + "Power off" + '</option>'; } // Remote control permission
4849 + if ((rights & 64) != 0) { y += '<option value=100>' + "Wake-up" + '</option>'; } // Wake-up permission
4850 + if ((rights & 8) != 0) { y += '<option value=4>' + "Sleep" + '</option><option value=3>' + "Reset" + '</option><option value=2>' + "Power off" + '</option>'; } // Remote control permission
4851 if ((currentNode.conn & 16) != 0) { y += '<option value=103>' + "Send MQTT Message" + '</option>'; }
4866 - if (((currentNode.conn & 1) != 0) && ((meshrights & 32768) != 0)) { y += '<option value=104>' + "Uninstall Agent" + '</option>'; }
4852 + if (((currentNode.conn & 1) != 0) && ((rights & 32768) != 0)) { y += '<option value=104>' + "Uninstall Agent" + '</option>'; }
4853 y += '</select>';
4854 x += addHtmlValue("Operation", y);
4855 setDialogMode(2, "Device Action", 3, deviceActionFunctionEx, x);
@@ -4975,7 +4961,7 @@
4961
4962 function editDeviceAmtSettings(nodeid, func, arg) {
4963 if (xxdialogMode) return;
4978 - var x = '', node = getNodeFromId(nodeid), buttons = 3, meshrights = getNodeRights(nodeid);
4964 + var x = '', node = getNodeFromId(nodeid), buttons = 3, meshrights = GetNodeRights(node);
4965 if ((meshrights & 4) == 0) return;
4966 x += addHtmlValue("Username", '<input id=dp10username style=width:230px maxlength=32 autocomplete=nope placeholder="admin" onchange=validateDeviceAmtSettings() onkeyup=validateDeviceAmtSettings() />');
4967 x += addHtmlValue("Password", '<input id=dp10password type=password style=width:230px autocomplete=nope maxlength=32 onchange=validateDeviceAmtSettings() onkeyup=validateDeviceAmtSettings() />');
@@ -5049,7 +5035,7 @@
5035 // List all available alternative groups
5036 var y = '<select id=p10newGroup style=width:236px>', count = 0;
5037 for (var i in meshes) {
5052 - var meshrights = meshes[i].links[userinfo._id].rights;
5038 + var meshrights = GetMeshRights(i);
5039 if ((meshes[i]._id != targetMeshId) && (meshrights & 4)) { count++; y += '<option value=\'' + meshes[i]._id + '\'>' + meshes[i].name + '</option>'; }
5040 }
5041 y += '</select>';
@@ -5195,17 +5181,16 @@
5181
5182 function p10showiconselector() {
5183 if (xxdialogMode) return;
5198 - var mesh = meshes[currentNode.meshid];
5199 - var meshrights = mesh.links[userinfo._id].rights;
5200 - if ((meshrights & 4) == 0) return;
5184 + if ((GetNodeRights(currentNode) & 4) == 0) return;
5185
5202 - var x = '<br><div style=display:inline-block;width:40px></div>';
5186 + var x = '<br><div style=display:inline-block;width:16px></div>';
5187 x += '<div tabindex=0 style=display:inline-block class=i1 onclick=p10setIcon(1) onkeypress="if (event.key==\'Enter\') p10setIcon(1)"></div>';
5188 x += '<div tabindex=0 style=display:inline-block class=i2 onclick=p10setIcon(2) onkeypress="if (event.key==\'Enter\') p10setIcon(2)"></div>';
5189 x += '<div tabindex=0 style=display:inline-block class=i3 onclick=p10setIcon(3) onkeypress="if (event.key==\'Enter\') p10setIcon(3)"></div>';
5190 x += '<div tabindex=0 style=display:inline-block class=i4 onclick=p10setIcon(4) onkeypress="if (event.key==\'Enter\') p10setIcon(4)"></div>';
5191 x += '<div tabindex=0 style=display:inline-block class=i5 onclick=p10setIcon(5) onkeypress="if (event.key==\'Enter\') p10setIcon(5)"></div>';
5208 - x += '<div tabindex=0 style=display:inline-block class=i6 onclick=p10setIcon(6) onkeypress="if (event.key==\'Enter\') p10setIcon(6)"></div><br><br>';
5192 + x += '<div tabindex=0 style=display:inline-block class=i6 onclick=p10setIcon(6) onkeypress="if (event.key==\'Enter\') p10setIcon(6)"></div>';
5193 + x += '<div tabindex=0 style=display:inline-block class=i7 onclick=p10setIcon(7) onkeypress="if (event.key==\'Enter\') p10setIcon(7)"></div><br><br>';
5194 setDialogMode(2, "Icon Selection", 0, null, x);
5195 QV('id_dialogclose', true);
5196 }
@@ -5292,14 +5277,14 @@
5277 var mesh = meshes[currentNode.meshid];
5278 var deskState = 0;
5279 if (desktop != null) { deskState = desktop.State; }
5295 - var meshrights = mesh.links[userinfo._id].rights;
5280 + var rights = GetNodeRights(currentNode);
5281
5282 // Show the right buttons
5283 QV('disconnectbutton1span', (deskState != 0));
5299 - QV('connectbutton1span', (deskState == 0) && ((meshrights & 8) || (meshrights & 256)) && (mesh.mtype == 2) && (currentNode.agent.caps & 1));
5284 + QV('connectbutton1span', (deskState == 0) && ((rights & 8) || (rights & 256)) && (mesh.mtype == 2) && (currentNode.agent.caps & 1));
5285 QV('connectbutton1hspan',
5286 (deskState == 0) &&
5302 - (meshrights & 8) &&
5287 + (rights & 8) &&
5288 ((mesh.mtype == 1) ||
5289 ((currentNode.intelamt != null) &&
5290 (currentNode.intelamt.state == 2) &&
@@ -5314,7 +5299,7 @@
5299 QV('d7meshkvm', (webRtcDesktop) || ((mesh.mtype == 2) && (currentNode.agent.caps & 1) && ((deskState == false) || (desktop.contype == 1))));
5300
5301 // Enable buttons
5317 - var inputAllowed = (meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) == 0));
5302 + var inputAllowed = (rights == 0xFFFFFFFF) || (((rights & 8) != 0) && ((rights & 256) == 0) && ((rights & 4096) == 0));
5303 var online = ((currentNode.conn & 1) != 0); // If Agent (1) connected, enable remote desktop
5304 QE('connectbutton1', online);
5305 var hwonline = ((currentNode.conn & 6) != 0); // If CIRA (2) or AMT (4) connected, enable hardware terminal
@@ -5330,8 +5315,8 @@
5315 QE('deskkeys', deskState == 3);
5316
5317 // Display this only if we have Chat & Notify permissions
5333 - QV('DeskChatButton', ((meshrights & 16384) != 0) && (browserfullscreen == false) && (inputAllowed) && (mesh.mtype == 2) && online);
5334 - QV('DeskNotifyButton', ((meshrights & 16384) != 0) && (browserfullscreen == false) && (currentNode.agent) && (currentNode.agent.id < 5) && (inputAllowed) && (mesh.mtype == 2) && online);
5318 + QV('DeskChatButton', ((rights & 16384) != 0) && (browserfullscreen == false) && (inputAllowed) && (mesh.mtype == 2) && online);
5319 + QV('DeskNotifyButton', ((rights & 16384) != 0) && (browserfullscreen == false) && (currentNode.agent) && (currentNode.agent.id < 5) && (inputAllowed) && (mesh.mtype == 2) && online);
5320
5321 QV('DeskToolsButton', (inputAllowed) && (mesh.mtype == 2) && online);
5322 QV('DeskOpenWebButton', (browserfullscreen == false) && (inputAllowed) && (mesh.mtype == 2) && online);
@@ -5339,7 +5324,7 @@
5324 QV('DeskControlSpan', inputAllowed)
5325 QV('deskActionsBtn', (browserfullscreen == false));
5326 QV('deskActionsSettings', (browserfullscreen == false));
5342 - if (meshrights & 8) { Q('DeskControl').checked = (getstore('DeskControl', 1) == 1); } else { Q('DeskControl').checked = false; }
5327 + if (rights & 8) { Q('DeskControl').checked = (getstore('DeskControl', 1) == 1); } else { Q('DeskControl').checked = false; }
5328 if (online == false) QV('DeskTools', false);
5329 }
5330
@@ -6966,8 +6951,8 @@
6951 consoleNode = currentNode;
6952
6953 var mesh = meshes[consoleNode.meshid];
6969 - var meshrights = mesh.links[userinfo._id].rights;
6970 - if ((meshrights & 16) != 0) {
6954 + var rights = GetNodeRights(currentNode);
6955 + if ((rights & 16) != 0) {
6956 if (consoleNode.consoleText == null) { consoleNode.consoleText = ''; }
6957 if (samenode == false) {
6958 QH('p15agentConsoleText', consoleNode.consoleText);
@@ -7443,8 +7428,7 @@
7428 count++;
7429
7430 // Mesh rights
7446 - var meshrights = 0;
7447 - if (meshes[i].links[userinfo._id]) { meshrights = meshes[i].links[userinfo._id].rights; }
7431 + var meshrights = GetMeshRights(meshes[i]);
7432 var rights = "Partial Rights";
7433 if (meshrights == 0xFFFFFFFF) rights = "Full Administrator"; else if (meshrights == 0) rights = "No Rights";
7434
@@ -7511,8 +7495,7 @@
7495 if (currentMesh == null) return;
7496 QH('p20meshName', EscapeHtml(currentMesh.name));
7497 var meshtype = format("Unknown #{0}", currentMesh.mtype);
7514 - var meshrights = 0;
7515 - try { meshrights = currentMesh.links[userinfo._id].rights; } catch (ex) { }
7498 + var meshrights = GetMeshRights(currentMesh);
7499 if (currentMesh.mtype == 1) meshtype = "Intel&reg; AMT only, no agent";
7500 if (currentMesh.mtype == 2) meshtype = "Managed using a software agent";
7501
@@ -7580,10 +7563,9 @@
7563 if (meshrights & 1) { x += '<br><input type=button value=' + "Notes" + ' title=\"' + "View notes about this device group" + '\" onclick=showNotes(false,"' + encodeURIComponent(currentMesh._id) + '") />'; }
7564
7565 x += '<br style=clear:both><br>';
7583 - var currentMeshLinks = currentMesh.links[userinfo._id];
7584 - if (currentMeshLinks && ((currentMeshLinks.rights & 2) != 0)) { x += '<a href=# onclick="return p20showAddMeshUserDialog()" style=cursor:pointer;margin-right:10px><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Add Users" + '</a>'; }
7566 + if (meshrights & 2) { x += '<a href=# onclick="return p20showAddMeshUserDialog()" style=cursor:pointer;margin-right:10px><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Add Users" + '</a>'; }
7567
7586 - if ((meshrights & 4) != 0) {
7568 + if (meshrights & 4) {
7569 if (currentMesh.mtype == 1) {
7570 x += '<a href=# onclick=\'return addCiraDeviceToMesh(\"' + currentMesh._id + '\")\' style=cursor:pointer;margin-right:10px title=\"' + "Add a new Intel&reg; AMT computer that is located on the internet." + '\"><img src=images/icon-installmesh.png border=0 height=12 width=12> ' + "Install CIRA" + '</a>';
7571 x += '<a href=# onclick=\'return addDeviceToMesh(\"' + currentMesh._id + '\")\' style=cursor:pointer;margin-right:10px title=\"' + "Add a new Intel&reg; AMT computer that is located on the local network." + '\"><img src=images/icon-installmesh.png border=0 height=12 width=12> ' + "Install local" + '</a>';
@@ -7838,7 +7820,7 @@
7820 Q('dp20username').focus();
7821 } else {
7822 setDialogMode(2, "Edit User Device Group Permissions", 7, p20showAddMeshUserDialogEx, x, userid);
7841 - var cmeshrights = currentMesh.links[userinfo._id].rights, meshrights = currentMesh.links[userid].rights;
7823 + var cmeshrights = GetMeshRights(currentMesh), meshrights = GetMeshRights(currentMesh, userid);
7824 if (meshrights == 0xFFFFFFFF) {
7825 Q('p20fulladmin').checked = true;
7826 } else {
@@ -7877,7 +7859,7 @@
7859 }
7860
7861 function p20validateAddMeshUserDialog() {
7880 - var meshrights = currentMesh.links[userinfo._id].rights;
7862 + var meshrights = GetMeshRights(currentMesh);
7863 var ok = true;
7864 if (Q('dp20username')) {
7865 var xusers = Q('dp20username').value.split(',');
@@ -7964,7 +7946,7 @@
7946 function p20viewuser(userid) {
7947 if (xxdialogMode) return;
7948 var xuserid = decodeURIComponent(userid);
7967 - var cmeshrights = currentMesh.links[userinfo._id].rights, meshrights = currentMesh.links[xuserid].rights;
7949 + var cmeshrights = GetMeshRights(currentMesh), meshrights = GetMeshRights(currentMesh, xuserid);
7950 if (((userinfo._id) != xuserid) && (cmeshrights == 0xFFFFFFFF || (((cmeshrights & 2) != 0) && (meshrights != 0xFFFFFFFF)))) {
7951 p20showAddMeshUserDialog(userid);
7952 } else {
@@ -10063,7 +10045,49 @@
10045 if (pname == null) { Q('p43iframe').src = ''; } else { QH('p43title', title); Q('p43iframe').src = '/pluginadmin.ashx?pin=' + pname; go(43); }
10046 }
10047
10048 + //
10049 + // Access Control Functions
10050 + // These must match server
10051 + //
10052 +
10053 + // Get the right of a user on a given device group
10054 + function GetMeshRights(mesh, user) {
10055 + if (mesh == null) { return 0; }
10056 + if (user == null) { user = userinfo._id; }
10057 + if (typeof mesh == 'string') { mesh = meshes[mesh] }
10058 + if ((mesh == null) || (mesh.links == null)) { return 0; }
10059 + var rights = mesh.links[user];
10060 + if (rights == null) { return 0; }
10061 + return rights.rights;
10062 + }
10063 +
10064 + // Returns true if the user can view the given device group
10065 + function IsMeshViewable(mesh, user) {
10066 + if (mesh == null) { return 0; }
10067 + if (user == null) { user = userinfo._id; }
10068 + if (typeof mesh == 'string') { mesh = meshes[mesh] }
10069 + if ((mesh == null) || (mesh.links == null)) { return false; }
10070 + var rights = mesh.links[user];
10071 + if (rights == null) { return false; }
10072 + return true;
10073 + }
10074 +
10075 + // Return the user rights for a given node
10076 + function GetNodeRights(node, user) {
10077 + if (node == null) { return 0; }
10078 + if (user == null) { user = userinfo._id; }
10079 + if (typeof node == 'string') { node = getNodeFromId(node); if (node == null) { return 0; } }
10080 + var mesh = meshes[node.meshid];
10081 + if ((mesh == null) || (mesh.links == null)) { return 0; }
10082 + var meshlinks = mesh.links[user];
10083 + if (meshlinks == null) { return 0; }
10084 + return meshlinks.rights;
10085 + }
10086 +
10087 + //
10088 // Generic methods
10089 + //
10090 +
10091 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('/'); }
10092 function putstore(name, val) {
10093 try {
webserver.js
+44 -32
@@ -2065,7 +2065,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
2065 if ((splitpath.length < 3) || (splitpath[0] != 'user' && splitpath[0] != 'mesh') || (splitpath[1] != domain.id)) return null; // Basic validation
2066 var objid = splitpath[0] + '/' + splitpath[1] + '/' + splitpath[2];
2067 if (splitpath[0] == 'user' && (objid != user._id)) return null; // User validation, only self allowed
2068 - 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
2068 + if (splitpath[0] == 'mesh') { if ((obj.GetMeshRights(user, objid) & 32) == 0) { return null; } } // Check mesh server file rights
2069 if (splitpath[1] != '') { serverpath += '-' + splitpath[1]; } // Add the domain if needed
2070 serverpath += ('/' + splitpath[0] + '-' + splitpath[2]);
2071 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) {
2265 if (!node.intelamt) { console.log('ERR: Not AMT node'); try { ws.close(); } catch (e) { } return; } // Disconnect websocket
2266
2267 // Check if this user has permission to manage this computer
2268 - var meshlinks = user.links[node.meshid];
2269 - if ((!meshlinks) || (!meshlinks.rights) || ((meshlinks.rights & MESHRIGHT_REMOTECONTROL) == 0)) { console.log('ERR: Access denied (2)'); try { ws.close(); } catch (e) { } return; }
2268 + if ((obj.GetMeshRights(user, node.meshid) & MESHRIGHT_REMOTECONTROL) == 0) { console.log('ERR: Access denied (2)'); try { ws.close(); } catch (e) { } return; }
2269
2270 // Check what connectivity is available for this node
2271 var state = parent.GetConnectivityState(req.query.host);
@@ -2998,9 +2997,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
2997
2998 // If required, check if this user has rights to do this
2999 if ((obj.parent.config.settings != null) && ((obj.parent.config.settings.lockagentdownload == true) || (domain.lockagentdownload == true))) {
3001 - var user = obj.users[req.session.userid];
3002 - if ((user == null) || (mesh.links[user._id] == null) || ((mesh.links[user._id].rights & 1) == 0)) { res.sendStatus(401); return; }
3003 - if (domain.id != mesh.domain) { res.sendStatus(401); return; }
3000 + if ((domain.id != mesh.domain) || ((obj.GetMeshRights(req.session.userid, mesh) & 1) == 0)) { res.sendStatus(401); return; }
3001 }
3002
3003 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) {
3165
3166 // If required, check if this user has rights to do this
3167 if ((obj.parent.config.settings != null) && ((obj.parent.config.settings.lockagentdownload == true) || (domain.lockagentdownload == true))) {
3171 - var user = obj.users[req.session.userid];
3172 - if ((user == null) || (mesh.links[user._id] == null) || ((mesh.links[user._id].rights & 1) == 0)) { res.sendStatus(401); return; }
3173 - if (domain.id != mesh.domain) { res.sendStatus(401); return; }
3168 + if ((domain.id != mesh.domain) || ((obj.GetMeshRights(req.session.userid, mesh) & 1) == 0)) { res.sendStatus(401); return; }
3169 }
3170
3171 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) {
3252
3253 // If needed, check if this user has rights to do this
3254 if ((obj.parent.config.settings != null) && ((obj.parent.config.settings.lockagentdownload == true) || (domain.lockagentdownload == true))) {
3260 - var user = obj.users[req.session.userid];
3261 - if ((user == null) || (mesh.links[user._id] == null) || ((mesh.links[user._id].rights & 1) == 0)) { res.sendStatus(401); return; }
3262 - if (domain.id != mesh.domain) { res.sendStatus(401); return; }
3255 + if ((domain.id != mesh.domain) || ((obj.GetMeshRights(req.session.userid, mesh) & 1) == 0)) { res.sendStatus(401); return; }
3256 }
3257
3258 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) {
3290 var node = docs[0];
3291
3292 // Check if we have right to this node
3300 - var rights = 0;
3301 - for (var i in user.links) { if (i == node.meshid) { rights = user.links[i].rights; } }
3302 - if (rights == 0) { res.sendStatus(401); return; }
3293 + if (obj.GetMeshRights(user, node.meshid) == 0) { res.sendStatus(401); return; }
3294
3295 // Get the list of power events and send them
3296 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) {
3763 if (agent == null) return;
3764
3765 // Check we have agent rights
3775 - var rights = user.links[agent.dbMeshKey].rights;
3776 - if ((rights != null) && ((rights & MESHRIGHT_AGENTCONSOLE) != 0) || (user.siteadmin == 0xFFFFFFFF)) { agent.close(disconnectMode); }
3766 + if (((obj.GetMeshRights(user, agent.dbMeshKey) & MESHRIGHT_AGENTCONSOLE) != 0) || (user.siteadmin == 0xFFFFFFFF)) { agent.close(disconnectMode); }
3767 };
3768
3769 // Send the core module to the mesh agent
@@ -3787,8 +3777,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3777 if (agent == null) return;
3778
3779 // Check we have agent rights
3790 - var rights = user.links[agent.dbMeshKey].rights;
3791 - if ((rights != null) && ((rights & MESHRIGHT_AGENTCONSOLE) != 0) || (user.siteadmin == 0xFFFFFFFF)) {
3780 + if (((obj.GetMeshRights(user, agent.dbMeshKey) & MESHRIGHT_AGENTCONSOLE) != 0) || (user.siteadmin == 0xFFFFFFFF)) {
3781 if (coretype == 'clear') {
3782 // Clear the mesh agent core
3783 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) {
3896 }
3897 };
3898
3899 + //
3900 + // Access Control Functions
3901 + //
3902 +
3903 + // Return the node and rights for a given nodeid
3904 + obj.GetNodeWithRights = function (domain, user, nodeid, func) {
3905 + // Perform user pre-validation
3906 + if ((user == null) || (nodeid == null)) { func(null, 0, false); return; } // Invalid user
3907 + if (typeof user == 'string') { user = obj.users[user]; }
3908 + if ((user == null) || (user.links == null)) { func(null, 0, false); return; } // No rights
3909 +
3910 + // Perform node pre-validation
3911 + if (obj.common.validateString(nodeid, 0, 128) == false) { func(null, 0, false); return; } // Invalid nodeid
3912 + const snode = nodeid.split('/');
3913 + if ((snode.length != 3) || (snode[0] != 'node')) { func(null, 0, false); return; } // Invalid nodeid
3914 + if ((domain != null) && (snode[1] != domain.id)) { func(null, 0, false); return; } // Invalid domain
3915 +
3916 + // Check that we have permissions for this node.
3917 + db.Get(nodeid, function (err, nodes) {
3918 + if ((nodes == null) || (nodes.length != 1)) { func(null, 0, false); return; } // No such nodeid
3919 + var rights = user.links[nodes[0].meshid];
3920 + if (rights == null) { func(null, 0, false); return; } // No rights to this mesh
3921 + func(nodes[0], rights.rights, true);
3922 + });
3923 + }
3924 +
3925 // Returns a list of all meshes that this user has some rights too
3926 obj.GetAllMeshWithRights = function (user, rights) {
3927 if (typeof user == 'string') { user = obj.users[user]; }
@@ -4076,9 +4091,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
4091 if (typeof command.sessionid != 'string') return;
4092 var splitsessionid = command.sessionid.split('/');
4093 // Check that we are in the same domain and the user has rights over this node.
4079 - if ((splitsessionid[0] == 'user') && (splitsessionid[1] == domainid)) {
4094 + if ((splitsessionid.length == 4) && (splitsessionid[0] == 'user') && (splitsessionid[1] == domainid)) {
4095 // Check if this user has rights to get this message
4081 - //if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 16) == 0)) return; // TODO!!!!!!!!!!!!!!!!!!!!!
4096 + if (obj.GetMeshRights(splitsessionid[0] + '/' + splitsessionid[1] + '/' + splitsessionid[2], meshid) == 0) return; // TODO: Check if this is ok
4097
4098 // See if the session is connected. If so, go ahead and send this message to the target node
4099 var ws = obj.wssessions2[command.sessionid];
@@ -4101,7 +4116,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
4116 // Check that we are in the same domain and the user has rights over this node.
4117 if ((splituserid[0] == 'user') && (splituserid[1] == domainid)) {
4118 // Check if this user has rights to get this message
4104 - //if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 16) == 0)) return; // TODO!!!!!!!!!!!!!!!!!!!!!
4119 + if (obj.GetMeshRights(command.userid, meshid) == 0) return; // TODO: Check if this is ok
4120
4121 // See if the session is connected
4122 var sessions = obj.wssessions[command.userid];
@@ -4120,16 +4135,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
4135 } else { // Route this command to the mesh
4136 command.nodeid = nodeid;
4137 var cmdstr = JSON.stringify(command);
4123 - for (var userid in obj.wssessions) { // Find all connected users for this mesh and send the message
4124 - var user = obj.users[userid];
4125 - if ((user != null) && (user.links != null)) {
4126 - var rights = user.links[meshid];
4127 - if (rights != null) { // TODO: Look at what rights are needed for message routing
4128 - var xsessions = obj.wssessions[userid];
4129 - // Send the message to all users on this server
4130 - for (i in xsessions) { try { xsessions[i].send(cmdstr); } catch (e) { } }
4131 - }
4132 - }
4138 + if (obj.GetMeshRights(userid, meshid) == 0) return; // TODO: Check if this is ok
4139 +
4140 + // Find all connected users for this mesh and send the message
4141 + for (var userid in obj.wssessions) {
4142 + var xsessions = obj.wssessions[userid];
4143 + // Send the message to all users on this server
4144 + for (i in xsessions) { try { xsessions[i].send(cmdstr); } catch (e) { } }
4145 }
4146
4147 // Send the message to all users of other servers