More access control improvements, RPi icon.

This commit is contained in:
Ylian Saint-Hilaire 2019-12-27 15:18:43 -08:00
parent e80acec96b
commit c3efdb39c1
16 changed files with 699 additions and 833 deletions

View File

@ -1919,13 +1919,10 @@ function createMeshCore(agent) {
switch (cmd) {
case 'help': { // Displays available commands
var fin = '', f = '', availcommands = 'version,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,amt,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,sendcaps,openurl,amtreset,amtccm,amtacm,amtdeactivate,amtpolicy,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,scanamt,wallpaper';
if (process.platform == 'win32')
{
availcommands += ',safemode,wpfhwacceleration';
}
if (process.platform == 'win32') { availcommands += ',safemode,wpfhwacceleration'; }
availcommands = availcommands.split(',').sort();
while (availcommands.length > 0) {
if (f.length > 100) { fin += (f + ',\r\n'); f = ''; }
if (f.length > 90) { fin += (f + ',\r\n'); f = ''; }
f += (((f != '') ? ', ' : ' ') + availcommands.shift());
}
if (f != '') { fin += f; }

View File

@ -12,10 +12,10 @@
/*jshint node: true */
/*jshint strict: false */
/*jshint esversion: 6 */
"use strict";
'use strict';
const crypto = require("crypto");
const common = require("./common.js");
const crypto = require('crypto');
const common = require('./common.js');
var HttpInterceptorAuthentications = {};
//var RedirInterceptorAuthentications = {};
@ -28,8 +28,8 @@ module.exports.CreateHttpInterceptor = function (args) {
obj.randomValueHex = function (len) { return crypto.randomBytes(Math.ceil(len / 2)).toString('hex').slice(0, len); };
obj.args = args;
obj.amt = { acc: "", mode: 0, count: 0, error: false }; // mode: 0:Header, 1:LengthBody, 2:ChunkedBody, 3:UntilClose
obj.ws = { acc: "", mode: 0, count: 0, error: false, authCNonce: obj.randomValueHex(10), authCNonceCount: 1 };
obj.amt = { acc: '', mode: 0, count: 0, error: false }; // mode: 0:Header, 1:LengthBody, 2:ChunkedBody, 3:UntilClose
obj.ws = { acc: '', mode: 0, count: 0, error: false, authCNonce: obj.randomValueHex(10), authCNonceCount: 1 };
obj.blockAmtStorage = false;
// Private method
@ -38,7 +38,7 @@ module.exports.CreateHttpInterceptor = function (args) {
// Process data coming from Intel AMT
obj.processAmtData = function (data) {
obj.amt.acc += data; // Add data to accumulator
data = "";
data = '';
var datalen = 0;
do {
datalen = data.length;
@ -53,7 +53,7 @@ module.exports.CreateHttpInterceptor = function (args) {
if (obj.amt.mode == 0) { // Header Mode
// Decode the HTTP header
headerend = obj.amt.acc.indexOf('\r\n\r\n');
if (headerend < 0) return "";
if (headerend < 0) return '';
var headerlines = obj.amt.acc.substring(0, headerend).split('\r\n');
obj.amt.acc = obj.amt.acc.substring(headerend + 4);
obj.amt.directive = headerlines[0].split(' ');
@ -98,7 +98,7 @@ module.exports.CreateHttpInterceptor = function (args) {
} else if (obj.amt.mode == 2) { // Chunked Body Mode
// Send data one chunk at a time
headerend = obj.amt.acc.indexOf('\r\n');
if (headerend < 0) return "";
if (headerend < 0) return '';
var chunksize = parseInt(obj.amt.acc.substring(0, headerend), 16);
if ((chunksize == 0) && (obj.amt.acc.length >= headerend + 4)) {
// Send the ending chunk (NOTE: We do not support trailing headers)
@ -114,16 +114,16 @@ module.exports.CreateHttpInterceptor = function (args) {
}
} else if (obj.amt.mode == 3) { // Until Close Mode
r = obj.amt.acc;
obj.amt.acc = "";
obj.amt.acc = '';
return r;
}
return "";
return '';
};
// Process data coming from the Browser
obj.processBrowserData = function (data) {
obj.ws.acc += data; // Add data to accumulator
data = "";
data = '';
var datalen = 0;
do {
datalen = data.length;
@ -138,7 +138,7 @@ module.exports.CreateHttpInterceptor = function (args) {
if (obj.ws.mode == 0) { // Header Mode
// Decode the HTTP header
headerend = obj.ws.acc.indexOf('\r\n\r\n');
if (headerend < 0) return "";
if (headerend < 0) return '';
var headerlines = obj.ws.acc.substring(0, headerend).split('\r\n');
obj.ws.acc = obj.ws.acc.substring(headerend + 4);
obj.ws.directive = headerlines[0].split(' ');
@ -199,7 +199,7 @@ module.exports.CreateHttpInterceptor = function (args) {
} else if (obj.amt.mode == 2) { // Chunked Body Mode
// Send data one chunk at a time
headerend = obj.amt.acc.indexOf('\r\n');
if (headerend < 0) return "";
if (headerend < 0) return '';
var chunksize = parseInt(obj.amt.acc.substring(0, headerend), 16);
if (isNaN(chunksize)) { // TODO: Check this path
// Chunk is not in this batch, move one
@ -226,10 +226,10 @@ module.exports.CreateHttpInterceptor = function (args) {
}
} else if (obj.ws.mode == 3) { // Until Close Mode
r = obj.ws.acc;
obj.ws.acc = "";
obj.ws.acc = '';
return r;
}
return "";
return '';
};
// Parse authentication values from the HTTP header
@ -249,9 +249,9 @@ module.exports.CreateHttpInterceptor = function (args) {
// Compute the MD5 digest hash for a set of values
obj.ComputeDigesthash = function (username, password, realm, method, path, qop, nonce, nc, cnonce) {
var ha1 = crypto.createHash('md5').update(username + ":" + realm + ":" + password).digest("hex");
var ha2 = crypto.createHash('md5').update(method + ":" + path).digest("hex");
return crypto.createHash('md5').update(ha1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + ha2).digest("hex");
var ha1 = crypto.createHash('md5').update(username + ':' + realm + ':' + password).digest('hex');
var ha2 = crypto.createHash('md5').update(method + ':' + path).digest('hex');
return crypto.createHash('md5').update(ha1 + ':' + nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + ha2).digest('hex');
};
return obj;
@ -266,8 +266,8 @@ module.exports.CreateRedirInterceptor = function (args) {
obj.randomValueHex = function (len) { return crypto.randomBytes(Math.ceil(len / 2)).toString('hex').slice(0, len); };
obj.args = args;
obj.amt = { acc: "", mode: 0, count: 0, error: false, direct: false };
obj.ws = { acc: "", mode: 0, count: 0, error: false, direct: false, authCNonce: obj.randomValueHex(10), authCNonceCount: 1 };
obj.amt = { acc: '', mode: 0, count: 0, error: false, direct: false };
obj.ws = { acc: '', mode: 0, count: 0, error: false, direct: false, authCNonce: obj.randomValueHex(10), authCNonceCount: 1 };
obj.RedirectCommands = { StartRedirectionSession: 0x10, StartRedirectionSessionReply: 0x11, EndRedirectionSession: 0x12, AuthenticateSession: 0x13, AuthenticateSessionReply: 0x14 };
obj.StartRedirectionSessionReplyStatus = { SUCCESS: 0, TYPE_UNKNOWN: 1, BUSY: 2, UNSUPPORTED: 3, ERROR: 0xFF };
@ -280,7 +280,7 @@ module.exports.CreateRedirInterceptor = function (args) {
// Process data coming from Intel AMT
obj.processAmtData = function (data) {
obj.amt.acc += data; // Add data to accumulator
data = "";
data = '';
var datalen = 0;
do { datalen = data.length; data += obj.processAmtDataEx(); } while (datalen != data.length); // Process as much data as possible
return data;
@ -298,11 +298,11 @@ module.exports.CreateRedirInterceptor = function (args) {
//console.log(obj.amt.acc.charCodeAt(0));
switch (obj.amt.acc.charCodeAt(0)) {
case obj.RedirectCommands.StartRedirectionSessionReply: {
if (obj.amt.acc.length < 4) return "";
if (obj.amt.acc.length < 4) return '';
if (obj.amt.acc.charCodeAt(1) == obj.StartRedirectionSessionReplyStatus.SUCCESS) {
if (obj.amt.acc.length < 13) return "";
if (obj.amt.acc.length < 13) return '';
var oemlen = obj.amt.acc.charCodeAt(12);
if (obj.amt.acc.length < 13 + oemlen) return "";
if (obj.amt.acc.length < 13 + oemlen) return '';
r = obj.amt.acc.substring(0, 13 + oemlen);
obj.amt.acc = obj.amt.acc.substring(13 + oemlen);
return r;
@ -310,9 +310,9 @@ module.exports.CreateRedirInterceptor = function (args) {
break;
}
case obj.RedirectCommands.AuthenticateSessionReply: {
if (obj.amt.acc.length < 9) return "";
if (obj.amt.acc.length < 9) return '';
var l = common.ReadIntX(obj.amt.acc, 5);
if (obj.amt.acc.length < 9 + l) return "";
if (obj.amt.acc.length < 9 + l) return '';
var authstatus = obj.amt.acc.charCodeAt(1);
var authType = obj.amt.acc.charCodeAt(4);
@ -337,17 +337,17 @@ module.exports.CreateRedirInterceptor = function (args) {
}
default: {
obj.amt.error = true;
return "";
return '';
}
}
}
return "";
return '';
};
// Process data coming from the Browser
obj.processBrowserData = function (data) {
obj.ws.acc += data; // Add data to accumulator
data = "";
data = '';
var datalen = 0;
do { datalen = data.length; data += obj.processBrowserDataEx(); } while (datalen != data.length); // Process as much data as possible
return data;
@ -356,39 +356,39 @@ module.exports.CreateRedirInterceptor = function (args) {
// Process data coming from the Browser in the accumulator
obj.processBrowserDataEx = function () {
var r;
if (obj.ws.acc.length == 0) return "";
if (obj.ws.acc.length == 0) return '';
if (obj.ws.direct == true) {
var data = obj.ws.acc;
obj.ws.acc = "";
obj.ws.acc = '';
return data;
} else {
switch (obj.ws.acc.charCodeAt(0)) {
case obj.RedirectCommands.StartRedirectionSession: {
if (obj.ws.acc.length < 8) return "";
if (obj.ws.acc.length < 8) return '';
r = obj.ws.acc.substring(0, 8);
obj.ws.acc = obj.ws.acc.substring(8);
return r;
}
case obj.RedirectCommands.EndRedirectionSession: {
if (obj.ws.acc.length < 4) return "";
if (obj.ws.acc.length < 4) return '';
r = obj.ws.acc.substring(0, 4);
obj.ws.acc = obj.ws.acc.substring(4);
return r;
}
case obj.RedirectCommands.AuthenticateSession: {
if (obj.ws.acc.length < 9) return "";
if (obj.ws.acc.length < 9) return '';
var l = common.ReadIntX(obj.ws.acc, 5);
if (obj.ws.acc.length < 9 + l) return "";
if (obj.ws.acc.length < 9 + l) return '';
var authType = obj.ws.acc.charCodeAt(4);
if (authType == obj.AuthenticationType.DIGEST && obj.args.user && obj.args.pass) {
var authurl = "/RedirectionService";
var authurl = '/RedirectionService';
if (obj.amt.digestRealm) {
// Replace this authentication digest with a server created one
// We have everything we need to authenticate
var nc = obj.ws.authCNonceCount;
obj.ws.authCNonceCount++;
var digest = obj.ComputeDigesthash(obj.args.user, obj.args.pass, obj.amt.digestRealm, "POST", authurl, obj.amt.digestQOP, obj.amt.digestNonce, nc, obj.ws.authCNonce);
var digest = obj.ComputeDigesthash(obj.args.user, obj.args.pass, obj.amt.digestRealm, 'POST', authurl, obj.amt.digestQOP, obj.amt.digestNonce, nc, obj.ws.authCNonce);
// Replace this authentication digest with a server created one
// We have everything we need to authenticate
@ -434,18 +434,18 @@ module.exports.CreateRedirInterceptor = function (args) {
}
default: {
obj.ws.error = true;
return "";
return '';
}
}
}
return "";
return '';
};
// Compute the MD5 digest hash for a set of values
obj.ComputeDigesthash = function (username, password, realm, method, path, qop, nonce, nc, cnonce) {
var ha1 = crypto.createHash('md5').update(username + ":" + realm + ":" + password).digest("hex");
var ha2 = crypto.createHash('md5').update(method + ":" + path).digest("hex");
return crypto.createHash('md5').update(ha1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + ha2).digest("hex");
var ha1 = crypto.createHash('md5').update(username + ':' + realm + ':' + password).digest('hex');
var ha2 = crypto.createHash('md5').update(method + ':' + path).digest('hex');
return crypto.createHash('md5').update(ha1 + ':' + nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + ha2).digest('hex');
};
return obj;

View File

@ -80,7 +80,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
var agent = parent.wsagents[command.nodeid];
if (agent != null) {
// Check if we have permission to send a message to that node
rights = user.links[agent.dbMeshKey];
rights = user.links[agent.dbMeshKey]; // TODO: Need to include user group / node rights
mesh = parent.meshes[agent.dbMeshKey];
if ((rights != null) && (mesh != null) || ((rights & 16) != 0)) { // TODO: 16 is console permission, may need more gradular permission checking
if (ws.sessionId) { command.sessionid = ws.sessionId; } // Set the session id, required for responses.
@ -98,7 +98,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
var routing = parent.parent.GetRoutingServerId(command.nodeid, 1); // 1 = MeshAgent routing type
if (routing != null) {
// Check if we have permission to send a message to that node
rights = user.links[routing.meshid];
rights = user.links[routing.meshid]; // TODO: Need to include user groups / node rights
mesh = parent.meshes[routing.meshid];
if (rights != null || ((rights & 16) != 0)) { // TODO: 16 is console permission, may need more gradular permission checking
if (ws.sessionId) { command.fromSessionid = ws.sessionId; } // Set the session id, required for responses.

View File

@ -152,7 +152,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
var agent = parent.wsagents[command.nodeid];
if (agent != null) {
// Check if we have permission to send a message to that node
var meshrights = parent.GetMeshRights(user, agent.dbMeshKey);
var meshrights = parent.GetMeshRights(user, agent.dbMeshKey); // TODO: We will need to get the rights for this specific node.
var mesh = parent.meshes[agent.dbMeshKey];
if ((mesh != null) && ((meshrights & MESHRIGHT_REMOTECONTROL) || (meshrights & MESHRIGHT_REMOTEVIEWONLY))) { // 8 is remote control permission, 256 is desktop read only
command.sessionid = ws.sessionId; // Set the session id, required for responses
@ -503,20 +503,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
case 'powertimeline':
{
// Perform pre-validation
if (common.validateString(command.nodeid, 0, 128) == false) break;
var snode = command.nodeid.split('/');
if ((snode.length != 3) || (snode[1] != domain.id)) break;
// Check that we have permissions for this node.
db.Get(command.nodeid, function (err, nodes) {
if (nodes == null || nodes.length != 1) return;
const node = nodes[0];
if (parent.GetMeshRights(user, node.meshid) != 0) {
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
if (visible == false) return;
// Query the database for the power timeline for a given node
// The result is a compacted array: [ startPowerState, startTimeUTC, powerState ] + many[ deltaTime, powerState ]
db.getPowerTimeline(command.nodeid, function (err, docs) {
db.getPowerTimeline(node._id, function (err, docs) {
if ((err == null) && (docs != null) && (docs.length > 0)) {
var timeline = [], time = null, previousPower;
for (i in docs) {
@ -535,69 +527,50 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
previousPower = doc.power;
}
try { ws.send(JSON.stringify({ action: 'powertimeline', nodeid: command.nodeid, timeline: timeline, tag: command.tag })); } catch (ex) { }
try { ws.send(JSON.stringify({ action: 'powertimeline', nodeid: node._id, timeline: timeline, tag: command.tag })); } catch (ex) { }
} else {
// No records found, send current state if we have it
var state = parent.parent.GetConnectivityState(command.nodeid);
if (state != null) { try { ws.send(JSON.stringify({ action: 'powertimeline', nodeid: command.nodeid, timeline: [state.powerState, Date.now(), state.powerState], tag: command.tag })); } catch (ex) { } }
if (state != null) { try { ws.send(JSON.stringify({ action: 'powertimeline', nodeid: node._id, timeline: [state.powerState, Date.now(), state.powerState], tag: command.tag })); } catch (ex) { } }
}
});
}
});
break;
}
case 'getsysinfo':
{
// Perform pre-validation
if (common.validateString(command.nodeid, 0, 128) == false) break;
var snode = command.nodeid.split('/');
if ((snode.length != 3) || (snode[1] != domain.id)) break;
// Check that we have permissions for this node.
db.Get(command.nodeid, function (err, nodes) {
if (nodes == null || nodes.length != 1) return;
const node = nodes[0];
if (parent.GetMeshRights(user, node.meshid) != 0) {
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
if (visible == false) return;
// Query the database system information
db.Get('si' + command.nodeid, function (err, docs) {
if ((docs != null) && (docs.length > 0)) {
var doc = docs[0];
doc.action = 'getsysinfo';
doc.nodeid = command.nodeid;
doc.nodeid = node._id;
doc.tag = command.tag;
delete doc.type;
delete doc.domain;
delete doc._id;
try { ws.send(JSON.stringify(doc)); } catch (ex) { }
} else {
try { ws.send(JSON.stringify({ action: 'getsysinfo', nodeid: command.nodeid, tag: command.tag, noinfo: true })); } catch (ex) { }
try { ws.send(JSON.stringify({ action: 'getsysinfo', nodeid: node._id, tag: command.tag, noinfo: true })); } catch (ex) { }
}
});
}
});
break;
}
case 'lastconnect':
{
// Perform pre-validation
if (common.validateString(command.nodeid, 0, 128) == false) return;
var snode = command.nodeid.split('/');
if ((snode.length != 3) || (snode[1] != domain.id)) break;
// Check that we have permissions for this node.
db.Get(command.nodeid, function (err, nodes) {
if (nodes == null || nodes.length != 1) return;
const node = nodes[0];
if (parent.GetMeshRights(user, node.meshid) != 0) {
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
if (visible == false) return;
// Query the database for the last time this node connected
db.Get('lc' + command.nodeid, function (err, docs) {
if ((docs != null) && (docs.length > 0)) {
try { ws.send(JSON.stringify({ action: 'lastconnect', nodeid: command.nodeid, time: docs[0].time, addr: docs[0].addr })); } catch (ex) { }
}
});
}
});
break;
}
@ -963,32 +936,28 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
try { ws.send(JSON.stringify({ action: 'events', events: docs, user: command.user, tag: command.tag })); } catch (ex) { }
});
}
} else if (common.validateString(command.nodeid, 0, 128) == true) { // Device filtered events
} else if (command.nodeid != null) { // Device filtered events
// Check that the user has access to this nodeid
db.Get(command.nodeid, function (err, nodes) {
if ((nodes == null) || (nodes.length != 1)) return;
const node = nodes[0];
parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
if (node == null) return;
var meshrights = parent.GetMeshRights(user, node.meshid);
if (meshrights != 0) {
// Put a limit on the number of returned entries if present
var limit = 10000;
if (common.validateInt(command.limit, 1, 60000) == true) { limit = command.limit; }
if ((meshrights & MESHRIGHT_LIMITEVENTS) != 0) {
if ((rights & MESHRIGHT_LIMITEVENTS) != 0) {
// Send the list of most recent events for this nodeid that only apply to us, up to 'limit' count
db.GetNodeEventsSelfWithLimit(command.nodeid, domain.id, user._id, limit, function (err, docs) {
db.GetNodeEventsSelfWithLimit(node._id, domain.id, user._id, limit, function (err, docs) {
if (err != null) return;
try { ws.send(JSON.stringify({ action: 'events', events: docs, nodeid: command.nodeid, tag: command.tag })); } catch (ex) { }
try { ws.send(JSON.stringify({ action: 'events', events: docs, nodeid: node._id, tag: command.tag })); } catch (ex) { }
});
} else {
// Send the list of most recent events for this nodeid, up to 'limit' count
db.GetNodeEventsWithLimit(command.nodeid, domain.id, limit, function (err, docs) {
db.GetNodeEventsWithLimit(node._id, domain.id, limit, function (err, docs) {
if (err != null) return;
try { ws.send(JSON.stringify({ action: 'events', events: docs, nodeid: command.nodeid, tag: command.tag })); } catch (ex) { }
try { ws.send(JSON.stringify({ action: 'events', events: docs, nodeid: node._id, tag: command.tag })); } catch (ex) { }
});
}
}
});
} else {
// Create a filter for device groups
@ -997,7 +966,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
// All events
var exGroupFilter2 = [], filter = [], filter2 = user.subscriptions;
// Remove MeshID's that we do not have rights to see events for (TODO: user groups)
// Add all meshes for groups this user is part of
// TODO (UserGroups)
// Remove MeshID's that we do not have rights to see events for
for (var link in obj.user.links) { if (((obj.user.links[link].rights & MESHRIGHT_LIMITEVENTS) != 0) && ((obj.user.links[link].rights != 0xFFFFFFFF))) { exGroupFilter2.push(link); } }
for (var i in filter2) { if (exGroupFilter2.indexOf(filter2[i]) == -1) { filter.push(filter2[i]); } }
@ -1198,7 +1170,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
}
// TODO: Remove user groups??
// TODO (UserGroups): Remove user groups??
db.Remove('ws' + deluser._id); // Remove user web state
db.Remove('nt' + deluser._id); // Remove notes for this user
@ -1507,18 +1479,21 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if (common.validateString(command.meshid, 1, 1024) == false) { err = 'Invalid group identifier'; } // Check the meshid
else if (command.meshid.indexOf('/') == -1) { command.meshid = 'mesh/' + domain.id + '/' + command.meshid; }
if (common.validateInt(command.notify) == false) { err = 'Invalid notification flags'; }
if (parent.GetMeshRights(user, command.meshid) == 0) err = 'Access denied';
if (parent.IsMeshViewable(user, command.meshid) == false) err = 'Access denied';
} catch (ex) { err = 'Validation exception: ' + ex; }
// Handle any errors
if (err != null) { if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'changemeshnotify', responseid: command.responseid, result: err })); } catch (ex) { } } break; }
// Change the notification (TODO: Add user group support, not sure how to do this here)
// TODO (UserGroups)
if (user.links[command.meshid]) {
if (command.notify == 0) {
delete user.links[command.meshid].notify;
} else {
user.links[command.meshid].notify = command.notify;
}
}
// Save the user
parent.db.SetUser(user);
@ -1674,20 +1649,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
}
// User-to-device chat is not support in LAN-only mode yet. We need the agent to replace the IP address of the server??
if (args.lanonly == true) { return; }
// Setup a user-to-node session
if (common.validateString(command.nodeid, 1, 2048)) {
if (args.lanonly == true) { return; } // User-to-device chat is not support in LAN-only mode yet. We need the agent to replace the IP address of the server??
// Get the device
db.Get(command.nodeid, function (err, nodes) {
if ((nodes == null) || (nodes.length != 1)) return;
var node = nodes[0];
// Get the mesh for this device
mesh = parent.meshes[node.meshid];
if (mesh) {
parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
// Check if this user has rights to do this
if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_CHATNOTIFY) == 0) return;
if ((rights & MESHRIGHT_CHATNOTIFY) == 0) return;
// Create the server url
var httpsPort = ((args.aliasport == null) ? args.port : args.aliasport); // Use HTTPS alias port is specified
@ -1697,10 +1665,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
// Create the notification message
routeCommandToNode({ "action": "openUrl", "nodeid": command.nodeid, "userid": user._id, "username": user.name, "url": url });
}
});
}
break;
}
case 'serverversion':
@ -2084,20 +2049,17 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
// For each nodeid, change the group
for (var i = 0; i < command.nodeids.length; i++) {
db.Get(command.nodeids[i], function (err, nodes) {
if ((nodes == null) || (nodes.length != 1)) return;
const node = nodes[0];
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) {
// Check if already in the right mesh
if (node.meshid == command.meshid) return;
if ((node == null) || (node.meshid == command.meshid)) return;
// Make sure both source and target mesh are the same type
try { if (parent.meshes[node.meshid].mtype != parent.meshes[command.meshid].mtype) return; } catch (e) { return; };
// Make sure that we have rights on both source and destination mesh
const sourceMeshRights = parent.GetMeshRights(user, node.meshid);
const targetMeshRights = parent.GetMeshRights(user, command.meshid);
if (((sourceMeshRights & MESHRIGHT_MANAGECOMPUTERS) == 0) || ((targetMeshRights & MESHRIGHT_MANAGECOMPUTERS) == 0)) return;
if (((rights & MESHRIGHT_MANAGECOMPUTERS) == 0) || ((targetMeshRights & MESHRIGHT_MANAGECOMPUTERS) == 0)) return;
// Perform the switch, start by saving the node with the new meshid.
const oldMeshId = node.meshid;
@ -2139,22 +2101,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
case 'removedevices':
{
if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
for (i in command.nodeids) {
nodeid = command.nodeids[i];
if (common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid
if ((nodeid.split('/').length != 3) || (nodeid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
// Get the device
db.Get(nodeid, function (err, nodes) {
if ((nodes == null) || (nodes.length != 1)) return;
var node = nodes[0];
// Get the mesh for this device
mesh = parent.meshes[node.meshid];
if (mesh) {
// Check if this user has rights to do this
if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_MANAGECOMPUTERS) == 0) return;
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) {
// Check we have the rights to delete this device
if ((rights & MESHRIGHT_MANAGECOMPUTERS) == 0) return;
// Delete this node including network interface information, events and timeline
db.Remove(node._id); // Remove node with that id
@ -2182,34 +2133,21 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if ((state.connectivity & 1) != 0) { parent.wsagents[nodeid].close(); } // Disconnect mesh agent
if ((state.connectivity & 2) != 0) { parent.parent.mpsserver.close(parent.parent.mpsserver.ciraConnections[nodeid]); } // Disconnect CIRA connection
}
}
});
}
break;
}
case 'wakedevices':
{
if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
// TODO: We can optimize this a lot.
// - We should get a full list of all MAC's to wake first.
// - We should try to only have one agent per subnet (using Gateway MAC) send a wake-on-lan.
if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
for (i in command.nodeids) {
nodeid = command.nodeids[i];
var wakeActions = 0;
if (common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid
if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain
// Get the device
db.Get(nodeid, function (err, nodes) {
if ((nodes == null) || (nodes.length != 1)) return;
var node = nodes[0];
// Get the mesh for this device
mesh = parent.meshes[node.meshid];
if (mesh) {
// Check if this user has rights to do this
if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_WAKEDEVICE) != 0) {
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) {
// Check we have the rights to delete this device
if ((rights & MESHRIGHT_WAKEDEVICE) == 0) return;
// If this device is connected on MQTT, send a wake action.
if (parent.parent.mqttbroker != null) { parent.parent.mqttbroker.publish(node._id, 'powerAction', 'wake'); }
@ -2217,12 +2155,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
// Get the device interface information
db.Get('if' + node._id, function (err, nodeifs) {
if ((nodeifs != null) && (nodeifs.length == 1)) {
var nodeif = nodeifs[0];
var macs = [];
var macs = [], nodeif = nodeifs[0];
for (var i in nodeif.netif) { if (nodeif.netif[i].mac) { macs.push(nodeif.netif[i].mac); } }
// Have the server send a wake-on-lan packet (Will not work in WAN-only)
if (parent.parent.meshScanner != null) { parent.parent.meshScanner.wakeOnLan(macs); wakeActions++; }
if (parent.parent.meshScanner != null) { parent.parent.meshScanner.wakeOnLan(macs); }
// Get the list of mesh this user as access to
var targetMeshes = [];
@ -2234,51 +2171,33 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if ((targetMeshes.indexOf(agent.dbMeshKey) >= 0) && (agent.authenticated == 2)) {
//console.log('Asking agent ' + agent.dbNodeKey + ' to wake ' + macs.join(','));
try { agent.send(JSON.stringify({ action: 'wakeonlan', macs: macs })); } catch (ex) { }
wakeActions++;
}
}
}
});
}
}
});
}
// Confirm we may be doing something (TODO)
try { ws.send(JSON.stringify({ action: 'wakedevices' })); } catch (ex) { }
}
break;
}
case 'uninstallagent':
{
if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
for (i in command.nodeids) {
nodeid = command.nodeids[i];
if (common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid
if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain
// Get the device
db.Get(nodeid, function (err, nodes) {
if ((nodes == null) || (nodes.length != 1)) return;
var node = nodes[0];
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) {
// Check we have the rights to delete this device
if ((rights & MESHRIGHT_UNINSTALL) == 0) return;
// Get the mesh for this device
mesh = parent.meshes[node.meshid];
if (mesh) {
// Check if this user has rights to do this
if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_UNINSTALL) != 0) {
// Send uninstall command to connected agent
var agent = parent.wsagents[node._id];
const agent = parent.wsagents[node._id];
if (agent != null) {
//console.log('Asking agent ' + agent.dbNodeKey + ' to uninstall.');
try { agent.send(JSON.stringify({ action: 'uninstallagent' })); } catch (ex) { }
}
}
}
});
}
}
break;
}
case 'poweraction':
@ -2286,34 +2205,21 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
if (common.validateInt(command.actiontype, 2, 4) == false) break; // Check actiontype
for (i in command.nodeids) {
nodeid = command.nodeids[i];
var powerActions = 0;
if (common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid
if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain
// Get the device
db.Get(nodeid, function (err, nodes) {
if ((nodes == null) || (nodes.length != 1)) return;
var node = nodes[0];
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) {
// Check we have the rights to delete this device
if ((rights & MESHRIGHT_REMOTECONTROL) == 0) return;
// Get the mesh for this device
mesh = parent.meshes[node.meshid];
if (mesh) {
// If this device is connected on MQTT, send a power action.
if (parent.parent.mqttbroker != null) { parent.parent.mqttbroker.publish(nodeid, 'powerAction', ['', '', 'poweroff', 'reset', 'sleep'][command.actiontype]); }
if (parent.parent.mqttbroker != null) { parent.parent.mqttbroker.publish(node._id, 'powerAction', ['', '', 'poweroff', 'reset', 'sleep'][command.actiontype]); }
// Check if this user has rights to do this
if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_REMOTECONTROL) != 0) { // "Remote Control permission"
// Get this device
var agent = parent.wsagents[node._id];
// Get this device and send the power command
const agent = parent.wsagents[node._id];
if (agent != null) {
// Send the power command
try { agent.send(JSON.stringify({ action: 'poweraction', actiontype: command.actiontype })); } catch (ex) { }
powerActions++;
}
}
}
});
}
// Confirm we may be doing something (TODO)
try { ws.send(JSON.stringify({ action: 'poweraction' })); } catch (ex) { }
}
@ -2325,31 +2231,18 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if (common.validateString(command.title, 1, 512) == false) break; // Check title
if (common.validateString(command.msg, 1, 4096) == false) break; // Check message
for (i in command.nodeids) {
nodeid = command.nodeids[i];
var powerActions = 0;
if (common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid
if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain
// Get the device
db.Get(nodeid, function (err, nodes) {
if ((nodes == null) || (nodes.length != 1)) return;
var node = nodes[0];
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) {
// Check we have the rights to delete this device
if ((rights & MESHRIGHT_CHATNOTIFY) == 0) return;
// Get the mesh for this device
mesh = parent.meshes[node.meshid];
if (mesh) {
// Check if this user has rights to do this
if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_CHATNOTIFY) != 0) {
// Get this device
var agent = parent.wsagents[node._id];
// Get this device and send toast command
const agent = parent.wsagents[node._id];
if (agent != null) {
// Send the power command
try { agent.send(JSON.stringify({ action: 'toast', title: command.title, msg: command.msg, sessionid: ws.sessionId, username: user.name, userid: user._id })); } catch (ex) { }
}
}
}
});
}
}
break;
}
case 'getnetworkinfo':
@ -2358,24 +2251,16 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
if ((command.nodeid.split('/').length != 3) || (command.nodeid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
// Get the device
db.Get(command.nodeid, function (err, nodes) {
if ((nodes == null) || (nodes.length != 1)) { try { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: command.nodeid, netif: null })); } catch (ex) { } return; }
var node = nodes[0];
// Get the mesh for this device
mesh = parent.meshes[node.meshid];
if (mesh) {
// Check if this user has rights to do this
if (parent.GetMeshRights(user, mesh) == 0) { try { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: command.nodeid, netif: null })); } catch (ex) { } return; }
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
if (visible == false) return;
// Get network information about this node
db.Get('if' + command.nodeid, function (err, netinfos) {
if ((netinfos == null) || (netinfos.length != 1)) { try { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: command.nodeid, netif: null })); } catch (ex) { } return; }
db.Get('if' + node._id, function (err, netinfos) {
if ((netinfos == null) || (netinfos.length != 1)) { try { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: node._id, netif: null })); } catch (ex) { } return; }
var netinfo = netinfos[0];
try { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: command.nodeid, updateTime: netinfo.updateTime, netif: netinfo.netif })); } catch (ex) { }
try { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: node._id, updateTime: netinfo.updateTime, netif: netinfo.netif })); } catch (ex) { }
});
}
});
break;
}
@ -2383,24 +2268,17 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
{
// Argument validation
if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
if ((command.nodeid.split('/').length != 3) || (command.nodeid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
if ((command.userloc) && (command.userloc.length != 2) && (command.userloc.length != 0)) return;
// Change the device
db.Get(command.nodeid, function (err, nodes) {
if ((nodes == null) || (nodes.length != 1)) return;
var node = nodes[0];
// Get the mesh for this device
mesh = parent.meshes[node.meshid];
if (mesh) {
// Check if this user has rights to do this
if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_MANAGECOMPUTERS) == 0) return;
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
if ((rights & MESHRIGHT_MANAGECOMPUTERS) == 0) return;
var mesh = parent.meshes[node.meshid];
// Ready the node change event
var changes = [], event = { etype: 'node', userid: user._id, username: user.name, action: 'changenode', nodeid: node._id, domain: domain.id };
change = 0;
event.msg = ": ";
event.msg = ': ';
// If we are in WAN-only mode, host is not used
if ((args.wanonly == true) && (command.host)) { delete command.host; }
@ -2443,35 +2321,26 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come.
parent.parent.DispatchEvent(['*', node.meshid, user._id], obj, event);
}
}
});
break;
}
case 'uploadagentcore':
{
if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
if (common.validateString(command.type, 1, 40) == false) break; // Check path
// Change the device
db.Get(command.nodeid, function (err, nodes) {
if ((nodes == null) || (nodes.length != 1)) return;
var node = nodes[0];
// Get the mesh for this device
mesh = parent.meshes[node.meshid];
if (mesh) {
// Check if this user has rights to do this
if (((parent.GetMeshRights(user, mesh) & MESHRIGHT_AGENTCONSOLE) == 0) && (user.siteadmin != 0xFFFFFFFF)) { return; }
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
if ((node == null) || (((rights & MESHRIGHT_AGENTCONSOLE) == 0) && (user.siteadmin != 0xFFFFFFFF))) return;
if (command.type == 'default') {
// Send the default core to the agent
parent.parent.updateMeshCore(function () { parent.sendMeshAgentCore(user, domain, command.nodeid, 'default'); });
parent.parent.updateMeshCore(function () { parent.sendMeshAgentCore(user, domain, node._id, 'default'); });
} else if (command.type == 'clear') {
// Clear the mesh agent core on the mesh agent
parent.sendMeshAgentCore(user, domain, command.nodeid, 'clear');
parent.sendMeshAgentCore(user, domain, node._id, 'clear');
} else if (command.type == 'recovery') {
// Send the recovery core to the agent
parent.sendMeshAgentCore(user, domain, command.nodeid, 'recovery');
parent.sendMeshAgentCore(user, domain, node._id, 'recovery');
} else if ((command.type == 'custom') && (common.validateString(command.path, 1, 2048) == true)) {
// Send a mesh agent core to the mesh agent
var file = parent.getServerFilePath(user, domain, command.path);
@ -2479,34 +2348,24 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
fs.readFile(file.fullpath, 'utf8', function (err, data) {
if (err != null) {
data = common.IntToStr(0) + data; // Add the 4 bytes encoding type & flags (Set to 0 for raw)
parent.sendMeshAgentCore(user, domain, command.nodeid, 'custom', data);
parent.sendMeshAgentCore(user, domain, node._id, 'custom', data);
}
});
}
}
}
});
break;
}
case 'agentdisconnect':
{
if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
if (common.validateInt(command.disconnectMode) == false) return; // Check disconnect mode
// Change the device
db.Get(command.nodeid, function (err, nodes) {
if ((nodes == null) || (nodes.length != 1)) return;
var node = nodes[0];
// Get the mesh for this device
mesh = parent.meshes[node.meshid];
if (mesh) {
// Check if this user has rights to do this
if (((parent.GetMeshRights(user, mesh) & MESHRIGHT_AGENTCONSOLE) == 0) && (user.siteadmin != 0xFFFFFFFF)) return;
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
if ((node == null) || (((rights & MESHRIGHT_AGENTCONSOLE) == 0) && (user.siteadmin != 0xFFFFFFFF))) return;
// Force mesh agent disconnection
parent.forceMeshAgentDisconnect(user, domain, command.nodeid, command.disconnectMode);
}
parent.forceMeshAgentDisconnect(user, domain, node._id, command.disconnectMode);
});
break;
}
@ -2552,7 +2411,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
mesh = parent.meshes[command.meshid];
if (mesh == null) { err = 'Unknown device group'; } // Check if the group exists
else if (mesh.mtype != 2) { err = 'Invalid group type'; } // Check if this is the correct group type
else if (parent.GetMeshRights(user, mesh) == 0) { err = 'Not allowed'; } // Check if this user has rights to do this
else if (parent.IsMeshViewable(user, mesh) == false) { err = 'Not allowed'; } // Check if this user has rights to do this
}
}
} catch (ex) { err = 'Validation exception: ' + ex; }
@ -2574,24 +2433,16 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
{
// Argument validation
if (common.validateString(command.msg, 1, 4096) == false) break; // Check event
if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
var splitid = command.nodeid.split('/');
if ((splitid.length != 3) || (splitid[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
var idtype = splitid[0];
if ((idtype != 'node')) return;
// Check if this user has rights on this id to set notes
db.Get(command.nodeid, function (err, nodes) {
if ((nodes == null) || (nodes.length == 1)) {
if (parent.GetMeshRights(user, nodes[0].meshid) != 0) {
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
if (rights == 0) return;
// Add an event for this device
var targets = ['*', 'server-users', user._id, nodes[0].meshid];
var event = { etype: 'node', userid: user._id, username: user.name, nodeid: nodes[0]._id, action: 'manual', msg: decodeURIComponent(command.msg), domain: domain.id };
parent.parent.DispatchEvent(targets, obj, event);
}
}
});
break;
}
case 'setNotes':
@ -2604,16 +2455,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if ((idtype != 'user') && (idtype != 'mesh') && (idtype != 'node')) return;
if (idtype == 'node') {
// Check if this user has rights on this id to set notes
db.Get(command.id, function (err, nodes) { // TODO: Make a NodeRights(user) method that also does not do a db call if agent is connected (???)
if ((nodes == null) || (nodes.length == 1)) {
if ((parent.GetMeshRights(user, nodes[0].meshid) & MESHRIGHT_SETNOTES) != 0) {
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, command.id, function (node, rights, visible) {
if ((rights & MESHRIGHT_SETNOTES) != 0) {
// Set the id's notes
if (common.validateString(command.notes, 1) == false) {
db.Remove('nt' + command.id); // Delete the note for this node
db.Remove('nt' + node._id); // Delete the note for this node
} else {
db.Set({ _id: 'nt' + command.id, type: 'note', value: command.notes }); // Set the note for this node
}
db.Set({ _id: 'nt' + node._id, type: 'note', value: command.notes }); // Set the note for this node
}
}
});
@ -2893,44 +2742,26 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
case 'getClip': {
if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
// Get the device
db.Get(command.nodeid, function (err, nodes) {
if ((nodes == null) || (nodes.length != 1)) return;
var node = nodes[0];
// Get the mesh for this device
mesh = parent.meshes[node.meshid];
if (mesh) {
// Check if this user has "remote" rights to do this
var meshrights = parent.GetMeshRights(user, mesh);
if ((meshrights & MESHRIGHT_AGENTCONSOLE) == 0) return;
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
if ((rights & MESHRIGHT_AGENTCONSOLE) == 0) return;
// Ask for clipboard data from agent
var agent = parent.wsagents[node._id];
if (agent != null) { try { agent.send(JSON.stringify({ action: 'getClip' })); } catch (ex) { } }
}
});
break;
}
case 'setClip': {
if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
if (common.validateString(command.data, 1, 65535) == false) break; // Check
// Get the device
db.Get(command.nodeid, function (err, nodes) {
if ((nodes == null) || (nodes.length != 1)) return;
var node = nodes[0];
// Get the mesh for this device
mesh = parent.meshes[node.meshid];
if (mesh) {
// Check if this user has "remote" rights to do this
if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_AGENTCONSOLE) == 0) return;
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
if ((rights & MESHRIGHT_AGENTCONSOLE) == 0) return;
// Send clipboard data to the agent
var agent = parent.wsagents[node._id];
if (agent != null) { try { agent.send(JSON.stringify({ action: 'setClip', data: command.data })); } catch (ex) { } }
}
});
break;
}
@ -2951,16 +2782,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if ((idtype != 'user') && (idtype != 'mesh') && (idtype != 'node')) return;
if (idtype == 'node') {
// Get the device
db.Get(command.id, function (err, nodes) {
if ((nodes == null) || (nodes.length != 1)) return;
var node = nodes[0];
// Get the mesh for this device
mesh = parent.meshes[node.meshid];
if (mesh) {
// Check if this user has rights to do this
if (parent.GetMeshRights(user, mesh) == 0) return;
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, command.id, function (node, rights, visible) {
if (visible == false) return;
// Get the notes about this node
db.Get('nt' + command.id, function (err, notes) {
@ -2969,7 +2793,6 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
ws.send(JSON.stringify({ action: 'getNotes', id: command.id, notes: notes[0].value }));
} catch (ex) { }
});
}
});
} else if (idtype == 'mesh') {
// Get the mesh for this device
@ -3037,6 +2860,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
break;
}
case 'sendmqttmsg': {
if (parent.parent.mqttbroker == null) { err = 'MQTT not supported on this server'; }; // MQTT not available
if (common.validateArray(command.nodeids, 1) == false) { err = 'Invalid nodeids'; }; // Check nodeid's
if (common.validateString(command.topic, 1, 64) == false) { err = 'Invalid topic'; } // Check the topic
if (common.validateString(command.msg, 1, 4096) == false) { err = 'Invalid msg'; } // Check the message
@ -3047,31 +2871,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
break;
}
// TODO: We can optimize this a lot.
// - We should get a full list of all MAC's to wake first.
// - We should try to only have one agent per subnet (using Gateway MAC) send a wake-on-lan.
// Send the MQTT message
for (i in command.nodeids) {
nodeid = command.nodeids[i];
var wakeActions = 0;
if (common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid
if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain
// Get the device
db.Get(nodeid, function (err, nodes) {
if ((nodes == null) || (nodes.length != 1)) return;
var node = nodes[0];
// Get the mesh for this device
mesh = parent.meshes[node.meshid];
if (mesh) {
// Check if this user has rights to do this
if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_WAKEDEVICE) != 0) {
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) {
// If this device is connected on MQTT, send a wake action.
if (parent.parent.mqttbroker != null) { parent.parent.mqttbroker.publish(node._id, command.topic, command.msg); }
}
}
if (rights != 0) { parent.parent.mqttbroker.publish(node._id, command.topic, command.msg); }
});
}
}
break;
}
@ -3083,19 +2890,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
// Handle any errors
if (err != null) { if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'getmqttlogin', responseid: command.responseid, result: err })); } catch (ex) { } } break; }
var nodeid = command.nodeid;
if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain
// Get the device
db.Get(nodeid, function (err, nodes) {
if ((nodes == null) || (nodes.length != 1)) { if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'getmqttlogin', responseid: command.responseid, result: 'Invalid node id' })); } catch (ex) { } return; } }
var node = nodes[0];
// Get the device group for this node
var mesh = parent.meshes[node.meshid];
if (mesh) {
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
// Check if this user has rights to do this
if ((parent.GetMeshRights(user, mesh) == 0xFFFFFFFF)) {
var token = parent.parent.mqttbroker.generateLogin(mesh._id, node._id);
if (rights == 0xFFFFFFFF) {
var token = parent.parent.mqttbroker.generateLogin(node.meshid, node._id);
var r = { action: 'getmqttlogin', responseid: command.responseid, nodeid: node._id, user: token.user, pass: token.pass };
const serverName = parent.getWebServerName(domain);
@ -3108,18 +2907,16 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
// Add WS URL
var xdomain = (domain.dns == null) ? domain.id : '';
if (xdomain != '') xdomain += "/";
if (xdomain != '') xdomain += '/';
var httpsPort = ((args.aliasport == null) ? args.port : args.aliasport); // Use HTTPS alias port is specified
r.wsUrl = "ws" + (args.notls ? '' : 's') + "://" + serverName + ":" + httpsPort + "/" + xdomain + "mqtt.ashx";
r.wsUrl = 'ws' + (args.notls ? '' : 's') + '://' + serverName + ':' + httpsPort + '/' + xdomain + 'mqtt.ashx';
r.wsTrustedCert = parent.isTrustedCert(domain);
try { ws.send(JSON.stringify(r)); } catch (ex) { }
} else {
if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'getmqttlogin', responseid: command.responseid, result: 'Unable to perform this operation' })); } catch (ex) { } }
}
}
});
}
break;
}
case 'amt': {
@ -3136,23 +2933,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
} else if (command.mode == 3) {
if (parent.parent.apfserver.apfConnections[command.nodeid] == null) break;
}
var nodeid = command.nodeid;
if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain
// Get the device
db.Get(nodeid, function (err, nodes) {
if ((nodes == null) || (nodes.length != 1)) return;
var node = nodes[0];
// Get the mesh for this device
var mesh = parent.meshes[node.meshid];
if (mesh) {
// Check if this user has rights to do this
if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_REMOTECONTROL) != 0) { // "Remote Control permission"
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
if ((rights & MESHRIGHT_REMOTECONTROL) == 0) return;
handleAmtCommand(command, node);
}
}
});
}
break;
}
case 'distributeCore': {
@ -3304,15 +3090,15 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
// Read all files recursively
try {
files.filetree.f[user._id].f = readFilesRec(parent.path.join(parent.filespath, domainx + "/user-" + usersplit[2]));
files.filetree.f[user._id].f = readFilesRec(parent.path.join(parent.filespath, domainx + '/user-' + usersplit[2]));
} catch (e) {
// TODO: We may want to fake this file structure until it's needed.
// Got an error, try to create all the folders and try again...
try { fs.mkdirSync(parent.filespath); } catch (e) { }
try { fs.mkdirSync(parent.path.join(parent.filespath, domainx)); } catch (e) { }
try { fs.mkdirSync(parent.path.join(parent.filespath, domainx + "/user-" + usersplit[2])); } catch (e) { }
try { fs.mkdirSync(parent.path.join(parent.filespath, domainx + "/user-" + usersplit[2] + "/Public")); } catch (e) { }
try { files.filetree.f[user._id].f = readFilesRec(parent.path.join(parent.filespath, domainx + "/user-" + usersplit[2])); } catch (e) { }
try { fs.mkdirSync(parent.path.join(parent.filespath, domainx + '/user-' + usersplit[2])); } catch (e) { }
try { fs.mkdirSync(parent.path.join(parent.filespath, domainx + '/user-' + usersplit[2] + '/Public')); } catch (e) { }
try { files.filetree.f[user._id].f = readFilesRec(parent.path.join(parent.filespath, domainx + '/user-' + usersplit[2])); } catch (e) { }
}
// Add files for each mesh // TODO: Get all meshes including groups!!
@ -3326,7 +3112,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
// Read all files recursively
try {
files.filetree.f[mesh._id].f = readFilesRec(parent.path.join(parent.filespath, domainx + "/mesh-" + meshsplit[2]));
files.filetree.f[mesh._id].f = readFilesRec(parent.path.join(parent.filespath, domainx + '/mesh-' + meshsplit[2]));
} catch (e) {
files.filetree.f[mesh._id].f = {}; // Got an error, return empty folder. We will create the folder only when needed.
}
@ -3338,7 +3124,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
try { ws.send(JSON.stringify(files)); } catch (ex) { }
}
function EscapeHtml(x) { if (typeof x == "string") return x.replace(/&/g, '&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; }
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; }
//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; }
// Split a string taking into account the quoats. Used for command line parsing
@ -3388,13 +3174,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
node.intelamt.tls, tlsoptions, parent.parent, cmd.mode);
var amt = new Amt(wsman);
switch (cmd.command) {
case "Get-GeneralSettings": {
amt.Get("AMT_GeneralSettings", function (obj, name, response, status) {
case 'Get-GeneralSettings': {
amt.Get('AMT_GeneralSettings', function (obj, name, response, status) {
if (status == 200) {
var resp = { action: 'amt', nodeid: cmd.nodeid, command: 'Get-GeneralSettings', value: response.Body }
ws.send(JSON.stringify(resp));
} else {
ws.send(JSON.stringify({ "error": error }));
ws.send(JSON.stringify({ 'error': error }));
}
});
break;

View File

@ -11,7 +11,7 @@
/*jshint strict:false */
/*jshint -W097 */
/*jshint esversion: 6 */
"use strict";
'use strict';
// Construct a Intel AMT MPS server object
module.exports.CreateMpsServer = function (parent, db, args, certificates) {
@ -24,9 +24,9 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
var tlsSessionStore = {}; // Store TLS session information for quick resume.
var tlsSessionStoreCount = 0; // Number of cached TLS session information in store.
const constants = (require('crypto').constants ? require('crypto').constants : require('constants')); // require('constants') is deprecated in Node 11.10, use require('crypto').constants instead.
const common = require("./common.js");
const net = require("net");
const tls = require("tls");
const common = require('./common.js');
const net = require('net');
const tls = require('tls');
const MAX_IDLE = 90000; // 90 seconds max idle time, higher than the typical KEEP-ALIVE periode of 60 seconds
if (obj.args.mpstlsoffload) {
@ -42,9 +42,9 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
obj.server.listen(args.mpsport, function () { console.log("MeshCentral Intel(R) AMT server running on " + certificates.AmtMpsName + ":" + args.mpsport + ((args.mpsaliasport != null) ? (", alias port " + args.mpsaliasport) : "") + "."); }).on("error", function (err) { console.error("ERROR: MeshCentral Intel(R) AMT server port " + args.mpsport + " is not available."); if (args.exactports) { process.exit(); } });
obj.server.on('tlsClientError', function (err, tlssocket) { if (args.mpsdebug) { var remoteAddress = tlssocket.remoteAddress; if (tlssocket.remoteFamily == 'IPv6') { remoteAddress = '[' + remoteAddress + ']'; } console.log('MPS:Invalid TLS connection from ' + remoteAddress + ':' + tlssocket.remotePort + '.'); } });
obj.parent.updateServerState("mps-port", args.mpsport);
obj.parent.updateServerState("mps-name", certificates.AmtMpsName);
if (args.mpsaliasport != null) { obj.parent.updateServerState("mps-alias-port", args.mpsaliasport); }
obj.parent.updateServerState('mps-port', args.mpsport);
obj.parent.updateServerState('mps-name', certificates.AmtMpsName);
if (args.mpsaliasport != null) { obj.parent.updateServerState('mps-alias-port', args.mpsaliasport); }
const APFProtocol = {
UNKNOWN: 0,
@ -202,47 +202,47 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
function onConnection(socket) {
connectionCount++;
if (obj.args.mpstlsoffload) {
socket.tag = { first: true, clientCert: null, accumulator: "", activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 };
socket.tag = { first: true, clientCert: null, accumulator: '', activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 };
} else {
socket.tag = { first: true, clientCert: socket.getPeerCertificate(true), accumulator: "", activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 };
socket.tag = { first: true, clientCert: socket.getPeerCertificate(true), accumulator: '', activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 };
}
socket.setEncoding("binary");
socket.setEncoding('binary');
parent.debug('mps', "New CIRA connection");
// Setup the CIRA keep alive timer
socket.setTimeout(MAX_IDLE);
socket.on("timeout", () => { ciraTimeoutCount++; parent.debug('mps', "CIRA timeout, disconnecting."); try { socket.end(); } catch (e) { } });
socket.on('timeout', () => { ciraTimeoutCount++; parent.debug('mps', "CIRA timeout, disconnecting."); try { socket.end(); } catch (e) { } });
socket.addListener("data", function (data) {
if (args.mpsdebug) { var buf = Buffer.from(data, "binary"); console.log("MPS <-- (" + buf.length + "):" + buf.toString('hex')); } // Print out received bytes
socket.addListener('data', function (data) {
if (args.mpsdebug) { var buf = Buffer.from(data, 'binary'); console.log("MPS <-- (" + buf.length + "):" + buf.toString('hex')); } // Print out received bytes
socket.tag.accumulator += data;
// Detect if this is an HTTPS request, if it is, return a simple answer and disconnect. This is useful for debugging access to the MPS port.
if (socket.tag.first == true) {
if (socket.tag.accumulator.length < 3) return;
//if (!socket.tag.clientCert.subject) { console.log("MPS Connection, no client cert: " + socket.remoteAddress); socket.write('HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nMeshCentral2 MPS server.\r\nNo client certificate given.'); socket.end(); return; }
if (socket.tag.accumulator.substring(0, 3) == "GET") { if (args.mpsdebug) { console.log("MPS Connection, HTTP GET detected: " + socket.remoteAddress); } socket.write("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\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; }
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; }
// If the MQTT broker is active, look for inbound MQTT connections
if (parent.mqttbroker != null) {
var chunk = Buffer.from(socket.tag.accumulator, "binary");
var chunk = Buffer.from(socket.tag.accumulator, 'binary');
var packet_len = 0;
if (chunk.readUInt8(0) == 16) { packet_len = getMQTTPacketLength(chunk); }
if (chunk.readUInt8(0) == 16 && (socket.tag.accumulator.length < packet_len)) return; // Minimum MQTT detection
// check if it is MQTT, need more initial packet to probe
if (chunk.readUInt8(0) == 16 && ((chunk.slice(4, 8).toString() === "MQTT") || (chunk.slice(5, 9).toString() === "MQTT")
|| (chunk.slice(6, 10).toString() === "MQTT") || (chunk.slice(7, 11).toString() === "MQTT"))) {
parent.debug("mps", "MQTT connection detected.");
if (chunk.readUInt8(0) == 16 && ((chunk.slice(4, 8).toString() === 'MQTT') || (chunk.slice(5, 9).toString() === 'MQTT')
|| (chunk.slice(6, 10).toString() === 'MQTT') || (chunk.slice(7, 11).toString() === 'MQTT'))) {
parent.debug('mps', "MQTT connection detected.");
socket.removeAllListeners("data");
socket.removeAllListeners("close");
socket.setNoDelay(true);
socket.serialtunnel = SerialTunnel();
socket.serialtunnel.xtransport = 'mps';
socket.serialtunnel.xip = socket.remoteAddress;
socket.on("data", function (b) { socket.serialtunnel.updateBuffer(Buffer.from(b, "binary")) });
socket.serialtunnel.forwardwrite = function (b) { socket.write(b, "binary") }
socket.on("close", function () { socket.serialtunnel.emit("end"); });
socket.on('data', function (b) { socket.serialtunnel.updateBuffer(Buffer.from(b, 'binary')) });
socket.serialtunnel.forwardwrite = function (b) { socket.write(b, 'binary') }
socket.on('close', function () { socket.serialtunnel.emit('end'); });
// Pass socket wrapper to the MQTT broker
parent.mqttbroker.handle(socket.serialtunnel);
@ -533,7 +533,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
var request = data.substring(5, 5 + requestLen);
//var wantResponse = data.charCodeAt(5 + requestLen);
if (request == "tcpip-forward") {
if (request == 'tcpip-forward') {
var addrLen = common.ReadInt(data, 6 + requestLen);
if (len < 14 + requestLen + addrLen) return 0;
var addr = data.substring(10 + requestLen, 10 + requestLen + addrLen);
@ -545,7 +545,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
return 14 + requestLen + addrLen;
}
if (request == "cancel-tcpip-forward") {
if (request == 'cancel-tcpip-forward') {
var addrLen = common.ReadInt(data, 6 + requestLen);
if (len < 14 + requestLen + addrLen) return 0;
var addr = data.substring(10 + requestLen, 10 + requestLen + addrLen);
@ -557,7 +557,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
return 14 + requestLen + addrLen;
}
if (request == "udp-send-to@amt.intel.com") {
if (request == 'udp-send-to@amt.intel.com') {
var addrLen = common.ReadInt(data, 6 + requestLen);
if (len < 26 + requestLen + addrLen) return 0;
var addr = data.substring(10 + requestLen, 10 + requestLen + addrLen);
@ -748,14 +748,14 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
}
}
socket.addListener("close", function () {
socket.addListener('close', function () {
socketClosedCount++;
parent.debug('mps', 'CIRA connection closed');
try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { }
obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2);
});
socket.addListener("error", function () {
socket.addListener('error', function () {
socketErrorCount++;
//console.log("MPS Error: " + socket.remoteAddress);
});
@ -802,7 +802,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
*/
function SendChannelOpen(socket, direct, channelid, windowsize, target, targetport, source, sourceport) {
var connectionType = ((direct == true) ? "direct-tcpip" : "forwarded-tcpip");
var connectionType = ((direct == true) ? 'direct-tcpip' : 'forwarded-tcpip');
if ((target == null) || (target == null)) target = ''; // TODO: Reports of target being undefined that causes target.length to fail. This is a hack.
Write(socket, String.fromCharCode(APFProtocol.CHANNEL_OPEN) + common.IntToStr(connectionType.length) + connectionType + common.IntToStr(channelid) + common.IntToStr(windowsize) + common.IntToStr(-1) + common.IntToStr(target.length) + target + common.IntToStr(targetport) + common.IntToStr(source.length) + source + common.IntToStr(sourceport));
}
@ -837,11 +837,11 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
function Write(socket, data) {
if (args.mpsdebug) {
// Print out sent bytes
var buf = Buffer.from(data, "binary");
var buf = Buffer.from(data, 'binary');
console.log('MPS --> (' + buf.length + '):' + buf.toString('hex'));
socket.write(buf);
} else {
socket.write(Buffer.from(data, "binary"));
socket.write(Buffer.from(data, 'binary'));
}
}

View File

@ -11,7 +11,7 @@
/*jshint strict:false */
/*jshint -W097 */
/*jshint esversion: 6 */
"use strict";
'use strict';
// Construct a Mesh Multi-Server object. This is used for MeshCentral-to-MeshCentral communication.
module.exports.CreateMultiServer = function (parent, args) {
@ -552,17 +552,13 @@ module.exports.CreateMultiServer = function (parent, args) {
if (msg.fromNodeid != null) { msg.nodeid = msg.fromNodeid; delete msg.fromNodeid; }
var cmdstr = JSON.stringify(msg);
for (userid in obj.parent.webserver.wssessions) { // Find all connected users for this mesh and send the message
var user = obj.parent.webserver.users[userid];
if (user) {
var rights = user.links[msg.meshid];
if (rights != null) { // TODO: Look at what rights are needed for message routing
if (parent.webserver.GetMeshRights(userid, msg.meshid) != 0) { // TODO: Look at what rights are needed for message routing
var sessions = obj.parent.webserver.wssessions[userid];
// Send the message to all users on this server
for (i in sessions) { sessions[i].send(cmdstr); }
}
}
}
}
break;
}
default: {

View File

@ -1,6 +1,6 @@
{
"name": "meshcentral",
"version": "0.4.6-p",
"version": "0.4.6-q",
"keywords": [
"Remote Management",
"Intel AMT",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -1155,6 +1155,14 @@ a {
border: none;
}
.i7 {
background: url(../images/icons50.png) -300px 0px;
height: 50px;
width: 50px;
cursor: pointer;
border: none;
}
.j1 {
background: url(../images/icons16.png) 0px 0px;
height: 16px;
@ -1203,6 +1211,14 @@ a {
border: none;
}
.j7 {
background: url(../images/icons16.png) -96px 0px;
height: 16px;
width: 16px;
cursor: pointer;
border: none;
}
.relayIcon16 {
background: url(../images/icon-relay.png);
height: 16px;
@ -1339,7 +1355,7 @@ a {
float: left;
}
.si0 {
.si1 {
background: url(../images/icons16.png) 0px 0px;
height: 16px;
width: 16px;
@ -1347,7 +1363,7 @@ a {
float: left;
}
.si1 {
.si2 {
background: url(../images/icons16.png) -16px 0px;
height: 16px;
width: 16px;
@ -1355,7 +1371,7 @@ a {
float: left;
}
.si2 {
.si3 {
background: url(../images/icons16.png) -32px 0px;
height: 16px;
width: 16px;
@ -1363,7 +1379,7 @@ a {
float: left;
}
.si3 {
.si4 {
background: url(../images/icons16.png) -48px 0px;
height: 16px;
width: 16px;
@ -1371,7 +1387,7 @@ a {
float: left;
}
.si4 {
.si5 {
background: url(../images/icons16.png) -64px 0px;
height: 16px;
width: 16px;
@ -1379,7 +1395,7 @@ a {
float: left;
}
.si5 {
.si6 {
background: url(../images/icons16.png) -80px 0px;
height: 16px;
width: 16px;
@ -1387,6 +1403,14 @@ a {
float: left;
}
.si7 {
background: url(../images/icons16.png) -96px 0px;
height: 16px;
width: 16px;
border: none;
float: left;
}
.mi {
background: url(../images/meshicon50.png) 0px 0px;
height: 50px;

View File

@ -8,8 +8,7 @@
"nl": "0",
"xloc": [
"default.handlebars->container->masthead->7->notificationCount",
"default-mobile.handlebars->9->229",
{ "$ref": "#" }
"default-mobile.handlebars->9->229"
]
},
{
@ -5610,7 +5609,7 @@
{
"en": "free",
"fr": "libre",
"pt": "Livre",
"pt": "livre",
"ja": "無料",
"cs": "volné",
"nl": "vrij",

View File

@ -1327,7 +1327,7 @@
count++;
// Mesh rights
var meshrights = meshes[i].links[userinfo._id].rights;
var meshrights = GetMeshRights(meshes[i]);
var rights = "Partial Rights";
if (meshrights == 0xFFFFFFFF) rights = "Full Administrator"; else if (meshrights == 0) rights = "No Rights";
@ -1576,8 +1576,7 @@
if (desktop && !xxdialogMode && xxcurrentView == 10) {
// Check what keys we are allows to send
if (currentNode != null) {
var mesh = meshes[currentNode.meshid];
var meshrights = mesh.links[userinfo._id].rights;
var meshrights = GetMeshRights(currentNode.meshid);
var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
if (inputAllowed == false) return false;
var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
@ -1594,8 +1593,7 @@
if (desktop && !xxdialogMode && xxcurrentView == 10) {
// Check what keys we are allows to send
if (currentNode != null) {
var mesh = meshes[currentNode.meshid];
var meshrights = mesh.links[userinfo._id].rights;
var meshrights = GetMeshRights(currentNode.meshid);
var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
if (inputAllowed == false) return false;
var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
@ -1612,8 +1610,7 @@
if (desktop && !xxdialogMode && xxcurrentView == 10) {
// Check what keys we are allows to send
if (currentNode != null) {
var mesh = meshes[currentNode.meshid];
var meshrights = mesh.links[userinfo._id].rights;
var meshrights = GetMeshRights(currentNode.meshid);
var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
if (inputAllowed == false) return false;
var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
@ -1655,9 +1652,7 @@
// Go thru the list of nodes and display them
for (var i in nodes) {
if (nodes[i].v == false) continue;
var mesh2 = meshes[nodes[i].meshid], meshlinks = mesh2.links[userinfo._id];
if (meshlinks == null) continue;
var meshrights = meshlinks.rights;
//var meshrights = GetNodeRights(nodes[i]);
if (sort == 0) {
// Mesh header
@ -1668,7 +1663,7 @@
if (meshes[nodes[i].meshid].mtype == 1) { extra = '<span style=color:lightgray>' + ", Intel&reg; AMT only" + '</span>'; }
if (current != null) { if (c == 2) { r += '<td><div style=width:301px></div></td>'; } if (r != '') { r += '</tr></table>'; } }
r += '<div class=DevSt style=padding-top:4px><span style=float:right>';
//r += getMeshActions(mesh2, meshrights);
//r += getMeshActions(meshes[nodes[i].meshid], meshrights);
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>';
current = nodes[i].meshid;
displayedMeshes[current] = 1;
@ -1726,10 +1721,8 @@
// Display all empty meshes, we need to do this because users can add devices to these at any time.
if (sort == 0) {
for (var i in meshes) {
var mesh = meshes[i], meshlink = mesh.links[userinfo._id];
if (meshlink != null) {
var meshrights = meshlink.rights;
if (displayedMeshes[mesh._id] == null) {
var mesh = meshes[i];
if (IsMeshViewable(mesh)) {
if ((current != '') && (r != '')) { r += '</tr></table>'; }
r += '<div><div colspan=3 class=DevSt><span style=float:right>';
//r += getMeshActions(mesh, meshrights);
@ -1742,7 +1735,6 @@
}
}
}
}
if (count == 0) {
QH('xdevices', '<div style="margin-top:50px;text-align:center"><span style="font-size:30px">' + "No devices" + '</span><br /><br />' + "Use the desktop version of this website to add devices." + '</div>');
@ -1813,11 +1805,6 @@
gotoDevice(nodeid, xxcurrentView, true);
}
function getNodeRights(nodeid) {
var node = getNodeFromId(nodeid), mesh = meshes[node.meshid];
return mesh.links[userinfo._id].rights;
}
var currentDevicePanel = 0;
var currentNode;
var powerTimelineNode = null;
@ -1837,7 +1824,7 @@
if (node == null) { goBack(); return; }
var mesh = meshes[node.meshid];
if (mesh == null) { goBack(); return; }
var meshrights = mesh.links[userinfo._id].rights;
var meshrights = GetMeshRights(mesh);
if (!currentNode || currentNode._id != node._id || refresh == true) {
currentNode = node;
@ -2008,8 +1995,7 @@
}
function setupDeviceMenu(op, obj) {
var meshrights = 0;
if (currentNode) { meshrights = meshes[currentNode.meshid].links[userinfo._id].rights; }
var meshrights = GetNodeRights(currentNode);
if (op != null) { currentDevicePanel = op; }
QV('p10general', currentDevicePanel == 0);
QV('p10desktop', currentDevicePanel == 1); // Show if we have remote control rights or desktop view only rights
@ -2027,7 +2013,7 @@
function deviceActionFunction() {
if (xxdialogMode) return;
var meshrights = meshes[currentNode.meshid].links[userinfo._id].rights;
var meshrights = GetNodeRights(currentNode);
var x = "Select an operation to perform on this device." + '<br /><br />';
var y = '<select id=d2deviceop style=float:right;width:170px>';
if ((meshrights & 64) != 0) { y += '<option value=100>' + "Wake-up" + '</option>'; } // Wake-up permission
@ -2117,7 +2103,7 @@
function editDeviceAmtSettings(nodeid, func) {
if (xxdialogMode) return;
var x = '', node = getNodeFromId(nodeid), buttons = 3, meshrights = getNodeRights(nodeid);
var x = '', node = getNodeFromId(nodeid), buttons = 3, meshrights = GetNodeRights(node);
if ((meshrights & 4) == 0) return;
x += addHtmlValue("Username", '<input id=dp10username style=width:170px maxlength=32 autocomplete=nope placeholder="admin" onchange=validateDeviceAmtSettings() onkeyup=validateDeviceAmtSettings() />');
x += addHtmlValue("Password", '<input id=dp10password type=password style=width:170px autocomplete=nope maxlength=32 onchange=validateDeviceAmtSettings() onkeyup=validateDeviceAmtSettings() />');
@ -2166,9 +2152,8 @@
function p10showiconselector() {
if (xxdialogMode) return;
var mesh = meshes[currentNode.meshid];
var meshrights = mesh.links[userinfo._id].rights;
if ((meshrights & 4) == 0) return;
var rights = GetNodeRights(currentNode);
if ((rights & 4) == 0) return;
var x = '<table align=center><td>';
x += '<div style=display:inline-block class=i1 onclick=p10setIcon(1)></div>';
@ -2245,7 +2230,7 @@
var mesh = meshes[currentNode.meshid];
var deskState = 0;
if (desktop != null) { deskState = desktop.State; }
var meshrights = mesh.links[userinfo._id].rights;
var meshrights = GetNodeRights(currentNode);
// Show the right buttons
QV('disconnectbutton1', (deskState != 0));
@ -3096,7 +3081,7 @@
if (currentMesh == null) return;
QH('p20meshName', EscapeHtml(currentMesh.name));
var meshtype = format("Unknown #{0}", currentMesh.mtype);
var meshrights = currentMesh.links[userinfo._id].rights;
var meshrights = GetMeshRights(currentMesh);
if (currentMesh.mtype == 1) meshtype = "Intel&reg; AMT only, no agent";
if (currentMesh.mtype == 2) meshtype = "Managed using a software agent";
@ -3229,7 +3214,7 @@
}
function p20validateAddMeshUserDialog() {
var meshrights = currentMesh.links[userinfo._id].rights;
var meshrights = GetMeshRights(currentMesh);
var nc = !Q('p20fulladmin').checked;
QE('p20fulladmin', meshrights == 0xFFFFFFFF);
QE('p20editmesh', nc && (meshrights == 0xFFFFFFFF));
@ -3278,7 +3263,7 @@
function p20viewuser(userid) {
if (xxdialogMode) return;
userid = decodeURIComponent(userid);
var r = [], cmeshrights = currentMesh.links[userinfo._id].rights, meshrights = currentMesh.links[userid].rights;
var r = [], cmeshrights = GetMeshRights(currentMesh), meshrights = GetMeshRights(currentMesh, userid);
if (meshrights == 0xFFFFFFFF) r.push("Full Administrator"); else {
if ((meshrights & 1) != 0) r.push("Edit Device Group");
if ((meshrights & 2) != 0) r.push("Manage Device Group Users");
@ -3361,6 +3346,49 @@
if (((b & 8) || x) && f) f(x, t);
}
//
// Access Control Functions
// These must match server
//
// Get the right of a user on a given device group
function GetMeshRights(mesh, user) {
if (mesh == null) { return 0; }
if (user == null) { user = userinfo._id; }
if (typeof mesh == 'string') { mesh = meshes[mesh] }
if ((mesh == null) || (mesh.links == null)) { return 0; }
var rights = mesh.links[user];
if (rights == null) { return 0; }
return rights.rights;
}
// Returns true if the user can view the given device group
function IsMeshViewable(mesh, user) {
if (mesh == null) { return 0; }
if (user == null) { user = userinfo._id; }
if (typeof mesh == 'string') { mesh = meshes[mesh] }
if ((mesh == null) || (mesh.links == null)) { return false; }
var rights = mesh.links[user];
if (rights == null) { return false; }
return true;
}
// Return the user rights for a given node
function GetNodeRights(node, user) {
if (node == null) { return 0; }
if (user == null) { user = userinfo._id; }
if (typeof node == 'string') { node = getNodeFromId(node); if (node == null) { return 0; } }
var mesh = meshes[node.meshid];
if ((mesh == null) || (mesh.links == null)) { return 0; }
var meshlinks = mesh.links[user];
if (meshlinks == null) { return 0; }
return meshlinks.rights;
}
//
// Generic Methods
//
function putstore(name, val) { try { if ((typeof (localStorage) === 'undefined') || (localStorage.getItem(name) == val)) return; if (val == null) { localStorage.removeItem(name); } else { localStorage.setItem(name, val); } } catch (e) { } if (name[0] != '_') { var s = {}; for (var i = 0, len = localStorage.length; i < len; ++i) { var k = localStorage.key(i); if (k[0] != '_') { s[k] = localStorage.getItem(k); } } meshserver.send({ action: 'userWebState', state: JSON.stringify(s) }); } }
function getstore(name, val) { try { if (typeof (localStorage) === 'undefined') return val; var v = localStorage.getItem(name); if ((v == null) || (v == null)) return val; return v; } catch (e) { return val; } }
function center() { QS('dialog').left = ((((getDocWidth() - 300) / 2)) + 'px'); deskAdjust(); deskAdjust(); /*drawDeviceTimeline();*/ }

View File

@ -2587,8 +2587,7 @@
if (!xxdialogMode && xxcurrentView == 11 && desktop && Q('DeskControl').checked) {
// Check what keys we are allows to send
if (currentNode != null) {
var mesh = meshes[currentNode.meshid];
var meshrights = mesh.links[userinfo._id].rights;
var meshrights = GetMeshRights(currentNode.meshid);
var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
if (inputAllowed == false) return false;
var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
@ -2647,8 +2646,7 @@
if (!xxdialogMode && xxcurrentView == 11 && desktop && Q('DeskControl').checked) {
// Check what keys we are allows to send
if (currentNode != null) {
var mesh = meshes[currentNode.meshid];
var meshrights = mesh.links[userinfo._id].rights;
var meshrights = GetMeshRights(currentNode.meshid);
var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
if (inputAllowed == false) return false;
var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
@ -2682,8 +2680,7 @@
if (!xxdialogMode && xxcurrentView == 11 && desktop && Q('DeskControl').checked) {
// Check what keys we are allows to send
if (currentNode != null) {
var mesh = meshes[currentNode.meshid];
var meshrights = mesh.links[userinfo._id].rights;
var meshrights = GetMeshRights(currentNode.meshid);
var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
if (inputAllowed == false) return false;
var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
@ -2783,10 +2780,9 @@
for (var i in nodes) {
var node = nodes[i];
if (node.v == false) continue;
var mesh2 = meshes[node.meshid], meshlinks = mesh2.links[userinfo._id];
if (meshlinks == null) continue;
var meshrights = meshlinks.rights;
var mesh2 = meshes[node.meshid];
if ((view == 3) && (mesh2.mtype == 1)) continue;
var meshrights = GetNodeRights(node);
if (sort == 0) {
// Mesh header
if (node.meshid != current) {
@ -2948,9 +2944,7 @@
if ((sort == 0) && (Q('SearchInput').value == '') && (view < 3)) {
var deviceHeaderId2 = deviceHeaderId;
for (var i in meshes) {
var mesh = meshes[i], meshlink = mesh.links[userinfo._id];
if (meshlink != null) {
var meshrights = meshlink.rights;
var mesh = meshes[i], meshrights = GetMeshRights(mesh);
if (displayedMeshes[mesh._id] == null) {
if ((current != '') && (r != '')) { r += '</tr></table>'; }
r += '<table style=width:100%;padding-top:4px cellpadding=0 cellspacing=0><tr><td colspan=3 class=DevSt>';
@ -2981,7 +2975,6 @@
}
}
}
}
if (r != '') {
r += '</table>';
@ -3107,8 +3100,8 @@
function toggleKvmDevice(node) {
if (typeof node == 'string') { node = getNodeFromId(node); } // Convert nodeid to node if needed
var mesh = meshes[node.meshid], meshrights = mesh.links[userinfo._id].rights;
if ((meshrights & 8) || (meshrights & 256)) { // Requires remote control rights or desktop view only rights
var rights = GetNodeRights(node);
if ((rights & 8) || (rights & 256)) { // Requires remote control rights or desktop view only rights
//var conn = 0;
//if ((node.conn & 1) != 0) { conn = 1; } else if ((node.conn & 6) != 0) { conn = 2; } // Check what type of connect we can do (Agent vs AMT)
if (node.conn & 1) { connectMultiDesktop(node, 1); }
@ -3133,8 +3126,8 @@
for (var i in nodes) {
var node = nodes[i], nodeid = nodes[i]._id;
if ((multiDesktop[nodeid] == null) && ((multiDesktopFilter.length == 0) || (multiDesktopFilter.indexOf('devid_' + nodeid) >= 0))) {
var mesh = meshes[node.meshid], meshrights = mesh.links[userinfo._id].rights;
if ((meshrights & 8) || (meshrights & 256)) { // Requires remote control rights or desktop view only rights
var rights = GetNodeRights(node);
if ((rights & 8) || (rights & 256)) { // Requires remote control rights or desktop view only rights
//var conn = 0;
//if ((node.conn & 1) != 0) { conn = 1; } else if ((node.conn & 6) != 0) { conn = 2; } // Check what type of connect we can do (Agent vs AMT)
if ((node.conn & 1) && (node.v == true)) { count++; }
@ -3646,9 +3639,8 @@
// Display the "Uninstall Agent" option if allowed and we selected connected devices.
for (var i in nodeids) {
var node = getNodeFromId(nodeids[i]);
var mesh = meshes[node.meshid];
var meshrights = mesh.links[userinfo._id].rights;
if (((node.conn & 1) != 0) && ((meshrights & 32768) != 0)) { addedOptions += '<option value=104>' + "Uninstall Agent" + '</option>'; break; }
var rights = GetNodeRights(node);
if (((node.conn & 1) != 0) && ((rights & 32768) != 0)) { addedOptions += '<option value=104>' + "Uninstall Agent" + '</option>'; break; }
}
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 @@
var nodeid = contextelement.children[1].attributes.onclick.value;
var node = getNodeFromId(nodeid.substring(12, nodeid.length - 18));
var mesh = meshes[node.meshid];
var meshlinks = mesh.links[userinfo._id];
var meshrights = meshlinks.rights;
var consoleRights = ((meshrights & 16) != 0);
var rights = GetNodeRights(node);
var consoleRights = ((rights & 16) != 0);
// Check if we have terminal and file access
var terminalAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 512) == 0));
var fileAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 1024) == 0));
var terminalAccess = ((rights == 0xFFFFFFFF) || ((rights & 512) == 0));
var fileAccess = ((rights == 0xFFFFFFFF) || ((rights & 1024) == 0));
QV('cxdesktop', ((mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 1) != 0) || (node.intelamt && (node.intelamt.state == 2))) && ((meshrights & 8) || (meshrights & 256)));
QV('cxterminal', ((mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 2) != 0) || (node.intelamt && (node.intelamt.state == 2))) && (meshrights & 8) && terminalAccess);
QV('cxfiles', ((mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 4) != 0))) && (meshrights & 8) && fileAccess);
QV('cxevents', (node.intelamt != null) && ((node.intelamt.state == 2) || (node.conn & 2)) && (meshrights & 8));
QV('cxconsole', (consoleRights && (mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 8) != 0))) && (meshrights & 8));
QV('cxdesktop', ((mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 1) != 0) || (node.intelamt && (node.intelamt.state == 2))) && ((rights & 8) || (rights & 256)));
QV('cxterminal', ((mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 2) != 0) || (node.intelamt && (node.intelamt.state == 2))) && (rights & 8) && terminalAccess);
QV('cxfiles', ((mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 4) != 0))) && (rights & 8) && fileAccess);
QV('cxevents', (node.intelamt != null) && ((node.intelamt.state == 2) || (node.conn & 2)) && (rights & 8));
QV('cxconsole', (consoleRights && (mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 8) != 0))) && (rights & 8));
}
return haltEvent(event);
@ -4488,11 +4479,6 @@
gotoDevice(nodeid, xxcurrentView, true);
}
function getNodeRights(nodeid) {
var node = getNodeFromId(nodeid), mesh = meshes[node.meshid];
return mesh.links[userinfo._id].rights;
}
var currentNode;
var powerTimelineNode = null;
var powerTimelineReq = null;
@ -4515,7 +4501,7 @@
//disconnectAllKvmFunction();
var node = getNodeFromId(nodeid);
var mesh = meshes[node.meshid];
var meshrights = mesh.links[userinfo._id].rights;
var meshrights = GetNodeRights(node);
if (!currentNode || currentNode._id != node._id || refresh == true) {
currentNode = node;
@ -4857,13 +4843,13 @@
function deviceActionFunction() {
if (xxdialogMode) return;
var meshrights = meshes[currentNode.meshid].links[userinfo._id].rights;
var rights = GetNodeRights(currentNode);
var x = "Select an operation to perform on this device." + '<br /><br />';
var y = '<select id=d2deviceop style=float:right;width:250px>';
if ((meshrights & 64) != 0) { y += '<option value=100>' + "Wake-up" + '</option>'; } // Wake-up permission
if ((meshrights & 8) != 0) { y += '<option value=4>' + "Sleep" + '</option><option value=3>' + "Reset" + '</option><option value=2>' + "Power off" + '</option>'; } // Remote control permission
if ((rights & 64) != 0) { y += '<option value=100>' + "Wake-up" + '</option>'; } // Wake-up permission
if ((rights & 8) != 0) { y += '<option value=4>' + "Sleep" + '</option><option value=3>' + "Reset" + '</option><option value=2>' + "Power off" + '</option>'; } // Remote control permission
if ((currentNode.conn & 16) != 0) { y += '<option value=103>' + "Send MQTT Message" + '</option>'; }
if (((currentNode.conn & 1) != 0) && ((meshrights & 32768) != 0)) { y += '<option value=104>' + "Uninstall Agent" + '</option>'; }
if (((currentNode.conn & 1) != 0) && ((rights & 32768) != 0)) { y += '<option value=104>' + "Uninstall Agent" + '</option>'; }
y += '</select>';
x += addHtmlValue("Operation", y);
setDialogMode(2, "Device Action", 3, deviceActionFunctionEx, x);
@ -4975,7 +4961,7 @@
function editDeviceAmtSettings(nodeid, func, arg) {
if (xxdialogMode) return;
var x = '', node = getNodeFromId(nodeid), buttons = 3, meshrights = getNodeRights(nodeid);
var x = '', node = getNodeFromId(nodeid), buttons = 3, meshrights = GetNodeRights(node);
if ((meshrights & 4) == 0) return;
x += addHtmlValue("Username", '<input id=dp10username style=width:230px maxlength=32 autocomplete=nope placeholder="admin" onchange=validateDeviceAmtSettings() onkeyup=validateDeviceAmtSettings() />');
x += addHtmlValue("Password", '<input id=dp10password type=password style=width:230px autocomplete=nope maxlength=32 onchange=validateDeviceAmtSettings() onkeyup=validateDeviceAmtSettings() />');
@ -5049,7 +5035,7 @@
// List all available alternative groups
var y = '<select id=p10newGroup style=width:236px>', count = 0;
for (var i in meshes) {
var meshrights = meshes[i].links[userinfo._id].rights;
var meshrights = GetMeshRights(i);
if ((meshes[i]._id != targetMeshId) && (meshrights & 4)) { count++; y += '<option value=\'' + meshes[i]._id + '\'>' + meshes[i].name + '</option>'; }
}
y += '</select>';
@ -5195,17 +5181,16 @@
function p10showiconselector() {
if (xxdialogMode) return;
var mesh = meshes[currentNode.meshid];
var meshrights = mesh.links[userinfo._id].rights;
if ((meshrights & 4) == 0) return;
if ((GetNodeRights(currentNode) & 4) == 0) return;
var x = '<br><div style=display:inline-block;width:40px></div>';
var x = '<br><div style=display:inline-block;width:16px></div>';
x += '<div tabindex=0 style=display:inline-block class=i1 onclick=p10setIcon(1) onkeypress="if (event.key==\'Enter\') p10setIcon(1)"></div>';
x += '<div tabindex=0 style=display:inline-block class=i2 onclick=p10setIcon(2) onkeypress="if (event.key==\'Enter\') p10setIcon(2)"></div>';
x += '<div tabindex=0 style=display:inline-block class=i3 onclick=p10setIcon(3) onkeypress="if (event.key==\'Enter\') p10setIcon(3)"></div>';
x += '<div tabindex=0 style=display:inline-block class=i4 onclick=p10setIcon(4) onkeypress="if (event.key==\'Enter\') p10setIcon(4)"></div>';
x += '<div tabindex=0 style=display:inline-block class=i5 onclick=p10setIcon(5) onkeypress="if (event.key==\'Enter\') p10setIcon(5)"></div>';
x += '<div tabindex=0 style=display:inline-block class=i6 onclick=p10setIcon(6) onkeypress="if (event.key==\'Enter\') p10setIcon(6)"></div><br><br>';
x += '<div tabindex=0 style=display:inline-block class=i6 onclick=p10setIcon(6) onkeypress="if (event.key==\'Enter\') p10setIcon(6)"></div>';
x += '<div tabindex=0 style=display:inline-block class=i7 onclick=p10setIcon(7) onkeypress="if (event.key==\'Enter\') p10setIcon(7)"></div><br><br>';
setDialogMode(2, "Icon Selection", 0, null, x);
QV('id_dialogclose', true);
}
@ -5292,14 +5277,14 @@
var mesh = meshes[currentNode.meshid];
var deskState = 0;
if (desktop != null) { deskState = desktop.State; }
var meshrights = mesh.links[userinfo._id].rights;
var rights = GetNodeRights(currentNode);
// Show the right buttons
QV('disconnectbutton1span', (deskState != 0));
QV('connectbutton1span', (deskState == 0) && ((meshrights & 8) || (meshrights & 256)) && (mesh.mtype == 2) && (currentNode.agent.caps & 1));
QV('connectbutton1span', (deskState == 0) && ((rights & 8) || (rights & 256)) && (mesh.mtype == 2) && (currentNode.agent.caps & 1));
QV('connectbutton1hspan',
(deskState == 0) &&
(meshrights & 8) &&
(rights & 8) &&
((mesh.mtype == 1) ||
((currentNode.intelamt != null) &&
(currentNode.intelamt.state == 2) &&
@ -5314,7 +5299,7 @@
QV('d7meshkvm', (webRtcDesktop) || ((mesh.mtype == 2) && (currentNode.agent.caps & 1) && ((deskState == false) || (desktop.contype == 1))));
// Enable buttons
var inputAllowed = (meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) == 0));
var inputAllowed = (rights == 0xFFFFFFFF) || (((rights & 8) != 0) && ((rights & 256) == 0) && ((rights & 4096) == 0));
var online = ((currentNode.conn & 1) != 0); // If Agent (1) connected, enable remote desktop
QE('connectbutton1', online);
var hwonline = ((currentNode.conn & 6) != 0); // If CIRA (2) or AMT (4) connected, enable hardware terminal
@ -5330,8 +5315,8 @@
QE('deskkeys', deskState == 3);
// Display this only if we have Chat & Notify permissions
QV('DeskChatButton', ((meshrights & 16384) != 0) && (browserfullscreen == false) && (inputAllowed) && (mesh.mtype == 2) && online);
QV('DeskNotifyButton', ((meshrights & 16384) != 0) && (browserfullscreen == false) && (currentNode.agent) && (currentNode.agent.id < 5) && (inputAllowed) && (mesh.mtype == 2) && online);
QV('DeskChatButton', ((rights & 16384) != 0) && (browserfullscreen == false) && (inputAllowed) && (mesh.mtype == 2) && online);
QV('DeskNotifyButton', ((rights & 16384) != 0) && (browserfullscreen == false) && (currentNode.agent) && (currentNode.agent.id < 5) && (inputAllowed) && (mesh.mtype == 2) && online);
QV('DeskToolsButton', (inputAllowed) && (mesh.mtype == 2) && online);
QV('DeskOpenWebButton', (browserfullscreen == false) && (inputAllowed) && (mesh.mtype == 2) && online);
@ -5339,7 +5324,7 @@
QV('DeskControlSpan', inputAllowed)
QV('deskActionsBtn', (browserfullscreen == false));
QV('deskActionsSettings', (browserfullscreen == false));
if (meshrights & 8) { Q('DeskControl').checked = (getstore('DeskControl', 1) == 1); } else { Q('DeskControl').checked = false; }
if (rights & 8) { Q('DeskControl').checked = (getstore('DeskControl', 1) == 1); } else { Q('DeskControl').checked = false; }
if (online == false) QV('DeskTools', false);
}
@ -6966,8 +6951,8 @@
consoleNode = currentNode;
var mesh = meshes[consoleNode.meshid];
var meshrights = mesh.links[userinfo._id].rights;
if ((meshrights & 16) != 0) {
var rights = GetNodeRights(currentNode);
if ((rights & 16) != 0) {
if (consoleNode.consoleText == null) { consoleNode.consoleText = ''; }
if (samenode == false) {
QH('p15agentConsoleText', consoleNode.consoleText);
@ -7443,8 +7428,7 @@
count++;
// Mesh rights
var meshrights = 0;
if (meshes[i].links[userinfo._id]) { meshrights = meshes[i].links[userinfo._id].rights; }
var meshrights = GetMeshRights(meshes[i]);
var rights = "Partial Rights";
if (meshrights == 0xFFFFFFFF) rights = "Full Administrator"; else if (meshrights == 0) rights = "No Rights";
@ -7511,8 +7495,7 @@
if (currentMesh == null) return;
QH('p20meshName', EscapeHtml(currentMesh.name));
var meshtype = format("Unknown #{0}", currentMesh.mtype);
var meshrights = 0;
try { meshrights = currentMesh.links[userinfo._id].rights; } catch (ex) { }
var meshrights = GetMeshRights(currentMesh);
if (currentMesh.mtype == 1) meshtype = "Intel&reg; AMT only, no agent";
if (currentMesh.mtype == 2) meshtype = "Managed using a software agent";
@ -7580,10 +7563,9 @@
if (meshrights & 1) { x += '<br><input type=button value=' + "Notes" + ' title=\"' + "View notes about this device group" + '\" onclick=showNotes(false,"' + encodeURIComponent(currentMesh._id) + '") />'; }
x += '<br style=clear:both><br>';
var currentMeshLinks = currentMesh.links[userinfo._id];
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>'; }
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>'; }
if ((meshrights & 4) != 0) {
if (meshrights & 4) {
if (currentMesh.mtype == 1) {
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>';
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 @@
Q('dp20username').focus();
} else {
setDialogMode(2, "Edit User Device Group Permissions", 7, p20showAddMeshUserDialogEx, x, userid);
var cmeshrights = currentMesh.links[userinfo._id].rights, meshrights = currentMesh.links[userid].rights;
var cmeshrights = GetMeshRights(currentMesh), meshrights = GetMeshRights(currentMesh, userid);
if (meshrights == 0xFFFFFFFF) {
Q('p20fulladmin').checked = true;
} else {
@ -7877,7 +7859,7 @@
}
function p20validateAddMeshUserDialog() {
var meshrights = currentMesh.links[userinfo._id].rights;
var meshrights = GetMeshRights(currentMesh);
var ok = true;
if (Q('dp20username')) {
var xusers = Q('dp20username').value.split(',');
@ -7964,7 +7946,7 @@
function p20viewuser(userid) {
if (xxdialogMode) return;
var xuserid = decodeURIComponent(userid);
var cmeshrights = currentMesh.links[userinfo._id].rights, meshrights = currentMesh.links[xuserid].rights;
var cmeshrights = GetMeshRights(currentMesh), meshrights = GetMeshRights(currentMesh, xuserid);
if (((userinfo._id) != xuserid) && (cmeshrights == 0xFFFFFFFF || (((cmeshrights & 2) != 0) && (meshrights != 0xFFFFFFFF)))) {
p20showAddMeshUserDialog(userid);
} else {
@ -10063,7 +10045,49 @@
if (pname == null) { Q('p43iframe').src = ''; } else { QH('p43title', title); Q('p43iframe').src = '/pluginadmin.ashx?pin=' + pname; go(43); }
}
//
// Access Control Functions
// These must match server
//
// Get the right of a user on a given device group
function GetMeshRights(mesh, user) {
if (mesh == null) { return 0; }
if (user == null) { user = userinfo._id; }
if (typeof mesh == 'string') { mesh = meshes[mesh] }
if ((mesh == null) || (mesh.links == null)) { return 0; }
var rights = mesh.links[user];
if (rights == null) { return 0; }
return rights.rights;
}
// Returns true if the user can view the given device group
function IsMeshViewable(mesh, user) {
if (mesh == null) { return 0; }
if (user == null) { user = userinfo._id; }
if (typeof mesh == 'string') { mesh = meshes[mesh] }
if ((mesh == null) || (mesh.links == null)) { return false; }
var rights = mesh.links[user];
if (rights == null) { return false; }
return true;
}
// Return the user rights for a given node
function GetNodeRights(node, user) {
if (node == null) { return 0; }
if (user == null) { user = userinfo._id; }
if (typeof node == 'string') { node = getNodeFromId(node); if (node == null) { return 0; } }
var mesh = meshes[node.meshid];
if ((mesh == null) || (mesh.links == null)) { return 0; }
var meshlinks = mesh.links[user];
if (meshlinks == null) { return 0; }
return meshlinks.rights;
}
//
// Generic methods
//
function joinPaths() { var x = []; for (var i in arguments) { var w = arguments[i]; if ((w != null) && (w != '')) { while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); } while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); } x.push(w); } } return x.join('/'); }
function putstore(name, val) {
try {

View File

@ -2065,7 +2065,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if ((splitpath.length < 3) || (splitpath[0] != 'user' && splitpath[0] != 'mesh') || (splitpath[1] != domain.id)) return null; // Basic validation
var objid = splitpath[0] + '/' + splitpath[1] + '/' + splitpath[2];
if (splitpath[0] == 'user' && (objid != user._id)) return null; // User validation, only self allowed
if (splitpath[0] == 'mesh') { var link = user.links[objid]; if ((link == null) || (link.rights == null) || ((link.rights & 32) == 0)) { return null; } } // Check mesh server file rights
if (splitpath[0] == 'mesh') { if ((obj.GetMeshRights(user, objid) & 32) == 0) { return null; } } // Check mesh server file rights
if (splitpath[1] != '') { serverpath += '-' + splitpath[1]; } // Add the domain if needed
serverpath += ('/' + splitpath[0] + '-' + splitpath[2]);
for (var i = 3; i < splitpath.length; i++) { if (obj.common.IsFilenameValid(splitpath[i]) == true) { serverpath += '/' + splitpath[i]; filename = splitpath[i]; } else { return null; } } // Check that each folder is correct
@ -2265,8 +2265,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (!node.intelamt) { console.log('ERR: Not AMT node'); try { ws.close(); } catch (e) { } return; } // Disconnect websocket
// Check if this user has permission to manage this computer
var meshlinks = user.links[node.meshid];
if ((!meshlinks) || (!meshlinks.rights) || ((meshlinks.rights & MESHRIGHT_REMOTECONTROL) == 0)) { console.log('ERR: Access denied (2)'); try { ws.close(); } catch (e) { } return; }
if ((obj.GetMeshRights(user, node.meshid) & MESHRIGHT_REMOTECONTROL) == 0) { console.log('ERR: Access denied (2)'); try { ws.close(); } catch (e) { } return; }
// Check what connectivity is available for this node
var state = parent.GetConnectivityState(req.query.host);
@ -2998,9 +2997,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// If required, check if this user has rights to do this
if ((obj.parent.config.settings != null) && ((obj.parent.config.settings.lockagentdownload == true) || (domain.lockagentdownload == true))) {
var user = obj.users[req.session.userid];
if ((user == null) || (mesh.links[user._id] == null) || ((mesh.links[user._id].rights & 1) == 0)) { res.sendStatus(401); return; }
if (domain.id != mesh.domain) { res.sendStatus(401); return; }
if ((domain.id != mesh.domain) || ((obj.GetMeshRights(req.session.userid, mesh) & 1) == 0)) { res.sendStatus(401); return; }
}
var meshidhex = Buffer.from(req.query.meshid.replace(/\@/g, '+').replace(/\$/g, '/'), 'base64').toString('hex').toUpperCase();
@ -3168,9 +3165,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// If required, check if this user has rights to do this
if ((obj.parent.config.settings != null) && ((obj.parent.config.settings.lockagentdownload == true) || (domain.lockagentdownload == true))) {
var user = obj.users[req.session.userid];
if ((user == null) || (mesh.links[user._id] == null) || ((mesh.links[user._id].rights & 1) == 0)) { res.sendStatus(401); return; }
if (domain.id != mesh.domain) { res.sendStatus(401); return; }
if ((domain.id != mesh.domain) || ((obj.GetMeshRights(req.session.userid, mesh) & 1) == 0)) { res.sendStatus(401); return; }
}
var meshidhex = Buffer.from(req.query.meshid.replace(/\@/g, '+').replace(/\$/g, '/'), 'base64').toString('hex').toUpperCase();
@ -3257,9 +3252,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// If needed, check if this user has rights to do this
if ((obj.parent.config.settings != null) && ((obj.parent.config.settings.lockagentdownload == true) || (domain.lockagentdownload == true))) {
var user = obj.users[req.session.userid];
if ((user == null) || (mesh.links[user._id] == null) || ((mesh.links[user._id].rights & 1) == 0)) { res.sendStatus(401); return; }
if (domain.id != mesh.domain) { res.sendStatus(401); return; }
if ((domain.id != mesh.domain) || ((obj.GetMeshRights(req.session.userid, mesh) & 1) == 0)) { res.sendStatus(401); return; }
}
var meshidhex = Buffer.from(req.query.id.replace(/\@/g, '+').replace(/\$/g, '/'), 'base64').toString('hex').toUpperCase();
@ -3297,9 +3290,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
var node = docs[0];
// Check if we have right to this node
var rights = 0;
for (var i in user.links) { if (i == node.meshid) { rights = user.links[i].rights; } }
if (rights == 0) { res.sendStatus(401); return; }
if (obj.GetMeshRights(user, node.meshid) == 0) { res.sendStatus(401); return; }
// Get the list of power events and send them
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'text/csv', 'Content-Disposition': 'attachment; filename="powerevents.csv"' });
@ -3772,8 +3763,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (agent == null) return;
// Check we have agent rights
var rights = user.links[agent.dbMeshKey].rights;
if ((rights != null) && ((rights & MESHRIGHT_AGENTCONSOLE) != 0) || (user.siteadmin == 0xFFFFFFFF)) { agent.close(disconnectMode); }
if (((obj.GetMeshRights(user, agent.dbMeshKey) & MESHRIGHT_AGENTCONSOLE) != 0) || (user.siteadmin == 0xFFFFFFFF)) { agent.close(disconnectMode); }
};
// Send the core module to the mesh agent
@ -3787,8 +3777,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (agent == null) return;
// Check we have agent rights
var rights = user.links[agent.dbMeshKey].rights;
if ((rights != null) && ((rights & MESHRIGHT_AGENTCONSOLE) != 0) || (user.siteadmin == 0xFFFFFFFF)) {
if (((obj.GetMeshRights(user, agent.dbMeshKey) & MESHRIGHT_AGENTCONSOLE) != 0) || (user.siteadmin == 0xFFFFFFFF)) {
if (coretype == 'clear') {
// Clear the mesh agent core
agent.agentCoreCheck = 1000; // Tell the agent object we are using a custom core.
@ -3907,6 +3896,32 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
}
};
//
// Access Control Functions
//
// Return the node and rights for a given nodeid
obj.GetNodeWithRights = function (domain, user, nodeid, func) {
// Perform user pre-validation
if ((user == null) || (nodeid == null)) { func(null, 0, false); return; } // Invalid user
if (typeof user == 'string') { user = obj.users[user]; }
if ((user == null) || (user.links == null)) { func(null, 0, false); return; } // No rights
// Perform node pre-validation
if (obj.common.validateString(nodeid, 0, 128) == false) { func(null, 0, false); return; } // Invalid nodeid
const snode = nodeid.split('/');
if ((snode.length != 3) || (snode[0] != 'node')) { func(null, 0, false); return; } // Invalid nodeid
if ((domain != null) && (snode[1] != domain.id)) { func(null, 0, false); return; } // Invalid domain
// Check that we have permissions for this node.
db.Get(nodeid, function (err, nodes) {
if ((nodes == null) || (nodes.length != 1)) { func(null, 0, false); return; } // No such nodeid
var rights = user.links[nodes[0].meshid];
if (rights == null) { func(null, 0, false); return; } // No rights to this mesh
func(nodes[0], rights.rights, true);
});
}
// Returns a list of all meshes that this user has some rights too
obj.GetAllMeshWithRights = function (user, rights) {
if (typeof user == 'string') { user = obj.users[user]; }
@ -4076,9 +4091,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (typeof command.sessionid != 'string') return;
var splitsessionid = command.sessionid.split('/');
// Check that we are in the same domain and the user has rights over this node.
if ((splitsessionid[0] == 'user') && (splitsessionid[1] == domainid)) {
if ((splitsessionid.length == 4) && (splitsessionid[0] == 'user') && (splitsessionid[1] == domainid)) {
// Check if this user has rights to get this message
//if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 16) == 0)) return; // TODO!!!!!!!!!!!!!!!!!!!!!
if (obj.GetMeshRights(splitsessionid[0] + '/' + splitsessionid[1] + '/' + splitsessionid[2], meshid) == 0) return; // TODO: Check if this is ok
// See if the session is connected. If so, go ahead and send this message to the target node
var ws = obj.wssessions2[command.sessionid];
@ -4101,7 +4116,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Check that we are in the same domain and the user has rights over this node.
if ((splituserid[0] == 'user') && (splituserid[1] == domainid)) {
// Check if this user has rights to get this message
//if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 16) == 0)) return; // TODO!!!!!!!!!!!!!!!!!!!!!
if (obj.GetMeshRights(command.userid, meshid) == 0) return; // TODO: Check if this is ok
// See if the session is connected
var sessions = obj.wssessions[command.userid];
@ -4120,17 +4135,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} else { // Route this command to the mesh
command.nodeid = nodeid;
var cmdstr = JSON.stringify(command);
for (var userid in obj.wssessions) { // Find all connected users for this mesh and send the message
var user = obj.users[userid];
if ((user != null) && (user.links != null)) {
var rights = user.links[meshid];
if (rights != null) { // TODO: Look at what rights are needed for message routing
if (obj.GetMeshRights(userid, meshid) == 0) return; // TODO: Check if this is ok
// Find all connected users for this mesh and send the message
for (var userid in obj.wssessions) {
var xsessions = obj.wssessions[userid];
// Send the message to all users on this server
for (i in xsessions) { try { xsessions[i].send(cmdstr); } catch (e) { } }
}
}
}
// Send the message to all users of other servers
if (parent.multiServer != null) {