diff --git a/agents/MeshCmd-signed.exe b/agents/MeshCmd-signed.exe index 538b54ec..bdecb2ba 100644 Binary files a/agents/MeshCmd-signed.exe and b/agents/MeshCmd-signed.exe differ diff --git a/agents/MeshCmd64-signed.exe b/agents/MeshCmd64-signed.exe index e8f7bd64..6b9f4bd9 100644 Binary files a/agents/MeshCmd64-signed.exe and b/agents/MeshCmd64-signed.exe differ diff --git a/agents/MeshService-signed.exe b/agents/MeshService-signed.exe index 64d419b4..805988dc 100644 Binary files a/agents/MeshService-signed.exe and b/agents/MeshService-signed.exe differ diff --git a/agents/MeshService.exe b/agents/MeshService.exe index d5dd0ebd..7166c07d 100644 Binary files a/agents/MeshService.exe and b/agents/MeshService.exe differ diff --git a/agents/MeshService64-signed.exe b/agents/MeshService64-signed.exe index 902a58d9..06254cbc 100644 Binary files a/agents/MeshService64-signed.exe and b/agents/MeshService64-signed.exe differ diff --git a/agents/MeshService64.exe b/agents/MeshService64.exe index 7c670468..bec875e1 100644 Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ diff --git a/agents/meshcore.js b/agents/meshcore.js index bb8ed2b8..fe192c23 100644 --- a/agents/meshcore.js +++ b/agents/meshcore.js @@ -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.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 }); } } } diff --git a/db.js b/db.js index d5bba0fe..c5704534 100644 --- a/db.js +++ b/db.js @@ -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.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.RemoveNode = function (id) { obj.file.remove({ node: id }, { multi: true }); } obj.RemoveAll = function (func) { obj.file.remove({}, { 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); } diff --git a/letsEncrypt.js b/letsEncrypt.js index 403ca775..8eb8ef47 100644 --- a/letsEncrypt.js +++ b/letsEncrypt.js @@ -95,6 +95,9 @@ module.exports.CreateLetsEncrypt = function (parent) { agreeTos: true, rsaKeySize: rsaKeySize, 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) { obj.parent.performServerCertUpdate(); // Reset the server, TODO: Reset all peers }, function (err) { diff --git a/meshagent.js b/meshagent.js index 4c70d45e..1ba96572 100644 --- a/meshagent.js +++ b/meshagent.js @@ -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.agentInfo.capabilities & 0x20) { // This is a temporary agent, remote it // Delete this node including network interface information and events - obj.db.Remove(obj.dbNodeKey); - obj.db.Remove('if' + obj.dbNodeKey); + obj.db.Remove(obj.dbNodeKey); // Remove node with that id + 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 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); // Event the new node - 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 }) + if (obj.agentInfo.capabilities & 0x20) { + // 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 { // Device already exists, look if changes has occured device = nodes[0]; @@ -292,6 +299,9 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { if (change == 1) { 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 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(', '); } @@ -581,6 +591,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { // 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(', ') }; + if (obj.agentInfo.capabilities & 0x20) { event.nolog = 1; } // If this is a temporary device, don't log changes var device2 = obj.common.Clone(device); if (device2.intelamt && device2.intelamt.pass) delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this. event.node = device; @@ -616,6 +627,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { // 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(', ') }; + if (obj.agentInfo.capabilities & 0x20) { event.nolog = 1; } // If this is a temporary device, don't log changes var device2 = obj.common.Clone(device); if (device2.intelamt && device2.intelamt.pass) delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this. event.node = device; diff --git a/meshuser.js b/meshuser.js index 9acfd742..9758c3a4 100644 --- a/meshuser.js +++ b/meshuser.js @@ -713,9 +713,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { // Check if this user has rights to do this if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 4) == 0)) return; - // Delete this node including network interface information and events - obj.db.Remove(node._id); - obj.db.Remove('if' + node._id); + // Delete this node including network interface information, events and timeline + obj.db.Remove(node._id); // Remove node with that 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 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; } + 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; + } } }); diff --git a/package.json b/package.json index 539d0d99..60c41bd9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.1.6-d", + "version": "0.1.6-f", "keywords": [ "Remote Management", "Intel AMT", diff --git a/views/default.handlebars b/views/default.handlebars index c20bf057..3e771f76 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -1051,6 +1051,20 @@ } 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': { if (!message.event.nolog) { events.unshift(message.event); @@ -2761,9 +2775,10 @@ x += addDeviceAttribute('Connectivity', cstate.join(', ')); } - x += ''; + x += '
'; // Show action button, only show if we have permissions 4, 8, 64 - if ((meshrights & 76) != 0) { x += '
'; } + if ((meshrights & 76) != 0) { x += ''; } + x += ''; QH('p10html', x); // Show node last 7 days timeline @@ -2844,6 +2859,14 @@ go(panel); } + function deviceNotesFunction(readonly, nodeid) { + if (xxdialogMode) return; + setDialogMode(2, "Device Notes", 2, deviceNotesFunctionEx, '', nodeid); + meshserver.send({ action: 'getNotes', nodeid: nodeid }); + } + + function deviceNotesFunctionEx(buttons, tag) { meshserver.send({ action: 'setNotes', nodeid: tag, notes: encodeURIComponent(Q('d2devNotes').value) }); } + function deviceActionFunction() { var meshrights = meshes[currentNode.meshid].links['user/{{{domain}}}/' + userinfo.name.toLowerCase()].rights; var x = "Select an operation to perform on this device.

"; @@ -4485,6 +4508,7 @@ x += 'Mesh Agent Console
'; x += 'Server Files
'; x += 'Wake Devices
'; + x += 'Edit Notes
'; x += ''; setDialogMode(2, "Add User to Mesh", 3, p20showAddMeshUserDialogEx, x); p20validateAddMeshUserDialog(); @@ -4502,6 +4526,7 @@ QE('p20meshagentconsole', !Q('p20fulladmin').checked); QE('p20meshserverfiles', !Q('p20fulladmin').checked); QE('p20wakedevices', !Q('p20fulladmin').checked); + QE('p20editnotes', !Q('p20fulladmin').checked); } function p20showAddMeshUserDialogEx() { @@ -4514,6 +4539,7 @@ if (Q('p20meshagentconsole').checked == true) meshadmin += 16; if (Q('p20meshserverfiles').checked == true) meshadmin += 32; 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}); } @@ -4524,13 +4550,14 @@ var meshrights = currentMesh.links[userid].rights; var r = ''; if (meshrights == 0xFFFFFFFF) r = ', Full Administrator (all rights)'; else { - if ((meshrights & 1) != 0) r += ', Edit Mesh'; - if ((meshrights & 2) != 0) r += ', Manage Mesh Users'; - if ((meshrights & 4) != 0) r += ', Manage Mesh Computers'; - if ((meshrights & 8) != 0) r += ', Remote Control'; - if ((meshrights & 16) != 0) r += ', Agent Console'; - if ((meshrights & 32) != 0) r += ', Server Files'; - if ((meshrights & 64) != 0) r += ', Wake Devices'; + if ((meshrights & 1) != 0) r += ', Edit Mesh'; + if ((meshrights & 2) != 0) r += ', Manage Mesh Users'; + if ((meshrights & 4) != 0) r += ', Manage Mesh Computers'; + if ((meshrights & 8) != 0) r += ', Remote Control'; + if ((meshrights & 16) != 0) r += ', Agent Console'; + if ((meshrights & 32) != 0) r += ', Server Files'; + if ((meshrights & 64) != 0) r += ', Wake Devices'; + if ((meshrights & 128) != 0) r += ', Edit Notes'; } r = r.substring(2); if (r == '') { r = 'No Rights'; } diff --git a/webserver.js b/webserver.js index 812aeff6..66f314ae 100644 --- a/webserver.js +++ b/webserver.js @@ -80,6 +80,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate const MESHRIGHT_AGENTCONSOLE = 16; const MESHRIGHT_SERVERFILES = 32; const MESHRIGHT_WAKEDEVICE = 64; + const MESHRIGHT_SETNOTES = 128; // Site rights const SITERIGHT_SERVERBACKUP = 1;