Added device notes support

This commit is contained in:
Ylian Saint-Hilaire 2018-04-12 18:14:03 -07:00
parent bcb7100ced
commit 4080abd217
14 changed files with 114 additions and 18 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -817,7 +817,9 @@ function createMeshCore(agent) {
this.websocket.rtcchannel.on('end', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed');*/ }); this.websocket.rtcchannel.on('end', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed');*/ });
this.websocket.write("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc0\"}"); // Indicate we are ready for WebRTC switch-over. this.websocket.write("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc0\"}"); // Indicate we are ready for WebRTC switch-over.
}); });
ws.write({ type: 'answer', ctrlChannel: '102938', sdp: ws.webrtc.setOffer(obj.sdp) }); var sdp = null;
try { sdp = ws.webrtc.setOffer(obj.sdp); } catch (ex) { }
if (sdp != null) { ws.write({ type: 'answer', ctrlChannel: '102938', sdp: sdp }); }
} }
} }

1
db.js
View File

@ -92,6 +92,7 @@ module.exports.CreateDB = function (args, datapath) {
obj.GetUserWithEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email }, { type: 0 }, func); } obj.GetUserWithEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email }, { type: 0 }, func); }
obj.GetUserWithVerifiedEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email, emailVerified: true }, { type: 0 }, func); } obj.GetUserWithVerifiedEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email, emailVerified: true }, { type: 0 }, func); }
obj.Remove = function (id) { obj.file.remove({ _id: id }); } obj.Remove = function (id) { obj.file.remove({ _id: id }); }
obj.RemoveNode = function (id) { obj.file.remove({ node: id }, { multi: true }); }
obj.RemoveAll = function (func) { obj.file.remove({}, { multi: true }, func); } obj.RemoveAll = function (func) { obj.file.remove({}, { multi: true }, func); }
obj.RemoveAllOfType = function (type, func) { obj.file.remove({ type: type }, { multi: true }, func); } obj.RemoveAllOfType = function (type, func) { obj.file.remove({ type: type }, { multi: true }, func); }
obj.InsertMany = function (data, func) { obj.file.insert(data, func); } obj.InsertMany = function (data, func) { obj.file.insert(data, func); }

View File

@ -95,6 +95,9 @@ module.exports.CreateLetsEncrypt = function (parent) {
agreeTos: true, agreeTos: true,
rsaKeySize: rsaKeySize, rsaKeySize: rsaKeySize,
challengeType: 'http-01' challengeType: 'http-01'
//renewWithin: 15 * 24 * 60 * 60 * 1000 // 15 days
//renewWithin: 81 * 24 * 60 * 60 * 1000, // 81 days
//renewBy: 80 * 24 * 60 * 60 * 1000 // 80 days
}).then(function (xresults) { }).then(function (xresults) {
obj.parent.performServerCertUpdate(); // Reset the server, TODO: Reset all peers obj.parent.performServerCertUpdate(); // Reset the server, TODO: Reset all peers
}, function (err) { }, function (err) {

View File

@ -50,8 +50,10 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
if (obj.agentUpdate != null) { obj.fs.close(obj.agentUpdate.fd); obj.agentUpdate = null; } if (obj.agentUpdate != null) { obj.fs.close(obj.agentUpdate.fd); obj.agentUpdate = null; }
if (obj.agentInfo.capabilities & 0x20) { // This is a temporary agent, remote it if (obj.agentInfo.capabilities & 0x20) { // This is a temporary agent, remote it
// Delete this node including network interface information and events // Delete this node including network interface information and events
obj.db.Remove(obj.dbNodeKey); obj.db.Remove(obj.dbNodeKey); // Remove node with that id
obj.db.Remove('if' + obj.dbNodeKey); obj.db.Remove('if' + obj.dbNodeKey); // Remove interface information
obj.db.Remove('nt' + obj.dbNodeKey); // Remove notes
obj.db.RemoveNode(obj.dbNodeKey); // Remove all entries with node:id
// Event node deletion // Event node deletion
obj.parent.parent.DispatchEvent(['*', obj.dbMeshKey], obj, { etype: 'node', action: 'removenode', nodeid: obj.dbNodeKey, domain: obj.domain.id, nolog: 1 }) obj.parent.parent.DispatchEvent(['*', obj.dbMeshKey], obj, { etype: 'node', action: 'removenode', nodeid: obj.dbNodeKey, domain: obj.domain.id, nolog: 1 })
@ -276,8 +278,13 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
obj.db.Set(device); obj.db.Set(device);
// Event the new node // Event the new node
var change = 'Added device ' + obj.agentInfo.computerName + ' to mesh ' + mesh.name; if (obj.agentInfo.capabilities & 0x20) {
obj.parent.parent.DispatchEvent(['*', obj.dbMeshKey], obj, { etype: 'node', action: 'addnode', node: device, msg: change, domain: domain.id }) // This is a temporary agent, don't log.
obj.parent.parent.DispatchEvent(['*', obj.dbMeshKey], obj, { etype: 'node', action: 'addnode', node: device, domain: domain.id, nolog: 1 })
} else {
var change = 'Added device ' + obj.agentInfo.computerName + ' to mesh ' + mesh.name;
obj.parent.parent.DispatchEvent(['*', obj.dbMeshKey], obj, { etype: 'node', action: 'addnode', node: device, msg: change, domain: domain.id })
}
} else { } else {
// Device already exists, look if changes has occured // Device already exists, look if changes has occured
device = nodes[0]; device = nodes[0];
@ -292,6 +299,9 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
if (change == 1) { if (change == 1) {
obj.db.Set(device); obj.db.Set(device);
// If this is a temporary device, don't log changes
if (obj.agentInfo.capabilities & 0x20) { log = 0; }
// Event the node change // Event the node change
var event = { etype: 'node', action: 'changenode', nodeid: obj.dbNodeKey, domain: domain.id }; var event = { etype: 'node', action: 'changenode', nodeid: obj.dbNodeKey, domain: domain.id };
if (log == 0) { event.nolog = 1; } else { event.msg = 'Changed device ' + device.name + ' from mesh ' + mesh.name + ': ' + changes.join(', '); } if (log == 0) { event.nolog = 1; } else { event.msg = 'Changed device ' + device.name + ' from mesh ' + mesh.name + ': ' + changes.join(', '); }
@ -581,6 +591,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
// Event the node change // Event the node change
var event = { etype: 'node', action: 'changenode', nodeid: obj.dbNodeKey, domain: domain.id, msg: 'Changed device ' + device.name + ' from mesh ' + mesh.name + ': ' + changes.join(', ') }; var event = { etype: 'node', action: 'changenode', nodeid: obj.dbNodeKey, domain: domain.id, msg: 'Changed device ' + device.name + ' from mesh ' + mesh.name + ': ' + changes.join(', ') };
if (obj.agentInfo.capabilities & 0x20) { event.nolog = 1; } // If this is a temporary device, don't log changes
var device2 = obj.common.Clone(device); var device2 = obj.common.Clone(device);
if (device2.intelamt && device2.intelamt.pass) delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this. if (device2.intelamt && device2.intelamt.pass) delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this.
event.node = device; event.node = device;
@ -616,6 +627,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
// Event the node change // Event the node change
var event = { etype: 'node', action: 'changenode', nodeid: obj.dbNodeKey, domain: domain.id, msg: 'Changed device ' + device.name + ' from mesh ' + mesh.name + ': ' + changes.join(', ') }; var event = { etype: 'node', action: 'changenode', nodeid: obj.dbNodeKey, domain: domain.id, msg: 'Changed device ' + device.name + ' from mesh ' + mesh.name + ': ' + changes.join(', ') };
if (obj.agentInfo.capabilities & 0x20) { event.nolog = 1; } // If this is a temporary device, don't log changes
var device2 = obj.common.Clone(device); var device2 = obj.common.Clone(device);
if (device2.intelamt && device2.intelamt.pass) delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this. if (device2.intelamt && device2.intelamt.pass) delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this.
event.node = device; event.node = device;

View File

@ -713,9 +713,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
// Check if this user has rights to do this // Check if this user has rights to do this
if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 4) == 0)) return; if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 4) == 0)) return;
// Delete this node including network interface information and events // Delete this node including network interface information, events and timeline
obj.db.Remove(node._id); obj.db.Remove(node._id); // Remove node with that id
obj.db.Remove('if' + node._id); obj.db.Remove('if' + node._id); // Remove interface information
obj.db.Remove('nt' + node._id); // Remove notes
obj.db.RemoveNode(node._id); // Remove all entries with node:id
// Event node deletion // Event node deletion
var change = 'Removed device ' + node.name + ' from mesh ' + mesh.name; var change = 'Removed device ' + node.name + ' from mesh ' + mesh.name;
@ -994,6 +996,54 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
} }
break; break;
} }
case 'setNotes':
{
// Argument validation
if (obj.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
// Check if this user has rights on this nodeid to set notes
obj.db.Get(command.nodeid, function (err, nodes) { // TODO: Make a NodeRights(user) method that also does not do a db call if agent is connected (???)
if (nodes.length == 1) {
var meshlinks = user.links[nodes[0].meshid];
if ((meshlinks) && (meshlinks.rights) && (meshlinks.rights & obj.parent.MESHRIGHT_SETNOTES != 0)) {
// Set the node's notes
if (obj.common.validateString(command.notes, 1) == false) {
obj.db.Remove('nt' + command.nodeid); // Delete the note for this node
} else {
obj.db.Set({ _id: 'nt' + command.nodeid, value: command.notes }); // Set the note for this node
}
}
}
});
break;
}
case 'getNotes':
{
// Argument validation
if (obj.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
obj.db.Get(command.nodeid, function (err, nodes) {
if (nodes.length != 1) { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: command.nodeid, netif: null })); return; }
var node = nodes[0];
// Get the mesh for this device
var mesh = obj.parent.meshes[node.meshid];
if (mesh) {
// Check if this user has rights to do this
if (mesh.links[user._id] == null || (mesh.links[user._id].rights == 0)) { return; }
// Get the notes about this node
obj.db.Get('nt' + command.nodeid, function (err, notes) {
if (notes.length != 1) { ws.send(JSON.stringify({ action: 'getNotes', nodeid: command.nodeid, notes: null })); return; }
ws.send(JSON.stringify({ action: 'getNotes', nodeid: command.nodeid, notes: notes[0].value }));
});
}
});
break;
}
} }
}); });

View File

@ -1,6 +1,6 @@
{ {
"name": "meshcentral", "name": "meshcentral",
"version": "0.1.6-d", "version": "0.1.6-f",
"keywords": [ "keywords": [
"Remote Management", "Remote Management",
"Intel AMT", "Intel AMT",

View File

@ -1051,6 +1051,20 @@
} }
break; break;
} }
case 'getNotes':{
var n = Q('d2devNotes');
if (n && (message.nodeid == n.attributes['nodeid'].value)) {
if (message.notes) { QH('d2devNotes', decodeURIComponent(message.notes)); } else { QH('d2devNotes', ''); }
var ro = n.attributes['ro'].value == 'true';
if (ro == false) { // If we have permissions, set read/write on this note.
n.removeAttribute('readonly');
QE('idx_dlgOkButton', true);
QV('idx_dlgOkButton', true);
focusTextBox('d2devNotes');
}
}
break;
}
case 'event': { case 'event': {
if (!message.event.nolog) { if (!message.event.nolog) {
events.unshift(message.event); events.unshift(message.event);
@ -2761,9 +2775,10 @@
x += addDeviceAttribute('Connectivity', cstate.join(', ')); x += addDeviceAttribute('Connectivity', cstate.join(', '));
} }
x += '</table>'; x += '</table><br />';
// Show action button, only show if we have permissions 4, 8, 64 // Show action button, only show if we have permissions 4, 8, 64
if ((meshrights & 76) != 0) { x += '<br /><input type=button value=Actions title="Perform power actions on the device" onclick=deviceActionFunction() />'; } if ((meshrights & 76) != 0) { x += '<input type=button value=Actions title="Perform power actions on the device" onclick=deviceActionFunction() />'; }
x += '<input type=button value=Notes title="View notes about this device" onclick=deviceNotesFunction(' + ((meshrights & 128) == 0) + ',"' + node._id + '") />';
QH('p10html', x); QH('p10html', x);
// Show node last 7 days timeline // Show node last 7 days timeline
@ -2844,6 +2859,14 @@
go(panel); go(panel);
} }
function deviceNotesFunction(readonly, nodeid) {
if (xxdialogMode) return;
setDialogMode(2, "Device Notes", 2, deviceNotesFunctionEx, '<textarea id=d2devNotes ro=' + readonly + ' nodeid=' + nodeid + ' readonly style=width:100%;height:200px;resize:none;overflow-y:scroll></textarea>', nodeid);
meshserver.send({ action: 'getNotes', nodeid: nodeid });
}
function deviceNotesFunctionEx(buttons, tag) { meshserver.send({ action: 'setNotes', nodeid: tag, notes: encodeURIComponent(Q('d2devNotes').value) }); }
function deviceActionFunction() { function deviceActionFunction() {
var meshrights = meshes[currentNode.meshid].links['user/{{{domain}}}/' + userinfo.name.toLowerCase()].rights; var meshrights = meshes[currentNode.meshid].links['user/{{{domain}}}/' + userinfo.name.toLowerCase()].rights;
var x = "Select an operation to perform on this device.<br /><br />"; var x = "Select an operation to perform on this device.<br /><br />";
@ -4485,6 +4508,7 @@
x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20meshagentconsole>Mesh Agent Console<br>'; x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20meshagentconsole>Mesh Agent Console<br>';
x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20meshserverfiles>Server Files<br>'; x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20meshserverfiles>Server Files<br>';
x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20wakedevices>Wake Devices<br>'; x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20wakedevices>Wake Devices<br>';
x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20editnotes>Edit Notes<br>';
x += '</div>'; x += '</div>';
setDialogMode(2, "Add User to Mesh", 3, p20showAddMeshUserDialogEx, x); setDialogMode(2, "Add User to Mesh", 3, p20showAddMeshUserDialogEx, x);
p20validateAddMeshUserDialog(); p20validateAddMeshUserDialog();
@ -4502,6 +4526,7 @@
QE('p20meshagentconsole', !Q('p20fulladmin').checked); QE('p20meshagentconsole', !Q('p20fulladmin').checked);
QE('p20meshserverfiles', !Q('p20fulladmin').checked); QE('p20meshserverfiles', !Q('p20fulladmin').checked);
QE('p20wakedevices', !Q('p20fulladmin').checked); QE('p20wakedevices', !Q('p20fulladmin').checked);
QE('p20editnotes', !Q('p20fulladmin').checked);
} }
function p20showAddMeshUserDialogEx() { function p20showAddMeshUserDialogEx() {
@ -4514,6 +4539,7 @@
if (Q('p20meshagentconsole').checked == true) meshadmin += 16; if (Q('p20meshagentconsole').checked == true) meshadmin += 16;
if (Q('p20meshserverfiles').checked == true) meshadmin += 32; if (Q('p20meshserverfiles').checked == true) meshadmin += 32;
if (Q('p20wakedevices').checked == true) meshadmin += 64; if (Q('p20wakedevices').checked == true) meshadmin += 64;
if (Q('p20editnotes').checked == true) meshadmin += 128;
} }
meshserver.send({ action: 'addmeshuser', meshid: currentMesh._id, meshname: currentMesh.name, username: Q('dp20username').value , meshadmin: meshadmin}); meshserver.send({ action: 'addmeshuser', meshid: currentMesh._id, meshname: currentMesh.name, username: Q('dp20username').value , meshadmin: meshadmin});
} }
@ -4524,13 +4550,14 @@
var meshrights = currentMesh.links[userid].rights; var meshrights = currentMesh.links[userid].rights;
var r = ''; var r = '';
if (meshrights == 0xFFFFFFFF) r = ', Full Administrator (all rights)'; else { if (meshrights == 0xFFFFFFFF) r = ', Full Administrator (all rights)'; else {
if ((meshrights & 1) != 0) r += ', Edit Mesh'; if ((meshrights & 1) != 0) r += ', Edit Mesh';
if ((meshrights & 2) != 0) r += ', Manage Mesh Users'; if ((meshrights & 2) != 0) r += ', Manage Mesh Users';
if ((meshrights & 4) != 0) r += ', Manage Mesh Computers'; if ((meshrights & 4) != 0) r += ', Manage Mesh Computers';
if ((meshrights & 8) != 0) r += ', Remote Control'; if ((meshrights & 8) != 0) r += ', Remote Control';
if ((meshrights & 16) != 0) r += ', Agent Console'; if ((meshrights & 16) != 0) r += ', Agent Console';
if ((meshrights & 32) != 0) r += ', Server Files'; if ((meshrights & 32) != 0) r += ', Server Files';
if ((meshrights & 64) != 0) r += ', Wake Devices'; if ((meshrights & 64) != 0) r += ', Wake Devices';
if ((meshrights & 128) != 0) r += ', Edit Notes';
} }
r = r.substring(2); r = r.substring(2);
if (r == '') { r = 'No Rights'; } if (r == '') { r = 'No Rights'; }

View File

@ -80,6 +80,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
const MESHRIGHT_AGENTCONSOLE = 16; const MESHRIGHT_AGENTCONSOLE = 16;
const MESHRIGHT_SERVERFILES = 32; const MESHRIGHT_SERVERFILES = 32;
const MESHRIGHT_WAKEDEVICE = 64; const MESHRIGHT_WAKEDEVICE = 64;
const MESHRIGHT_SETNOTES = 128;
// Site rights // Site rights
const SITERIGHT_SERVERBACKUP = 1; const SITERIGHT_SERVERBACKUP = 1;