From e6b6bd061e0255d19e8bc02d0287f9387b717e69 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Thu, 26 Aug 2021 12:42:09 -0700 Subject: [PATCH] Started work on per-user session recording. #3064 --- meshcentral-config-schema.json | 1 + meshdesktopmultiplex.js | 18 ++++++++++--- meshrelay.js | 46 +++++++++++++++++++++------------- meshuser.js | 4 ++- sample-config-advanced.json | 1 + views/default.handlebars | 29 +++++++++++++++++++++ webserver.js | 18 ++++++++++--- 7 files changed, 92 insertions(+), 25 deletions(-) diff --git a/meshcentral-config-schema.json b/meshcentral-config-schema.json index 005f3075..fe5263b8 100644 --- a/meshcentral-config-schema.json +++ b/meshcentral-config-schema.json @@ -675,6 +675,7 @@ "type": "object", "additionalProperties": false, "properties": { + "onlySelectedUsers": { "type": "boolean", "default": false, "description": "When enabled, only device users with the session recording feature turned on will be recorded. When false, all users are recorded." }, "onlySelectedDeviceGroups": { "type": "boolean", "default": false, "description": "When enabled, only device groups with the session recording feature turned on will be recorded. When false, all devices are recorded." }, "filepath": { "type": "string" }, "index": { "type": "boolean", "default": false }, diff --git a/meshdesktopmultiplex.js b/meshdesktopmultiplex.js index b9b2add7..209b9656 100644 --- a/meshdesktopmultiplex.js +++ b/meshdesktopmultiplex.js @@ -731,9 +731,21 @@ function CreateDesktopMultiplexor(parent, domain, nodeid, func) { if ((domain.sessionrecording == true || ((typeof domain.sessionrecording == 'object') && ((domain.sessionrecording.protocols == null) || (domain.sessionrecording.protocols.indexOf(2) >= 0))))) { // Check again to make sure we need to start recording - if (domain.sessionrecording.onlyselecteddevicegroups === true) { - var mesh = parent.meshes[obj.meshid]; - if ((mesh.flags == null) || ((mesh.flags & 4) == 0)) { func(false); return; } // Do not record the session + if ((domain.sessionrecording.onlyselecteddevicegroups === true) || (domain.sessionrecording.onlyselectedusers === true)) { + var record = false; + + // Check user recording + if (domain.sessionrecording.onlyselectedusers === true) { + // TODO: Check recording ??? + } + + // Check device group recording + if (domain.sessionrecording.onlyselecteddevicegroups === true) { + var mesh = parent.meshes[obj.meshid]; + if ((mesh.flags != null) && ((mesh.flags & 4) != 0)) { record = true; } + } + + if (record == false) { func(false); return; } // Do not record the session } var now = new Date(Date.now()); diff --git a/meshrelay.js b/meshrelay.js index 367ef99d..21affb31 100644 --- a/meshrelay.js +++ b/meshrelay.js @@ -349,31 +349,41 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) { xtextSession = 2; // 1 = Raw recording of all strings, 2 = Record chat session messages only. } if ((obj.req.query.p != null) && (obj.req.query.nodeid != null) && (sessionUser != null) && (domain.sessionrecording == true || ((typeof domain.sessionrecording == 'object') && ((domain.sessionrecording.protocols == null) || (domain.sessionrecording.protocols.indexOf(parseInt(obj.req.query.p)) >= 0))))) { recordSession = true; } - + if (recordSession) { // Get the computer name parent.db.Get(obj.req.query.nodeid, function (err, nodes) { - var xusername = '', xdevicename = '', xdevicename2 = null, node = null; + var xusername = '', xdevicename = '', xdevicename2 = null, node = null, record = true; if ((nodes != null) && (nodes.length == 1)) { node = nodes[0]; xdevicename2 = node.name; xdevicename = '-' + parent.common.makeFilename(node.name); } - // Check again if we need to do recording - if ((node == null) || (domain.sessionrecording.onlyselecteddevicegroups === true)) { - var mesh = null; - if (node != null) { mesh = parent.meshes[node.meshid]; } - if ((node == null) || (mesh == null) || (mesh.flags == null) || ((mesh.flags & 4) == 0)) { - // Do not record the session, just send session start - try { ws.send('c'); } catch (ex) { } // Send connect to both peers - try { relayinfo.peer1.ws.send('c'); } catch (ex) { } + // Check again if we need to do messenger recording + if ((domain.sessionrecording.onlyselectedusers === true) || (domain.sessionrecording.onlyselecteddevicegroups === true)) { + record = false; - // Send any stored push messages - obj.pushStoredMessages(); - relayinfo.peer1.pushStoredMessages(); - - // Send other peer's image - obj.sendPeerImage(); - relayinfo.peer1.sendPeerImage(); - return; + // Check if this device group needs to be recorded + if ((node == null) || (domain.sessionrecording.onlyselecteddevicegroups === true)) { + var mesh = null; + if (node != null) { mesh = parent.meshes[node.meshid]; } + if ((node != null) && (mesh != null) && (mesh.flags != null) && ((mesh.flags & 4) != 0)) { record = true; } } + + // Check if this user needs to be recorded + if ((sessionUser != null) && (sessionUser.flags != null) && ((sessionUser.flags & 2) != 0)) { record = true; } + } + + // Do not record the session, just send session start + if (record == false) { + try { ws.send('c'); } catch (ex) { } // Send connect to both peers + try { relayinfo.peer1.ws.send('c'); } catch (ex) { } + + // Send any stored push messages + obj.pushStoredMessages(); + relayinfo.peer1.pushStoredMessages(); + + // Send other peer's image + obj.sendPeerImage(); + relayinfo.peer1.sendPeerImage(); + return; } // Get the username and make it acceptable as a filename diff --git a/meshuser.js b/meshuser.js index 26221cab..eac7d0ea 100644 --- a/meshuser.js +++ b/meshuser.js @@ -492,7 +492,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use serverinfo.languages = parent.renderLanguages; serverinfo.tlshash = Buffer.from(parent.webCertificateFullHashs[domain.id], 'binary').toString('hex').toUpperCase(); // SHA384 of server HTTPS certificate serverinfo.agentCertHash = parent.agentCertificateHashBase64; - if ((domain.sessionrecording) && (domain.sessionrecording.onlyselecteddevicegroups === true)) { serverinfo.devGroupSessionRecording = 1; } // Allow enabling of session recording + if ((domain.sessionrecording) && (domain.sessionrecording.onlyselectedusers === true)) { serverinfo.usersSessionRecording = 1; } // Allow enabling of session recording for user groups + if ((domain.sessionrecording) && (domain.sessionrecording.onlyselecteddevicegroups === true)) { serverinfo.devGroupSessionRecording = 1; } // Allow enabling of session recording for device groups if ((parent.parent.config.domains[domain.id].amtacmactivation != null) && (parent.parent.config.domains[domain.id].amtacmactivation.acmmatch != null)) { var matchingDomains = []; for (var i in parent.parent.config.domains[domain.id].amtacmactivation.acmmatch) { @@ -1581,6 +1582,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if (command.resetNextLogin === true) { chguser.passchange = -1; } if ((command.consent != null) && (typeof command.consent == 'number')) { if (command.consent == 0) { delete chguser.consent; } else { chguser.consent = command.consent; } change = 1; } if ((command.phone != null) && (typeof command.phone == 'string') && ((command.phone == '') || isPhoneNumber(command.phone))) { if (command.phone == '') { delete chguser.phone; } else { chguser.phone = command.phone; } change = 1; } + if ((command.flags != null) && (typeof command.flags == 'number')) { if (command.flags == 0) { delete chguser.flags; } else { chguser.flags = command.flags; } change = 1; } // Flags: 1 = Account Image, 2 = Session Recording // Site admins can change any server rights, user managers can only change AccountLock, NoMeshCmd and NoNewGroups if (common.validateInt(command.siteadmin) && (chguser._id !== user._id) && (chguser.siteadmin != command.siteadmin)) { // We can't change our own siteadmin permissions. diff --git a/sample-config-advanced.json b/sample-config-advanced.json index d4fdd321..71d31283 100644 --- a/sample-config-advanced.json +++ b/sample-config-advanced.json @@ -311,6 +311,7 @@ }, "_agentConfig": [ "webSocketMaskOverride=1", "coreDumpEnabled=1" ], "_sessionRecording": { + "_onlySelectedUsers": true, "_onlySelectedDeviceGroups": true, "_filepath": "C:\\temp", "_index": true, diff --git a/views/default.handlebars b/views/default.handlebars index 4aebcef6..4a70b8ad 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -14051,6 +14051,17 @@ x += addDeviceAttribute("Phone Number", (user.phone?user.phone:('' + "None" + '')) + ' '); } + // Display features + if (serverinfo.usersSessionRecording == 1) { + var userFeatures = []; + if (user.flags) { + if ((serverinfo.usersSessionRecording == 1) && (user.flags & 2)) { userFeatures.push("Record Sessions"); } + } + userFeatures = userFeatures.join(', '); + if (userFeatures == '') { userFeatures = '' + "None" + ''; } + x += addDeviceAttribute("Features", addLink(userFeatures, 'p20edituserfeatures()')); + } + x += addDeviceAttribute("Server Rights", premsg + msg.join(', ') + ' '); if (user.quota) x += addDeviceAttribute("Server Quota", EscapeHtml(parseInt(user.quota) / 1024) + ' k'); x += addDeviceAttribute("Creation", printDateTime(new Date(user.creation * 1000))); @@ -14165,6 +14176,24 @@ p30editPhoneValidate(); } + function p20edituserfeatures() { + if (xxdialogMode) return; + var flags = (currentUser.flags)?currentUser.flags:0, x = ''; // Flags: 1 = Account Image, 2 = Session Recording + if (serverinfo.usersSessionRecording == 1) { + x += '

'; + } + setDialogMode(2, "Edit User Features", 3, p20edituserfeaturesEx, x); + } + + function p20edituserfeaturesValidate() { } + + // Send to the server the new user's real name + function p20edituserfeaturesEx() { + var f = currentUser.flags & 1; // Flags: 1 = Account Image, 2 = Session Recording + if (Q('d20flag2').checked) { f += 2; } + meshserver.send({ action: 'edituser', id: currentUser._id, flags: f }); + } + function p30editPhoneValidate(x) { var ok = (Q('d2phoneinput').value == '') || (isPhoneNumber(Q('d2phoneinput').value)); QE('idx_dlgOkButton', ok); diff --git a/webserver.js b/webserver.js index 582ece31..89b38d3f 100644 --- a/webserver.js +++ b/webserver.js @@ -3943,9 +3943,21 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (domain.sessionrecording == true || ((typeof domain.sessionrecording == 'object') && ((domain.sessionrecording.protocols == null) || (domain.sessionrecording.protocols.indexOf((req.query.p == 2) ? 101 : 100) >= 0)))) { // TODO 100 // Check again if we need to do recording var record = true; - if (domain.sessionrecording.onlyselecteddevicegroups === true) { - var mesh = obj.meshes[node.meshid]; - if ((mesh.flags == null) || ((mesh.flags & 4) == 0)) { record = false; } // Do not record the session + + // Check user or device group recording + if ((domain.sessionrecording.onlyselectedusers === true) || (domain.sessionrecording.onlyselecteddevicegroups === true)) { + record = false; + + // Check device group recording + if (domain.sessionrecording.onlyselecteddevicegroups === true) { + var mesh = obj.meshes[node.meshid]; + if ((mesh.flags != null) && ((mesh.flags & 4) != 0)) { record = true; } // Record the session + } + + // Check user recording + if (domain.sessionrecording.onlyselectedusers === true) { + if ((user.flags != null) && ((user.flags & 2) != 0)) { record = true; } // Record the session + } } if (record == true) {