diff --git a/agents/meshcore.js b/agents/meshcore.js index e4943995..68bb7435 100644 --- a/agents/meshcore.js +++ b/agents/meshcore.js @@ -171,8 +171,9 @@ function createMeshCore(agent) } break; default: - this.parent._daipc = null; this.end(); return; - break; + this.parent._daipc = null; + this.end(); + return; } } catch(xe) @@ -306,6 +307,17 @@ function createMeshCore(agent) var nextTunnelIndex = 1; var amtPolicy = null; + // Add to the server event log + function MeshServerLog(msg, state) { + if (typeof msg == 'string') { msg = { 'action': 'log', 'msg': msg }; } else { msg.action = 'log'; } + if (state) { + if (state.userid) { msg.userid = state.userid; } + if (state.username) { msg.username = state.username; } + if (state.sessionid) { msg.sessionid = state.sessionid; } + } + mesh.SendCommand(msg); + } + // If we are running in Duktape, agent will be null if (agent == null) { // Running in native agent, Import libraries @@ -615,6 +627,7 @@ function createMeshCore(agent) switch (data.type) { case 'console': { // Process a console command if (data.value && data.sessionid) { + MeshServerLog('Processing console command: ' + data.value, data); var args = splitArgs(data.value); processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid); } @@ -636,6 +649,8 @@ function createMeshCore(agent) tunnel.rights = data.rights; tunnel.consent = data.consent; tunnel.username = data.username; + tunnel.userid = data.userid; + tunnel.remoteaddr = data.remoteaddr; tunnel.state = 0; tunnel.url = xurl; tunnel.protocol = 0; @@ -664,12 +679,14 @@ function createMeshCore(agent) case 'pskill': { // Kill a process if (data.value) { + MeshServerLog('Killing process ' + data.value, data); try { process.kill(data.value); } catch (e) { sendConsoleText("pskill: " + JSON.stringify(e)); } } break; } case 'openUrl': { // Open a local web browser and return success/fail + MeshServerLog('Opening: ' + data.url, data); sendConsoleText('OpenURL: ' + data.url); if (data.url) { mesh.SendCommand({ "action": "msg", "type":"openUrl", "url": data.url, "sessionid": data.sessionid, "success": (openUserDesktopUrl(data.url) != null) }); } break; @@ -677,31 +694,33 @@ function createMeshCore(agent) case 'getclip': { // Send the load clipboard back to the user //sendConsoleText('getClip: ' + JSON.stringify(data)); - if (require('MeshAgent').isService) - { - require('clipboard').dispatchRead().then(function (str) { mesh.SendCommand({ "action": "msg", "type": "getclip", "sessionid": data.sessionid, "data": str }); }); - } - else - { - require("clipboard").read().then(function (str) { mesh.SendCommand({ "action": "msg", "type": "getclip", "sessionid": data.sessionid, "data": str }); }); + if (require('MeshAgent').isService) { + require('clipboard').dispatchRead().then(function (str) { + if (str) { + MeshServerLog('Getting clipboard content, ' + str.length + ' byte(s)', data); + mesh.SendCommand({ "action": "msg", "type": "getclip", "sessionid": data.sessionid, "data": str }); + } + }); + } else { + require("clipboard").read().then(function (str) { + if (str) { + MeshServerLog('Getting clipboard content, ' + str.length + ' byte(s)', data); + mesh.SendCommand({ "action": "msg", "type": "getclip", "sessionid": data.sessionid, "data": str }); + } + }); } break; } case 'setclip': { // Set the load clipboard to a user value //sendConsoleText('setClip: ' + JSON.stringify(data)); - if (typeof data.data == 'string') - { - if (require('MeshAgent').isService) - { - require('clipboard').dispatchWrite(data.data); + if (typeof data.data == 'string') { + MeshServerLog('Setting clipboard content, ' + data.data.length + ' byte(s)', data); + if (typeof data.data == 'string') { + if (require('MeshAgent').isService) { require('clipboard').dispatchWrite(data.data); } else { require("clipboard")(data.data); } // Set the clipboard + mesh.SendCommand({ "action": "msg", "type": "setclip", "sessionid": data.sessionid, "success": true }); } - else - { - require("clipboard")(data.data); // Set the clipboard - } - mesh.SendCommand({ "action": "msg", "type": "setclip", "sessionid": data.sessionid, "success": true }); - } + } break; } default: @@ -711,7 +730,10 @@ function createMeshCore(agent) break; } case 'acmactivate': { - if (amt != null) { amt.setAcmResponse(data); } + if (amt != null) { + MeshServerLog('Attempting Intel AMT ACM mode activation', data); + amt.setAcmResponse(data); + } break; } case 'wakeonlan': { @@ -726,6 +748,7 @@ function createMeshCore(agent) var forced = 0; if (data.forced == 1) { forced = 1; } data.actiontype = parseInt(data.actiontype); + MeshServerLog('Performing power action=' + data.actiontype + ', forced=' + forced, data); sendConsoleText('Performing power action=' + data.actiontype + ', forced=' + forced + '.'); var r = mesh.ExecPowerState(data.actiontype, forced); sendConsoleText('ExecPowerState returned code: ' + r); @@ -740,13 +763,15 @@ function createMeshCore(agent) case 'toast': { // Display a toast message if (data.title && data.msg) { + MeshServerLog('Displaying toast message, title=' + data.title + ', message=' + data.msg, data); try { require('toaster').Toast(data.title, data.msg); } catch (ex) { } } break; } case 'openUrl': { // Open a local web browser and return success/fail - sendConsoleText('OpenURL: ' + data.url); + //sendConsoleText('OpenURL: ' + data.url); + MeshServerLog('Opening: ' + data.url, data); if (data.url) { mesh.SendCommand({ "action": "openUrl", "url": data.url, "sessionid": data.sessionid, "success": (openUserDesktopUrl(data.url) != null) }); } break; } @@ -1003,44 +1028,41 @@ function createMeshCore(agent) // Perform notification if needed. Toast messages may not be supported on all platforms. if (this.httprequest.consent && (this.httprequest.consent & 16)) { // User Consent Prompt is required - // Send a console message back using the console channel, "\n" is supported. this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'Waiting for user to grant access...' })); - var pr = require('message-box').create('MeshCentral', this.httprequest.username + ' requesting Terminal Access. Grant access?', 10); pr.ws = this; this.pause(); pr.then( function () { - // Success! + // Success + MeshServerLog('Starting remote terminal after local user accepted (' + this.httprequest.remoteaddr + ')', this.httprequest); this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null })); if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 2)) { // User Notifications is required try { require('toaster').Toast('MeshCentral', this.ws.httprequest.username + ' started a remote terminal session.'); } catch (ex) { } } - this.ws.resume(); }, function (e) { - // User Consent Denied/Failed! + // User Consent Denied/Failed + MeshServerLog('Failed to start remote terminal after local user rejected (' + this.httprequest.remoteaddr + ')', this.httprequest); this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() })); this.ws.end(); }); - } - else { + } else { // User Consent Prompt is not required if (this.httprequest.consent && (this.httprequest.consent & 2)) { // User Notifications is required + MeshServerLog('Started remote terminal with toast notification (' + this.httprequest.remoteaddr + ')', this.httprequest); try { require('toaster').Toast('MeshCentral', this.httprequest.username + ' started a remote terminal session.'); } catch (ex) { } + } else { + MeshServerLog('Started remote terminal without notification (' + this.httprequest.remoteaddr + ')', this.httprequest); } this.resume(); } - - - - this.removeAllListeners('data'); this.on('data', onTunnelControlData); //this.write('MeshCore Terminal Hello'); @@ -1096,10 +1118,8 @@ function createMeshCore(agent) if (this.httprequest.consent && (this.httprequest.consent & 8)) { // User Consent Prompt is required - // Send a console message back using the console channel, "\n" is supported. this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'Waiting for user to grant access...' })); - var pr = require('message-box').create('MeshCentral', this.httprequest.username + ' requesting KVM Access. Grant access?', 10); pr.ws = this; this.pause(); @@ -1107,37 +1127,32 @@ function createMeshCore(agent) pr.then( function () { - // Success! + // Success + MeshServerLog('Starting remote desktop after local user accepted (' + this.httprequest.remoteaddr + ')', this.httprequest); this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null })); - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 1)) - { + if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 1)) { // User Notifications is required try { require('toaster').Toast('MeshCentral', this.ws.httprequest.username + ' started a remote desktop session.'); } catch (ex) { } } - this.ws.httprequest.desktop.kvm.pipe(this.ws, { dataTypeSkip: 1 }); this.ws.resume(); }, function (e) { - // User Consent Denied/Failed! + // User Consent Denied/Failed + MeshServerLog('Failed to start remote desktop after local user rejected (' + this.httprequest.remoteaddr + ')', this.httprequest); this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() })); - - //var err = 'User consent: ' + e.toString(); - //var b = Buffer.alloc(5 + err.length); - //b.writeUInt16BE(MNG_ERROR, 0); - //b.writeUInt16BE(err.length + 4, 2); - //Buffer.from(err).copy(b, 4); - //this.ws.end(b); }); } else { // User Consent Prompt is not required - if (this.httprequest.consent && (this.httprequest.consent & 1)) - { + if (this.httprequest.consent && (this.httprequest.consent & 1)) { // User Notifications is required + MeshServerLog('Started remote desktop with toast notification (' + this.httprequest.remoteaddr + ')', this.httprequest); try { require('toaster').Toast('MeshCentral', this.httprequest.username + ' started a remote desktop session.'); } catch (ex) { } + } else { + MeshServerLog('Started remote desktop without notification (' + this.httprequest.remoteaddr + ')', this.httprequest); } this.httprequest.desktop.kvm.pipe(this, { dataTypeSkip: 1 }); } @@ -1161,35 +1176,36 @@ function createMeshCore(agent) if (this.httprequest.consent && (this.httprequest.consent & 32)) { // User Consent Prompt is required - // Send a console message back using the console channel, "\n" is supported. this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'Waiting for user to grant access...' })); - var pr = require('message-box').create('MeshCentral', this.httprequest.username + ' requesting remote file access. Grant access?', 10); pr.ws = this; this.pause(); pr.then( function () { - // Success! + // Success + MeshServerLog('Starting remote files after local user accepted (' + this.httprequest.remoteaddr + ')', this.httprequest); this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null })); - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 4)) - { + if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 4)) { // User Notifications is required try { require('toaster').Toast('MeshCentral', this.ws.httprequest.username + ' started a remote file session.'); } catch (ex) { } } this.ws.resume(); }, function (e) { - // User Consent Denied/Failed! + // User Consent Denied/Failed + MeshServerLog('Failed to start remote files after local user rejected (' + this.httprequest.remoteaddr + ')', this.httprequest); this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() })); }); - } - else { + } else { // User Consent Prompt is not required if (this.httprequest.consent && (this.httprequest.consent & 4)) { // User Notifications is required + MeshServerLog('Started remote files with toast notification (' + this.httprequest.remoteaddr + ')', this.httprequest); try { require('toaster').Toast('MeshCentral', this.httprequest.username + ' started a remote file session.'); } catch (ex) { } + } else { + MeshServerLog('Started remote files without notification (' + this.httprequest.remoteaddr + ')', this.httprequest); } this.resume(); } @@ -1252,12 +1268,15 @@ function createMeshCore(agent) case 'mkdir': { // Create a new empty folder fs.mkdirSync(cmd.path); + MeshServerLog('Create folder: \"' + cmd.path + '\"', this.httprequest); break; } case 'rm': { // Delete, possibly recursive delete for (var i in cmd.delfiles) { - try { deleteFolderRecursive(obj.path.join(cmd.path, cmd.delfiles[i]), cmd.rec); } catch (e) { } + var p = obj.path.join(cmd.path, cmd.delfiles[i]), delcount = 0; + try { delcount = deleteFolderRecursive(p, cmd.rec); } catch (e) { } + MeshServerLog((cmd.rec ? 'Delete recursive: \"' : 'Delete: \"') + p + '\", ' + delcount + ' element(s) removed', this.httprequest); } break; } @@ -1265,6 +1284,7 @@ function createMeshCore(agent) // Rename a file or folder var oldfullpath = obj.path.join(cmd.path, cmd.oldname); var newfullpath = obj.path.join(cmd.path, cmd.newname); + MeshServerLog('Rename: \"' + oldfullpath + '\" to \"' + cmd.newname + '\"', this.httprequest); try { fs.renameSync(oldfullpath, newfullpath); } catch (e) { console.log(e); } break; } @@ -1272,6 +1292,7 @@ function createMeshCore(agent) // Download a file var sendNextBlock = 0; if (cmd.sub == 'start') { // Setup the download + MeshServerLog('Download: \"' + cmd.path + '\"', this.httprequest); if (this.filedownload != null) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; } this.filedownload = { id: cmd.id, path: cmd.path, ptr: 0 } try { this.filedownload.f = fs.openSync(this.filedownload.path, 'rbN'); } catch (e) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; } @@ -1317,6 +1338,7 @@ function createMeshCore(agent) if (this.httprequest.uploadFile != undefined) { fs.closeSync(this.httprequest.uploadFile); this.httprequest.uploadFile = undefined; } if (cmd.path == undefined) break; var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path; + MeshServerLog('Upload: \"' + filepath + '\"', this.httprequest); try { this.httprequest.uploadFile = fs.openSync(filepath, 'wbN'); } catch (e) { this.write(new Buffer(JSON.stringify({ action: 'uploaderror', reqid: cmd.reqid }))); break; } this.httprequest.uploadFileid = cmd.reqid; if (this.httprequest.uploadFile) { this.write(new Buffer(JSON.stringify({ action: 'uploadstart', reqid: this.httprequest.uploadFileid }))); } @@ -1326,6 +1348,7 @@ function createMeshCore(agent) // Copy a bunch of files from scpath to dspath for (var i in cmd.names) { var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]); + MeshServerLog('Copy: \"' + sc + '\" to \"' + ds + '\"', this.httprequest); if (sc != ds) { try { fs.copyFileSync(sc, ds); } catch (e) { } } } break; @@ -1334,6 +1357,7 @@ function createMeshCore(agent) // Move a bunch of files from scpath to dspath for (var i in cmd.names) { var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]); + MeshServerLog('Move: \"' + sc + '\" to \"' + ds + '\"', this.httprequest); if (sc != ds) { try { fs.copyFileSync(sc, ds); fs.unlinkSync(sc); } catch (e) { } } } break; @@ -1349,19 +1373,23 @@ function createMeshCore(agent) // Delete a directory with a files and directories within it function deleteFolderRecursive(path, rec) { + var count = 0; if (fs.existsSync(path)) { if (rec == true) { fs.readdirSync(obj.path.join(path, '*')).forEach(function (file, index) { var curPath = obj.path.join(path, file); if (fs.statSync(curPath).isDirectory()) { // recurse - deleteFolderRecursive(curPath, true); + count += deleteFolderRecursive(curPath, true); } else { // delete file fs.unlinkSync(curPath); + count++; } }); } fs.unlinkSync(path); + count++; } + return count; }; // Called when receiving control data on WebRTC @@ -1391,6 +1419,7 @@ function createMeshCore(agent) // Lock the current user out of the desktop try { if (process.platform == 'win32') { + MeshServerLog('Locking remote user out of desktop', ws.httprequest); var child = require('child_process'); child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 }); } @@ -1557,6 +1586,9 @@ function createMeshCore(agent) } break; */ + case 'log': + if (args['_'].length != 1) { response = 'Proper usage: log "sample text"'; } else { MeshServerLog(args['_'][0]); response = 'ok'; } + break; case 'getclip': if (require('MeshAgent').isService) { require('clipboard').dispatchRead().then(function (str) { sendConsoleText(str, sessionid); }); diff --git a/agents/meshcore.min.js b/agents/meshcore.min.js index e4943995..68bb7435 100644 --- a/agents/meshcore.min.js +++ b/agents/meshcore.min.js @@ -171,8 +171,9 @@ function createMeshCore(agent) } break; default: - this.parent._daipc = null; this.end(); return; - break; + this.parent._daipc = null; + this.end(); + return; } } catch(xe) @@ -306,6 +307,17 @@ function createMeshCore(agent) var nextTunnelIndex = 1; var amtPolicy = null; + // Add to the server event log + function MeshServerLog(msg, state) { + if (typeof msg == 'string') { msg = { 'action': 'log', 'msg': msg }; } else { msg.action = 'log'; } + if (state) { + if (state.userid) { msg.userid = state.userid; } + if (state.username) { msg.username = state.username; } + if (state.sessionid) { msg.sessionid = state.sessionid; } + } + mesh.SendCommand(msg); + } + // If we are running in Duktape, agent will be null if (agent == null) { // Running in native agent, Import libraries @@ -615,6 +627,7 @@ function createMeshCore(agent) switch (data.type) { case 'console': { // Process a console command if (data.value && data.sessionid) { + MeshServerLog('Processing console command: ' + data.value, data); var args = splitArgs(data.value); processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid); } @@ -636,6 +649,8 @@ function createMeshCore(agent) tunnel.rights = data.rights; tunnel.consent = data.consent; tunnel.username = data.username; + tunnel.userid = data.userid; + tunnel.remoteaddr = data.remoteaddr; tunnel.state = 0; tunnel.url = xurl; tunnel.protocol = 0; @@ -664,12 +679,14 @@ function createMeshCore(agent) case 'pskill': { // Kill a process if (data.value) { + MeshServerLog('Killing process ' + data.value, data); try { process.kill(data.value); } catch (e) { sendConsoleText("pskill: " + JSON.stringify(e)); } } break; } case 'openUrl': { // Open a local web browser and return success/fail + MeshServerLog('Opening: ' + data.url, data); sendConsoleText('OpenURL: ' + data.url); if (data.url) { mesh.SendCommand({ "action": "msg", "type":"openUrl", "url": data.url, "sessionid": data.sessionid, "success": (openUserDesktopUrl(data.url) != null) }); } break; @@ -677,31 +694,33 @@ function createMeshCore(agent) case 'getclip': { // Send the load clipboard back to the user //sendConsoleText('getClip: ' + JSON.stringify(data)); - if (require('MeshAgent').isService) - { - require('clipboard').dispatchRead().then(function (str) { mesh.SendCommand({ "action": "msg", "type": "getclip", "sessionid": data.sessionid, "data": str }); }); - } - else - { - require("clipboard").read().then(function (str) { mesh.SendCommand({ "action": "msg", "type": "getclip", "sessionid": data.sessionid, "data": str }); }); + if (require('MeshAgent').isService) { + require('clipboard').dispatchRead().then(function (str) { + if (str) { + MeshServerLog('Getting clipboard content, ' + str.length + ' byte(s)', data); + mesh.SendCommand({ "action": "msg", "type": "getclip", "sessionid": data.sessionid, "data": str }); + } + }); + } else { + require("clipboard").read().then(function (str) { + if (str) { + MeshServerLog('Getting clipboard content, ' + str.length + ' byte(s)', data); + mesh.SendCommand({ "action": "msg", "type": "getclip", "sessionid": data.sessionid, "data": str }); + } + }); } break; } case 'setclip': { // Set the load clipboard to a user value //sendConsoleText('setClip: ' + JSON.stringify(data)); - if (typeof data.data == 'string') - { - if (require('MeshAgent').isService) - { - require('clipboard').dispatchWrite(data.data); + if (typeof data.data == 'string') { + MeshServerLog('Setting clipboard content, ' + data.data.length + ' byte(s)', data); + if (typeof data.data == 'string') { + if (require('MeshAgent').isService) { require('clipboard').dispatchWrite(data.data); } else { require("clipboard")(data.data); } // Set the clipboard + mesh.SendCommand({ "action": "msg", "type": "setclip", "sessionid": data.sessionid, "success": true }); } - else - { - require("clipboard")(data.data); // Set the clipboard - } - mesh.SendCommand({ "action": "msg", "type": "setclip", "sessionid": data.sessionid, "success": true }); - } + } break; } default: @@ -711,7 +730,10 @@ function createMeshCore(agent) break; } case 'acmactivate': { - if (amt != null) { amt.setAcmResponse(data); } + if (amt != null) { + MeshServerLog('Attempting Intel AMT ACM mode activation', data); + amt.setAcmResponse(data); + } break; } case 'wakeonlan': { @@ -726,6 +748,7 @@ function createMeshCore(agent) var forced = 0; if (data.forced == 1) { forced = 1; } data.actiontype = parseInt(data.actiontype); + MeshServerLog('Performing power action=' + data.actiontype + ', forced=' + forced, data); sendConsoleText('Performing power action=' + data.actiontype + ', forced=' + forced + '.'); var r = mesh.ExecPowerState(data.actiontype, forced); sendConsoleText('ExecPowerState returned code: ' + r); @@ -740,13 +763,15 @@ function createMeshCore(agent) case 'toast': { // Display a toast message if (data.title && data.msg) { + MeshServerLog('Displaying toast message, title=' + data.title + ', message=' + data.msg, data); try { require('toaster').Toast(data.title, data.msg); } catch (ex) { } } break; } case 'openUrl': { // Open a local web browser and return success/fail - sendConsoleText('OpenURL: ' + data.url); + //sendConsoleText('OpenURL: ' + data.url); + MeshServerLog('Opening: ' + data.url, data); if (data.url) { mesh.SendCommand({ "action": "openUrl", "url": data.url, "sessionid": data.sessionid, "success": (openUserDesktopUrl(data.url) != null) }); } break; } @@ -1003,44 +1028,41 @@ function createMeshCore(agent) // Perform notification if needed. Toast messages may not be supported on all platforms. if (this.httprequest.consent && (this.httprequest.consent & 16)) { // User Consent Prompt is required - // Send a console message back using the console channel, "\n" is supported. this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'Waiting for user to grant access...' })); - var pr = require('message-box').create('MeshCentral', this.httprequest.username + ' requesting Terminal Access. Grant access?', 10); pr.ws = this; this.pause(); pr.then( function () { - // Success! + // Success + MeshServerLog('Starting remote terminal after local user accepted (' + this.httprequest.remoteaddr + ')', this.httprequest); this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null })); if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 2)) { // User Notifications is required try { require('toaster').Toast('MeshCentral', this.ws.httprequest.username + ' started a remote terminal session.'); } catch (ex) { } } - this.ws.resume(); }, function (e) { - // User Consent Denied/Failed! + // User Consent Denied/Failed + MeshServerLog('Failed to start remote terminal after local user rejected (' + this.httprequest.remoteaddr + ')', this.httprequest); this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() })); this.ws.end(); }); - } - else { + } else { // User Consent Prompt is not required if (this.httprequest.consent && (this.httprequest.consent & 2)) { // User Notifications is required + MeshServerLog('Started remote terminal with toast notification (' + this.httprequest.remoteaddr + ')', this.httprequest); try { require('toaster').Toast('MeshCentral', this.httprequest.username + ' started a remote terminal session.'); } catch (ex) { } + } else { + MeshServerLog('Started remote terminal without notification (' + this.httprequest.remoteaddr + ')', this.httprequest); } this.resume(); } - - - - this.removeAllListeners('data'); this.on('data', onTunnelControlData); //this.write('MeshCore Terminal Hello'); @@ -1096,10 +1118,8 @@ function createMeshCore(agent) if (this.httprequest.consent && (this.httprequest.consent & 8)) { // User Consent Prompt is required - // Send a console message back using the console channel, "\n" is supported. this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'Waiting for user to grant access...' })); - var pr = require('message-box').create('MeshCentral', this.httprequest.username + ' requesting KVM Access. Grant access?', 10); pr.ws = this; this.pause(); @@ -1107,37 +1127,32 @@ function createMeshCore(agent) pr.then( function () { - // Success! + // Success + MeshServerLog('Starting remote desktop after local user accepted (' + this.httprequest.remoteaddr + ')', this.httprequest); this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null })); - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 1)) - { + if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 1)) { // User Notifications is required try { require('toaster').Toast('MeshCentral', this.ws.httprequest.username + ' started a remote desktop session.'); } catch (ex) { } } - this.ws.httprequest.desktop.kvm.pipe(this.ws, { dataTypeSkip: 1 }); this.ws.resume(); }, function (e) { - // User Consent Denied/Failed! + // User Consent Denied/Failed + MeshServerLog('Failed to start remote desktop after local user rejected (' + this.httprequest.remoteaddr + ')', this.httprequest); this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() })); - - //var err = 'User consent: ' + e.toString(); - //var b = Buffer.alloc(5 + err.length); - //b.writeUInt16BE(MNG_ERROR, 0); - //b.writeUInt16BE(err.length + 4, 2); - //Buffer.from(err).copy(b, 4); - //this.ws.end(b); }); } else { // User Consent Prompt is not required - if (this.httprequest.consent && (this.httprequest.consent & 1)) - { + if (this.httprequest.consent && (this.httprequest.consent & 1)) { // User Notifications is required + MeshServerLog('Started remote desktop with toast notification (' + this.httprequest.remoteaddr + ')', this.httprequest); try { require('toaster').Toast('MeshCentral', this.httprequest.username + ' started a remote desktop session.'); } catch (ex) { } + } else { + MeshServerLog('Started remote desktop without notification (' + this.httprequest.remoteaddr + ')', this.httprequest); } this.httprequest.desktop.kvm.pipe(this, { dataTypeSkip: 1 }); } @@ -1161,35 +1176,36 @@ function createMeshCore(agent) if (this.httprequest.consent && (this.httprequest.consent & 32)) { // User Consent Prompt is required - // Send a console message back using the console channel, "\n" is supported. this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'Waiting for user to grant access...' })); - var pr = require('message-box').create('MeshCentral', this.httprequest.username + ' requesting remote file access. Grant access?', 10); pr.ws = this; this.pause(); pr.then( function () { - // Success! + // Success + MeshServerLog('Starting remote files after local user accepted (' + this.httprequest.remoteaddr + ')', this.httprequest); this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null })); - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 4)) - { + if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 4)) { // User Notifications is required try { require('toaster').Toast('MeshCentral', this.ws.httprequest.username + ' started a remote file session.'); } catch (ex) { } } this.ws.resume(); }, function (e) { - // User Consent Denied/Failed! + // User Consent Denied/Failed + MeshServerLog('Failed to start remote files after local user rejected (' + this.httprequest.remoteaddr + ')', this.httprequest); this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() })); }); - } - else { + } else { // User Consent Prompt is not required if (this.httprequest.consent && (this.httprequest.consent & 4)) { // User Notifications is required + MeshServerLog('Started remote files with toast notification (' + this.httprequest.remoteaddr + ')', this.httprequest); try { require('toaster').Toast('MeshCentral', this.httprequest.username + ' started a remote file session.'); } catch (ex) { } + } else { + MeshServerLog('Started remote files without notification (' + this.httprequest.remoteaddr + ')', this.httprequest); } this.resume(); } @@ -1252,12 +1268,15 @@ function createMeshCore(agent) case 'mkdir': { // Create a new empty folder fs.mkdirSync(cmd.path); + MeshServerLog('Create folder: \"' + cmd.path + '\"', this.httprequest); break; } case 'rm': { // Delete, possibly recursive delete for (var i in cmd.delfiles) { - try { deleteFolderRecursive(obj.path.join(cmd.path, cmd.delfiles[i]), cmd.rec); } catch (e) { } + var p = obj.path.join(cmd.path, cmd.delfiles[i]), delcount = 0; + try { delcount = deleteFolderRecursive(p, cmd.rec); } catch (e) { } + MeshServerLog((cmd.rec ? 'Delete recursive: \"' : 'Delete: \"') + p + '\", ' + delcount + ' element(s) removed', this.httprequest); } break; } @@ -1265,6 +1284,7 @@ function createMeshCore(agent) // Rename a file or folder var oldfullpath = obj.path.join(cmd.path, cmd.oldname); var newfullpath = obj.path.join(cmd.path, cmd.newname); + MeshServerLog('Rename: \"' + oldfullpath + '\" to \"' + cmd.newname + '\"', this.httprequest); try { fs.renameSync(oldfullpath, newfullpath); } catch (e) { console.log(e); } break; } @@ -1272,6 +1292,7 @@ function createMeshCore(agent) // Download a file var sendNextBlock = 0; if (cmd.sub == 'start') { // Setup the download + MeshServerLog('Download: \"' + cmd.path + '\"', this.httprequest); if (this.filedownload != null) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; } this.filedownload = { id: cmd.id, path: cmd.path, ptr: 0 } try { this.filedownload.f = fs.openSync(this.filedownload.path, 'rbN'); } catch (e) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; } @@ -1317,6 +1338,7 @@ function createMeshCore(agent) if (this.httprequest.uploadFile != undefined) { fs.closeSync(this.httprequest.uploadFile); this.httprequest.uploadFile = undefined; } if (cmd.path == undefined) break; var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path; + MeshServerLog('Upload: \"' + filepath + '\"', this.httprequest); try { this.httprequest.uploadFile = fs.openSync(filepath, 'wbN'); } catch (e) { this.write(new Buffer(JSON.stringify({ action: 'uploaderror', reqid: cmd.reqid }))); break; } this.httprequest.uploadFileid = cmd.reqid; if (this.httprequest.uploadFile) { this.write(new Buffer(JSON.stringify({ action: 'uploadstart', reqid: this.httprequest.uploadFileid }))); } @@ -1326,6 +1348,7 @@ function createMeshCore(agent) // Copy a bunch of files from scpath to dspath for (var i in cmd.names) { var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]); + MeshServerLog('Copy: \"' + sc + '\" to \"' + ds + '\"', this.httprequest); if (sc != ds) { try { fs.copyFileSync(sc, ds); } catch (e) { } } } break; @@ -1334,6 +1357,7 @@ function createMeshCore(agent) // Move a bunch of files from scpath to dspath for (var i in cmd.names) { var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]); + MeshServerLog('Move: \"' + sc + '\" to \"' + ds + '\"', this.httprequest); if (sc != ds) { try { fs.copyFileSync(sc, ds); fs.unlinkSync(sc); } catch (e) { } } } break; @@ -1349,19 +1373,23 @@ function createMeshCore(agent) // Delete a directory with a files and directories within it function deleteFolderRecursive(path, rec) { + var count = 0; if (fs.existsSync(path)) { if (rec == true) { fs.readdirSync(obj.path.join(path, '*')).forEach(function (file, index) { var curPath = obj.path.join(path, file); if (fs.statSync(curPath).isDirectory()) { // recurse - deleteFolderRecursive(curPath, true); + count += deleteFolderRecursive(curPath, true); } else { // delete file fs.unlinkSync(curPath); + count++; } }); } fs.unlinkSync(path); + count++; } + return count; }; // Called when receiving control data on WebRTC @@ -1391,6 +1419,7 @@ function createMeshCore(agent) // Lock the current user out of the desktop try { if (process.platform == 'win32') { + MeshServerLog('Locking remote user out of desktop', ws.httprequest); var child = require('child_process'); child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 }); } @@ -1557,6 +1586,9 @@ function createMeshCore(agent) } break; */ + case 'log': + if (args['_'].length != 1) { response = 'Proper usage: log "sample text"'; } else { MeshServerLog(args['_'][0]); response = 'ok'; } + break; case 'getclip': if (require('MeshAgent').isService) { require('clipboard').dispatchRead().then(function (str) { sendConsoleText(str, sessionid); }); diff --git a/meshagent.js b/meshagent.js index c9e36460..630f84a9 100644 --- a/meshagent.js +++ b/meshagent.js @@ -1192,6 +1192,20 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { // Nothing is done right now. break; } + case 'log': + { + // Log a value in the event log + if ((typeof command.msg == 'string') && (command.msg.length < 4096)) { + var event = { etype: 'node', action: 'agentlog', nodeid: obj.dbNodeKey, domain: domain.id, msg: command.msg }; + if (typeof command.userid == 'string') { + var loguser = parent.users[command.userid]; + if (loguser) { event.userid = command.userid; event.username = loguser.name; } + } + if ((typeof command.sessionid == 'string') && (command.sessionid.length < 500)) { event.sessionid = command.sessionid; } + parent.parent.DispatchEvent(['*', obj.dbMeshKey], obj, event); + } + break; + } case 'ping': { sendPong(); break; } case 'pong': { break; } case 'getScript': @@ -1273,10 +1287,8 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { break; } case 'log': { - // Only the diagnostic agent can do - if (((obj.agentInfo.capabilities & 0x40) != 0) && (typeof command.value.value == 'string') && (command.value.value.length < 256)) - { - // Log a value in the event log of the main again + if (((obj.agentInfo.capabilities & 0x40) != 0) && (typeof command.value.value == 'string') && (command.value.value.length < 256)) { + // If this is a diagnostic agent, log the event in the log of the main agent var event = { etype: 'node', action: 'diagnostic', nodeid: obj.realNodeKey, domain: domain.id, msg: command.value.value }; parent.parent.DispatchEvent(['*', obj.dbMeshKey], obj, event); } diff --git a/meshuser.js b/meshuser.js index 43bf5ef7..17ffe301 100644 --- a/meshuser.js +++ b/meshuser.js @@ -70,7 +70,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if (parent.parent.multiServer == null) { var targets = ['*', 'server-users']; if (obj.user.groups) { for (var i in obj.user.groups) { targets.push('server-users:' + i); } } - parent.parent.DispatchEvent(targets, obj, { action: 'wssessioncount', username: obj.user.name, count: parent.wssessions[obj.user._id].length, nolog: 1, domain: domain.id }); + parent.parent.DispatchEvent(targets, obj, { action: 'wssessioncount', userid: user._id, username: user.name, count: parent.wssessions[obj.user._id].length, nolog: 1, domain: domain.id }); } else { parent.recountSessions(ws.sessionId); // Recount sessions } @@ -141,6 +141,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use command.consent = mesh.consent; // Add user consent if (typeof domain.userconsentflags == 'number') { command.consent |= domain.userconsentflags; } // Add server required consent flags command.username = user.name; // Add user name + command.userid = user._id; // Add user id + command.remoteaddr = (req.ip.startsWith('::ffff:')) ? (req.ip.substring(7)) : req.ip; // User's IP address delete command.nodeid; // Remove the nodeid since it's implied try { agent.send(JSON.stringify(command)); } catch (ex) { } } @@ -157,6 +159,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use command.consent = mesh.consent; // Add user consent if (typeof domain.userconsentflags == 'number') { command.consent |= domain.userconsentflags; } // Add server required consent flags command.username = user.name; // Add user name + command.userid = user._id; // Add user id + command.remoteaddr = (req.ip.startsWith('::ffff:')) ? (req.ip.substring(7)) : req.ip; // User's IP address parent.parent.multiServer.DispatchMessageSingleServer(command, routing.serverid); } } @@ -214,7 +218,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if (parent.parent.multiServer == null) { var targets = ['*', 'server-users']; if (obj.user.groups) { for (var i in obj.user.groups) { targets.push('server-users:' + i); } } - parent.parent.DispatchEvent(targets, obj, { action: 'wssessioncount', username: user.name, count: parent.wssessions[user._id].length, nolog: 1, domain: domain.id }); + parent.parent.DispatchEvent(targets, obj, { action: 'wssessioncount', userid: user._id, username: user.name, count: parent.wssessions[user._id].length, nolog: 1, domain: domain.id }); } else { parent.recountSessions(ws.sessionId); // Recount sessions } @@ -835,14 +839,6 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use } break; } - case 'clearevents': - { - // Delete all events - if (user.siteadmin != 0xFFFFFFFF) break; - db.RemoveAllEvents(domain.id); - parent.parent.DispatchEvent(['*', 'server-global'], obj, { action: 'clearevents', nolog: 1, domain: domain.id }); - break; - } case 'users': { // Request a list of all users @@ -885,7 +881,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use parent.db.SetUser(user); // Event the change - var message = { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', domain: domain.id }; + var message = { etype: 'user', userid: user._id, username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', domain: domain.id }; if (db.changeStream) { message.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come. if (oldemail != null) { message.msg = 'Changed email of user ' + user.name + ' from ' + oldemail + ' to ' + user.email; @@ -994,7 +990,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if (mesh.links[deluser._id] != null) { delete mesh.links[deluser._id]; parent.db.Set(common.escapeLinksFieldName(mesh)); } // Notify mesh change change = 'Removed user ' + deluser.name + ' from group ' + mesh.name; - var event = { etype: 'mesh', username: user.name, userid: user._id, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id }; + var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id }; if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the mesh. Another event will come. parent.parent.DispatchEvent(['*', mesh._id, deluser._id, user._id], obj, event); } @@ -1115,9 +1111,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use var event, targets = ['*', 'server-users']; if (newuser.groups) { for (var i in newuser.groups) { targets.push('server-users:' + i); } } if (newuser.email == null) { - event = { etype: 'user', username: newuser.name, account: parent.CloneSafeUser(newuser), action: 'accountcreate', msg: 'Account created, username is ' + newuser.name, domain: domain.id }; + event = { etype: 'user', userid: newuser._id, username: newuser.name, account: parent.CloneSafeUser(newuser), action: 'accountcreate', msg: 'Account created, username is ' + newuser.name, domain: domain.id }; } else { - event = { etype: 'user', username: newuser.name, account: parent.CloneSafeUser(newuser), action: 'accountcreate', msg: 'Account created, email is ' + newuser.email, domain: domain.id }; + event = { etype: 'user', userid: newuser._id, username: newuser.name, account: parent.CloneSafeUser(newuser), action: 'accountcreate', msg: 'Account created, email is ' + newuser.email, domain: domain.id }; } if (parent.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to create the user. Another event will come. parent.parent.DispatchEvent(targets, obj, event); @@ -1202,9 +1198,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use var event, targets = ['*', 'server-users']; if (newuser.groups) { for (var i in newuser.groups) { targets.push('server-users:' + i); } } if (command.email == null) { - event = { etype: 'user', username: newusername, account: parent.CloneSafeUser(newuser), action: 'accountcreate', msg: 'Account created, username is ' + command.username, domain: domain.id }; + event = { etype: 'user', userid: newuser._id, username: newusername, account: parent.CloneSafeUser(newuser), action: 'accountcreate', msg: 'Account created, username is ' + command.username, domain: domain.id }; } else { - event = { etype: 'user', username: newusername, account: parent.CloneSafeUser(newuser), action: 'accountcreate', msg: 'Account created, email is ' + command.email.toLowerCase(), domain: domain.id }; + event = { etype: 'user', userid: newuser._id, username: newusername, account: parent.CloneSafeUser(newuser), action: 'accountcreate', msg: 'Account created, email is ' + command.email.toLowerCase(), domain: domain.id }; } if (parent.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to create the user. Another event will come. parent.parent.DispatchEvent(targets, obj, event); @@ -1285,7 +1281,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use var targets = ['*', 'server-users', user._id, chguser._id]; if (allTargetGroups) { for (var i in allTargetGroups) { targets.push('server-users:' + i); } } - var event = { etype: 'user', username: user.name, account: parent.CloneSafeUser(chguser), action: 'accountchange', msg: 'Account changed: ' + chguser.name, domain: domain.id }; + var event = { etype: 'user', userid: user._id, username: user.name, account: parent.CloneSafeUser(chguser), action: 'accountchange', msg: 'Account changed: ' + chguser.name, domain: domain.id }; if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come. parent.parent.DispatchEvent(targets, obj, event); } @@ -1324,7 +1320,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Notify change var targets = ['*', 'server-users', user._id]; if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } } - var event = { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Mesh notification change.', domain: domain.id }; + var event = { etype: 'user', userid: user._id, username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Mesh notification change.', domain: domain.id }; if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come. parent.parent.DispatchEvent(targets, obj, event); @@ -1361,7 +1357,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use var targets = ['*', 'server-users']; if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } } - var event = { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Account password changed: ' + user.name, domain: domain.id }; + var event = { etype: 'user', userid: user._id, username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Account password changed: ' + user.name, domain: domain.id }; if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come. parent.parent.DispatchEvent(targets, obj, event); @@ -1411,7 +1407,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use var targets = ['*', 'server-users', user._id, chguser._id]; if (chguser.groups) { for (var i in chguser.groups) { targets.push('server-users:' + i); } } - var event = { etype: 'user', username: user.name, account: parent.CloneSafeUser(chguser), action: 'accountchange', msg: 'Changed account credentials.', domain: domain.id }; + var event = { etype: 'user', userid: user._id, username: user.name, account: parent.CloneSafeUser(chguser), action: 'accountchange', msg: 'Changed account credentials.', domain: domain.id }; if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come. parent.parent.DispatchEvent(targets, obj, event); } else { @@ -1561,12 +1557,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Event the user change var targets = ['*', 'server-users', user._id]; if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } } - var event = { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', domain: domain.id, nolog: 1 }; + var event = { etype: 'user', userid: user._id, username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', domain: domain.id, nolog: 1 }; if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come. parent.parent.DispatchEvent(targets, obj, event); // Event the device group creation - var event = { etype: 'mesh', username: user.name, meshid: meshid, name: command.meshname, mtype: command.meshtype, desc: command.desc, action: 'createmesh', links: links, msg: 'Mesh created: ' + command.meshname, domain: domain.id }; + var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: meshid, name: command.meshname, mtype: command.meshtype, desc: command.desc, action: 'createmesh', links: links, msg: 'Mesh created: ' + command.meshname, domain: domain.id }; parent.parent.DispatchEvent(['*', meshid, user._id], obj, event); // Even if DB change stream is active, this event must be acted upon. try { ws.send(JSON.stringify({ action: 'createmesh', responseid: command.responseid, result: 'ok', meshid: meshid })); } catch (ex) { } @@ -1598,7 +1594,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if (err != null) { if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'deletemesh', responseid: command.responseid, result: err })); } catch (ex) { } } return; } // Fire the removal event first, because after this, the event will not route - var event = { etype: 'mesh', username: user.name, meshid: command.meshid, name: command.meshname, action: 'deletemesh', msg: 'Mesh deleted: ' + command.meshname, domain: domain.id }; + var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: command.meshid, name: command.meshname, action: 'deletemesh', msg: 'Mesh deleted: ' + command.meshname, domain: domain.id }; parent.parent.DispatchEvent(['*', command.meshid], obj, event); // Even if DB change stream is active, this event need to be acted on. // Remove all user links to this mesh @@ -1647,7 +1643,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if ((common.validateInt(command.consent) == true) && (command.consent != mesh.consent)) { if (change != '') change += ' and consent changed'; else change += 'Group "' + mesh.name + '" consent changed'; mesh.consent = command.consent; } if (change != '') { db.Set(common.escapeLinksFieldName(mesh)); - var event = { etype: 'mesh', username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, flags: mesh.flags, consent: mesh.consent, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id }; + var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, flags: mesh.flags, consent: mesh.consent, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id }; if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the mesh. Another event will come. parent.parent.DispatchEvent(['*', mesh._id, user._id], obj, event); } @@ -1797,7 +1793,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use db.Set(common.escapeLinksFieldName(mesh)); var amtpolicy2 = common.Clone(amtpolicy); delete amtpolicy2.password; - var event = { etype: 'mesh', username: user.name, meshid: mesh._id, amt: amtpolicy2, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id }; + var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: mesh._id, amt: amtpolicy2, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id }; if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the mesh. Another event will come. parent.parent.DispatchEvent(['*', mesh._id, user._id], obj, event); @@ -1845,7 +1841,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Event the new node var device2 = common.Clone(device); delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this. - parent.parent.DispatchEvent(['*', command.meshid], obj, { etype: 'node', username: user.name, action: 'addnode', node: device2, msg: 'Added device ' + command.devicename + ' to mesh ' + mesh.name, domain: domain.id }); + parent.parent.DispatchEvent(['*', command.meshid], obj, { etype: 'node', userid: user._id, username: user.name, action: 'addnode', node: device2, msg: 'Added device ' + command.devicename + ' to mesh ' + mesh.name, domain: domain.id }); }); } break; @@ -1909,7 +1905,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Event the node change var newMesh = parent.meshes[command.meshid]; - var event = { etype: 'node', username: user.name, action: 'nodemeshchange', nodeid: node._id, node: node, oldMeshId: oldMeshId, newMeshId: command.meshid, msg: 'Moved device ' + node.name + ' to group ' + newMesh.name, domain: domain.id }; + var event = { etype: 'node', userid: user._id, username: user.name, action: 'nodemeshchange', nodeid: node._id, node: node, oldMeshId: oldMeshId, newMeshId: command.meshid, msg: 'Moved device ' + node.name + ' to group ' + newMesh.name, domain: domain.id }; if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the mesh. Another event will come. parent.parent.DispatchEvent(['*', oldMeshId, command.meshid], obj, event); }); @@ -1950,7 +1946,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use }); // Event node deletion - var event = { etype: 'node', username: user.name, action: 'removenode', nodeid: node._id, msg: 'Removed device ' + node.name + ' from group ' + mesh.name, domain: domain.id }; + var event = { etype: 'node', userid: user._id, username: user.name, action: 'removenode', nodeid: node._id, msg: 'Removed device ' + node.name + ' from group ' + mesh.name, domain: domain.id }; // TODO: We can't use the changeStream for node delete because we will not know the meshid the device was in. //if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to remove the node. Another event will come. parent.parent.DispatchEvent(['*', node.meshid], obj, event); @@ -2080,7 +2076,6 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Get the mesh for this device mesh = 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 & 8) != 0)) { // "Remote Control permission" @@ -2088,7 +2083,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use var 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 })); } catch (ex) { } + try { agent.send(JSON.stringify({ action: 'toast', title: command.title, msg: command.msg, sessionid: ws.sessionId, username: user.name, userid: user._id })); } catch (ex) { } } } } @@ -2143,7 +2138,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 4) == 0)) return; // Ready the node change event - var changes = [], event = { etype: 'node', username: user.name, action: 'changenode', nodeid: node._id, domain: domain.id }; + var changes = [], event = { etype: 'node', userid: user._id, username: user.name, action: 'changenode', nodeid: node._id, domain: domain.id }; change = 0; event.msg = ": "; @@ -2405,7 +2400,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Notify change var targets = ['*', 'server-users', user._id]; if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } } - var event = { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added authentication application.', domain: domain.id }; + var event = { etype: 'user', userid: user._id, username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added authentication application.', domain: domain.id }; if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come. parent.parent.DispatchEvent(targets, obj, event); } else { @@ -2428,7 +2423,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Notify change var targets = ['*', 'server-users', user._id]; if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } } - var event = { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Removed authentication application.', domain: domain.id }; + var event = { etype: 'user', userid: user._id, username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Removed authentication application.', domain: domain.id }; if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come. parent.parent.DispatchEvent(targets, obj, event); } else { @@ -2467,7 +2462,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Notify change var targets = ['*', 'server-users', user._id]; if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } } - var event = { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added security key.', domain: domain.id }; + var event = { etype: 'user', userid: user._id, username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added security key.', domain: domain.id }; if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come. parent.parent.DispatchEvent(targets, obj, event); break; @@ -2502,7 +2497,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Notify change var targets = ['*', 'server-users', user._id]; if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } } - var event = { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Removed security key.', domain: domain.id }; + var event = { etype: 'user', userid: user._id, username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Removed security key.', domain: domain.id }; if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come. parent.parent.DispatchEvent(targets, obj, event); break; @@ -2552,7 +2547,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Notify change TODO: Should be done on all sessions/servers for this user. var targets = ['*', 'server-users', user._id]; if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } } - var event = { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added security key.', domain: domain.id }; + var event = { etype: 'user', userid: user._id, username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added security key.', domain: domain.id }; if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come. parent.parent.DispatchEvent(targets, obj, event); } else { @@ -2605,7 +2600,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Notify change var targets = ['*', 'server-users', user._id]; if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } } - var event = { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added security key.', domain: domain.id }; + var event = { etype: 'user', userid: user._id, username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added security key.', domain: domain.id }; if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come. parent.parent.DispatchEvent(targets, obj, event); } else { diff --git a/package.json b/package.json index 972fa3e0..11fa96c7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.3.8-s", + "version": "0.3.8-t", "keywords": [ "Remote Management", "Intel AMT", diff --git a/public/styles/style.css b/public/styles/style.css index f1a18784..e3dd8ec5 100644 --- a/public/styles/style.css +++ b/public/styles/style.css @@ -837,7 +837,7 @@ NoMeshesPanel img { color: gray; } -#p3events { +#p3events, #p16events, #p31events { height: calc(100vh - 245px); overflow-y: scroll; } @@ -848,12 +848,6 @@ NoMeshesPanel img { padding: 0; } -.p3eventsTable { - width: 100%; - border-spacing: 0; - padding: 0; -} - #p4name, #p4email, #p4pass1, #p4pass2 { width: 230px; } @@ -1360,6 +1354,14 @@ a { float: left; } +.si5 { + background: url(../images/icons16.png) -80px 0px; + height: 16px; + width: 16px; + border: none; + float: left; +} + .mi { background: url(../images/meshicon50.png) 0px 0px; height: 50px; @@ -1582,6 +1584,14 @@ a { float: none; } +#p16events .g1 { + float: none; +} + +#p31events .g1 { + float: none; +} + #p3users .g1 { height: 24px; float: left; @@ -1617,6 +1627,14 @@ a { float: none; } +#p16events .g2 { + float: none; +} + +#p31events .g2 { + float: none; +} + #p3users .g2 { height: 24px; float: right; @@ -2288,6 +2306,7 @@ a { width: calc(100vw - 120px); } +/* #p16events, #p31events { max-height: calc(100vh - 245px); overflow-y: auto; @@ -2296,6 +2315,7 @@ a { .room4submenu #p16events, #p31events { max-height: calc(100vh - 269px); } +*/ .night #p16events, #p31events { color: #222; diff --git a/views/default-min.handlebars b/views/default-min.handlebars index ed5144a1..cf724add 100644 --- a/views/default-min.handlebars +++ b/views/default-min.handlebars @@ -1 +1 @@ - {{{title}}}
{{{title}}}
{{{title2}}}

{{{logoutControl}}}

 

\ No newline at end of file + {{{title}}}
{{{title}}}
{{{title2}}}

{{{logoutControl}}}

 

\ No newline at end of file diff --git a/views/default-mobile-min.handlebars b/views/default-mobile-min.handlebars index b02b2847..4ad71dfc 100644 --- a/views/default-mobile-min.handlebars +++ b/views/default-mobile-min.handlebars @@ -1 +1 @@ - {{{title}}}
{{{title}}}
{{{title2}}}
\ No newline at end of file + {{{title}}}
{{{title}}}
{{{title2}}}
\ No newline at end of file diff --git a/views/default-mobile.handlebars b/views/default-mobile.handlebars index 6cc12148..932dabc8 100644 --- a/views/default-mobile.handlebars +++ b/views/default-mobile.handlebars @@ -1063,11 +1063,6 @@ } break; } - case 'clearevents': { - //events = []; - //events_update(); - break; - } case 'login': { // Update the last login time if (users != null && users['user/' + domain + '/' + message.event.username.toLowerCase()]) { users['user/' + domain + '/' + message.event.username.toLowerCase()].login = message.event.time; } diff --git a/views/default.handlebars b/views/default.handlebars index 4f9e4f7b..b65428e0 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -286,7 +286,6 @@ -
  Show
-
+