Added device notes support

Ylian Saint-Hilaire committed Apr 12, 2018 at 18:14 UTC 4080abd21776d57d6ed5aa4ff22581fd68334123
14 files changed +114 -18
agents/MeshCmd-signed.exe
Binary files a/agents/MeshCmd-signed.exe and b/agents/MeshCmd-signed.exe differ
agents/MeshCmd64-signed.exe
Binary files a/agents/MeshCmd64-signed.exe and b/agents/MeshCmd64-signed.exe differ
agents/MeshService-signed.exe
Binary files a/agents/MeshService-signed.exe and b/agents/MeshService-signed.exe differ
agents/MeshService.exe
Binary files a/agents/MeshService.exe and b/agents/MeshService.exe differ
agents/MeshService64-signed.exe
Binary files a/agents/MeshService64-signed.exe and b/agents/MeshService64-signed.exe differ
agents/MeshService64.exe
Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ
agents/meshcore.js
+3 -1
@@ -817,7 +817,9 @@ function createMeshCore(agent) {
817 this.websocket.rtcchannel.on('end', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed');*/ });
818 this.websocket.write("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc0\"}"); // Indicate we are ready for WebRTC switch-over.
819 });
820 - ws.write({ type: 'answer', ctrlChannel: '102938', sdp: ws.webrtc.setOffer(obj.sdp) });
820 + var sdp = null;
821 + try { sdp = ws.webrtc.setOffer(obj.sdp); } catch (ex) { }
822 + if (sdp != null) { ws.write({ type: 'answer', ctrlChannel: '102938', sdp: sdp }); }
823 }
824 }
825
db.js
+1
@@ -92,6 +92,7 @@ module.exports.CreateDB = function (args, datapath) {
92 obj.GetUserWithEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email }, { type: 0 }, func); }
93 obj.GetUserWithVerifiedEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email, emailVerified: true }, { type: 0 }, func); }
94 obj.Remove = function (id) { obj.file.remove({ _id: id }); }
95 + obj.RemoveNode = function (id) { obj.file.remove({ node: id }, { multi: true }); }
96 obj.RemoveAll = function (func) { obj.file.remove({}, { multi: true }, func); }
97 obj.RemoveAllOfType = function (type, func) { obj.file.remove({ type: type }, { multi: true }, func); }
98 obj.InsertMany = function (data, func) { obj.file.insert(data, func); }
letsEncrypt.js
+3
@@ -95,6 +95,9 @@ module.exports.CreateLetsEncrypt = function (parent) {
95 agreeTos: true,
96 rsaKeySize: rsaKeySize,
97 challengeType: 'http-01'
98 + //renewWithin: 15 * 24 * 60 * 60 * 1000 // 15 days
99 + //renewWithin: 81 * 24 * 60 * 60 * 1000, // 81 days
100 + //renewBy: 80 * 24 * 60 * 60 * 1000 // 80 days
101 }).then(function (xresults) {
102 obj.parent.performServerCertUpdate(); // Reset the server, TODO: Reset all peers
103 }, function (err) {
meshagent.js
+16 -4
@@ -50,8 +50,10 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
50 if (obj.agentUpdate != null) { obj.fs.close(obj.agentUpdate.fd); obj.agentUpdate = null; }
51 if (obj.agentInfo.capabilities & 0x20) { // This is a temporary agent, remote it
52 // Delete this node including network interface information and events
53 - obj.db.Remove(obj.dbNodeKey);
54 - obj.db.Remove('if' + obj.dbNodeKey);
53 + obj.db.Remove(obj.dbNodeKey); // Remove node with that id
54 + obj.db.Remove('if' + obj.dbNodeKey); // Remove interface information
55 + obj.db.Remove('nt' + obj.dbNodeKey); // Remove notes
56 + obj.db.RemoveNode(obj.dbNodeKey); // Remove all entries with node:id
57
58 // Event node deletion
59 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) {
278 obj.db.Set(device);
279
280 // Event the new node
279 - var change = 'Added device ' + obj.agentInfo.computerName + ' to mesh ' + mesh.name;
280 - obj.parent.parent.DispatchEvent(['*', obj.dbMeshKey], obj, { etype: 'node', action: 'addnode', node: device, msg: change, domain: domain.id })
281 + if (obj.agentInfo.capabilities & 0x20) {
282 + // This is a temporary agent, don't log.
283 + obj.parent.parent.DispatchEvent(['*', obj.dbMeshKey], obj, { etype: 'node', action: 'addnode', node: device, domain: domain.id, nolog: 1 })
284 + } else {
285 + var change = 'Added device ' + obj.agentInfo.computerName + ' to mesh ' + mesh.name;
286 + obj.parent.parent.DispatchEvent(['*', obj.dbMeshKey], obj, { etype: 'node', action: 'addnode', node: device, msg: change, domain: domain.id })
287 + }
288 } else {
289 // Device already exists, look if changes has occured
290 device = nodes[0];
@@ -292,6 +299,9 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
299 if (change == 1) {
300 obj.db.Set(device);
301
302 + // If this is a temporary device, don't log changes
303 + if (obj.agentInfo.capabilities & 0x20) { log = 0; }
304 +
305 // Event the node change
306 var event = { etype: 'node', action: 'changenode', nodeid: obj.dbNodeKey, domain: domain.id };
307 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) {
591
592 // Event the node change
593 var event = { etype: 'node', action: 'changenode', nodeid: obj.dbNodeKey, domain: domain.id, msg: 'Changed device ' + device.name + ' from mesh ' + mesh.name + ': ' + changes.join(', ') };
594 + if (obj.agentInfo.capabilities & 0x20) { event.nolog = 1; } // If this is a temporary device, don't log changes
595 var device2 = obj.common.Clone(device);
596 if (device2.intelamt && device2.intelamt.pass) delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this.
597 event.node = device;
@@ -616,6 +627,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
627
628 // Event the node change
629 var event = { etype: 'node', action: 'changenode', nodeid: obj.dbNodeKey, domain: domain.id, msg: 'Changed device ' + device.name + ' from mesh ' + mesh.name + ': ' + changes.join(', ') };
630 + if (obj.agentInfo.capabilities & 0x20) { event.nolog = 1; } // If this is a temporary device, don't log changes
631 var device2 = obj.common.Clone(device);
632 if (device2.intelamt && device2.intelamt.pass) delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this.
633 event.node = device;
meshuser.js
+53 -3
@@ -713,9 +713,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
713 // Check if this user has rights to do this
714 if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 4) == 0)) return;
715
716 - // Delete this node including network interface information and events
717 - obj.db.Remove(node._id);
718 - obj.db.Remove('if' + node._id);
716 + // Delete this node including network interface information, events and timeline
717 + obj.db.Remove(node._id); // Remove node with that id
718 + obj.db.Remove('if' + node._id); // Remove interface information
719 + obj.db.Remove('nt' + node._id); // Remove notes
720 + obj.db.RemoveNode(node._id); // Remove all entries with node:id
721
722 // Event node deletion
723 var change = 'Removed device ' + node.name + ' from mesh ' + mesh.name;
@@ -994,6 +996,54 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
996 }
997 break;
998 }
999 + case 'setNotes':
1000 + {
1001 + // Argument validation
1002 + if (obj.common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
1003 + if ((command.nodeid.split('/').length != 3) || (command.nodeid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
1004 +
1005 + // Check if this user has rights on this nodeid to set notes
1006 + 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 (???)
1007 + if (nodes.length == 1) {
1008 + var meshlinks = user.links[nodes[0].meshid];
1009 + if ((meshlinks) && (meshlinks.rights) && (meshlinks.rights & obj.parent.MESHRIGHT_SETNOTES != 0)) {
1010 + // Set the node's notes
1011 + if (obj.common.validateString(command.notes, 1) == false) {
1012 + obj.db.Remove('nt' + command.nodeid); // Delete the note for this node
1013 + } else {
1014 + obj.db.Set({ _id: 'nt' + command.nodeid, value: command.notes }); // Set the note for this node
1015 + }
1016 + }
1017 + }
1018 + });
1019 + break;
1020 + }
1021 + case 'getNotes':
1022 + {
1023 + // Argument validation
1024 + if (obj.common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
1025 + if ((command.nodeid.split('/').length != 3) || (command.nodeid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
1026 +
1027 + // Get the device
1028 + obj.db.Get(command.nodeid, function (err, nodes) {
1029 + if (nodes.length != 1) { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: command.nodeid, netif: null })); return; }
1030 + var node = nodes[0];
1031 +
1032 + // Get the mesh for this device
1033 + var mesh = obj.parent.meshes[node.meshid];
1034 + if (mesh) {
1035 + // Check if this user has rights to do this
1036 + if (mesh.links[user._id] == null || (mesh.links[user._id].rights == 0)) { return; }
1037 +
1038 + // Get the notes about this node
1039 + obj.db.Get('nt' + command.nodeid, function (err, notes) {
1040 + if (notes.length != 1) { ws.send(JSON.stringify({ action: 'getNotes', nodeid: command.nodeid, notes: null })); return; }
1041 + ws.send(JSON.stringify({ action: 'getNotes', nodeid: command.nodeid, notes: notes[0].value }));
1042 + });
1043 + }
1044 + });
1045 + break;
1046 + }
1047 }
1048 });
1049
package.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "name": "meshcentral",
3 - "version": "0.1.6-d",
3 + "version": "0.1.6-f",
4 "keywords": [
5 "Remote Management",
6 "Intel AMT",
views/default.handlebars
+36 -9
@@ -1051,6 +1051,20 @@
1051 }
1052 break;
1053 }
1054 + case 'getNotes':{
1055 + var n = Q('d2devNotes');
1056 + if (n && (message.nodeid == n.attributes['nodeid'].value)) {
1057 + if (message.notes) { QH('d2devNotes', decodeURIComponent(message.notes)); } else { QH('d2devNotes', ''); }
1058 + var ro = n.attributes['ro'].value == 'true';
1059 + if (ro == false) { // If we have permissions, set read/write on this note.
1060 + n.removeAttribute('readonly');
1061 + QE('idx_dlgOkButton', true);
1062 + QV('idx_dlgOkButton', true);
1063 + focusTextBox('d2devNotes');
1064 + }
1065 + }
1066 + break;
1067 + }
1068 case 'event': {
1069 if (!message.event.nolog) {
1070 events.unshift(message.event);
@@ -2761,9 +2775,10 @@
2775 x += addDeviceAttribute('Connectivity', cstate.join(', '));
2776 }
2777
2764 - x += '</table>';
2778 + x += '</table><br />';
2779 // Show action button, only show if we have permissions 4, 8, 64
2766 - if ((meshrights & 76) != 0) { x += '<br /><input type=button value=Actions title="Perform power actions on the device" onclick=deviceActionFunction() />'; }
2780 + if ((meshrights & 76) != 0) { x += '<input type=button value=Actions title="Perform power actions on the device" onclick=deviceActionFunction() />'; }
2781 + x += '<input type=button value=Notes title="View notes about this device" onclick=deviceNotesFunction(' + ((meshrights & 128) == 0) + ',"' + node._id + '") />';
2782 QH('p10html', x);
2783
2784 // Show node last 7 days timeline
@@ -2844,6 +2859,14 @@
2859 go(panel);
2860 }
2861
2862 + function deviceNotesFunction(readonly, nodeid) {
2863 + if (xxdialogMode) return;
2864 + 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);
2865 + meshserver.send({ action: 'getNotes', nodeid: nodeid });
2866 + }
2867 +
2868 + function deviceNotesFunctionEx(buttons, tag) { meshserver.send({ action: 'setNotes', nodeid: tag, notes: encodeURIComponent(Q('d2devNotes').value) }); }
2869 +
2870 function deviceActionFunction() {
2871 var meshrights = meshes[currentNode.meshid].links['user/{{{domain}}}/' + userinfo.name.toLowerCase()].rights;
2872 var x = "Select an operation to perform on this device.<br /><br />";
@@ -4485,6 +4508,7 @@
4508 x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20meshagentconsole>Mesh Agent Console<br>';
4509 x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20meshserverfiles>Server Files<br>';
4510 x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20wakedevices>Wake Devices<br>';
4511 + x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20editnotes>Edit Notes<br>';
4512 x += '</div>';
4513 setDialogMode(2, "Add User to Mesh", 3, p20showAddMeshUserDialogEx, x);
4514 p20validateAddMeshUserDialog();
@@ -4502,6 +4526,7 @@
4526 QE('p20meshagentconsole', !Q('p20fulladmin').checked);
4527 QE('p20meshserverfiles', !Q('p20fulladmin').checked);
4528 QE('p20wakedevices', !Q('p20fulladmin').checked);
4529 + QE('p20editnotes', !Q('p20fulladmin').checked);
4530 }
4531
4532 function p20showAddMeshUserDialogEx() {
@@ -4514,6 +4539,7 @@
4539 if (Q('p20meshagentconsole').checked == true) meshadmin += 16;
4540 if (Q('p20meshserverfiles').checked == true) meshadmin += 32;
4541 if (Q('p20wakedevices').checked == true) meshadmin += 64;
4542 + if (Q('p20editnotes').checked == true) meshadmin += 128;
4543 }
4544 meshserver.send({ action: 'addmeshuser', meshid: currentMesh._id, meshname: currentMesh.name, username: Q('dp20username').value , meshadmin: meshadmin});
4545 }
@@ -4524,13 +4550,14 @@
4550 var meshrights = currentMesh.links[userid].rights;
4551 var r = '';
4552 if (meshrights == 0xFFFFFFFF) r = ', Full Administrator (all rights)'; else {
4527 - if ((meshrights & 1) != 0) r += ', Edit Mesh';
4528 - if ((meshrights & 2) != 0) r += ', Manage Mesh Users';
4529 - if ((meshrights & 4) != 0) r += ', Manage Mesh Computers';
4530 - if ((meshrights & 8) != 0) r += ', Remote Control';
4531 - if ((meshrights & 16) != 0) r += ', Agent Console';
4532 - if ((meshrights & 32) != 0) r += ', Server Files';
4533 - if ((meshrights & 64) != 0) r += ', Wake Devices';
4553 + if ((meshrights & 1) != 0) r += ', Edit Mesh';
4554 + if ((meshrights & 2) != 0) r += ', Manage Mesh Users';
4555 + if ((meshrights & 4) != 0) r += ', Manage Mesh Computers';
4556 + if ((meshrights & 8) != 0) r += ', Remote Control';
4557 + if ((meshrights & 16) != 0) r += ', Agent Console';
4558 + if ((meshrights & 32) != 0) r += ', Server Files';
4559 + if ((meshrights & 64) != 0) r += ', Wake Devices';
4560 + if ((meshrights & 128) != 0) r += ', Edit Notes';
4561 }
4562 r = r.substring(2);
4563 if (r == '') { r = 'No Rights'; }
webserver.js
+1
@@ -80,6 +80,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
80 const MESHRIGHT_AGENTCONSOLE = 16;
81 const MESHRIGHT_SERVERFILES = 32;
82 const MESHRIGHT_WAKEDEVICE = 64;
83 + const MESHRIGHT_SETNOTES = 128;
84
85 // Site rights
86 const SITERIGHT_SERVERBACKUP = 1;