Started work on per-user session recording. #3064
Ylian Saint-Hilaire committed
Aug 26, 2021 at 12:42 UTC
e6b6bd061e0255d19e8bc02d0287f9387b717e69
7 files changed
+92
-25
meshcentral-config-schema.json
+1
@@ -675,6 +675,7 @@
675
"type": "object",
676
"additionalProperties": false,
677
"properties": {
678
+ "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." },
679
"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." },
680
"filepath": { "type": "string" },
681
"index": { "type": "boolean", "default": false },
meshdesktopmultiplex.js
+15
-3
@@ -731,9 +731,21 @@ function CreateDesktopMultiplexor(parent, domain, nodeid, func) {
731
if ((domain.sessionrecording == true || ((typeof domain.sessionrecording == 'object') && ((domain.sessionrecording.protocols == null) || (domain.sessionrecording.protocols.indexOf(2) >= 0))))) {
732
733
// Check again to make sure we need to start recording
734
- if (domain.sessionrecording.onlyselecteddevicegroups === true) {
735
- var mesh = parent.meshes[obj.meshid];
736
- if ((mesh.flags == null) || ((mesh.flags & 4) == 0)) { func(false); return; } // Do not record the session
734
+ if ((domain.sessionrecording.onlyselecteddevicegroups === true) || (domain.sessionrecording.onlyselectedusers === true)) {
735
+ var record = false;
736
+
737
+ // Check user recording
738
+ if (domain.sessionrecording.onlyselectedusers === true) {
739
+ // TODO: Check recording ???
740
+ }
741
+
742
+ // Check device group recording
743
+ if (domain.sessionrecording.onlyselecteddevicegroups === true) {
744
+ var mesh = parent.meshes[obj.meshid];
745
+ if ((mesh.flags != null) && ((mesh.flags & 4) != 0)) { record = true; }
746
+ }
747
+
748
+ if (record == false) { func(false); return; } // Do not record the session
749
}
750
751
var now = new Date(Date.now());
meshrelay.js
+28
-18
@@ -349,31 +349,41 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
349
xtextSession = 2; // 1 = Raw recording of all strings, 2 = Record chat session messages only.
350
}
351
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; }
352
-
352
+
353
if (recordSession) {
354
// Get the computer name
355
parent.db.Get(obj.req.query.nodeid, function (err, nodes) {
356
- var xusername = '', xdevicename = '', xdevicename2 = null, node = null;
356
+ var xusername = '', xdevicename = '', xdevicename2 = null, node = null, record = true;
357
if ((nodes != null) && (nodes.length == 1)) { node = nodes[0]; xdevicename2 = node.name; xdevicename = '-' + parent.common.makeFilename(node.name); }
358
359
- // Check again if we need to do recording
360
- if ((node == null) || (domain.sessionrecording.onlyselecteddevicegroups === true)) {
361
- var mesh = null;
362
- if (node != null) { mesh = parent.meshes[node.meshid]; }
363
- if ((node == null) || (mesh == null) || (mesh.flags == null) || ((mesh.flags & 4) == 0)) {
364
- // Do not record the session, just send session start
365
- try { ws.send('c'); } catch (ex) { } // Send connect to both peers
366
- try { relayinfo.peer1.ws.send('c'); } catch (ex) { }
367
-
368
- // Send any stored push messages
369
- obj.pushStoredMessages();
370
- relayinfo.peer1.pushStoredMessages();
359
+ // Check again if we need to do messenger recording
360
+ if ((domain.sessionrecording.onlyselectedusers === true) || (domain.sessionrecording.onlyselecteddevicegroups === true)) {
361
+ record = false;
362
372
- // Send other peer's image
373
- obj.sendPeerImage();
374
- relayinfo.peer1.sendPeerImage();
375
- return;
363
+ // Check if this device group needs to be recorded
364
+ if ((node == null) || (domain.sessionrecording.onlyselecteddevicegroups === true)) {
365
+ var mesh = null;
366
+ if (node != null) { mesh = parent.meshes[node.meshid]; }
367
+ if ((node != null) && (mesh != null) && (mesh.flags != null) && ((mesh.flags & 4) != 0)) { record = true; }
368
}
369
+
370
+ // Check if this user needs to be recorded
371
+ if ((sessionUser != null) && (sessionUser.flags != null) && ((sessionUser.flags & 2) != 0)) { record = true; }
372
+ }
373
+
374
+ // Do not record the session, just send session start
375
+ if (record == false) {
376
+ try { ws.send('c'); } catch (ex) { } // Send connect to both peers
377
+ try { relayinfo.peer1.ws.send('c'); } catch (ex) { }
378
+
379
+ // Send any stored push messages
380
+ obj.pushStoredMessages();
381
+ relayinfo.peer1.pushStoredMessages();
382
+
383
+ // Send other peer's image
384
+ obj.sendPeerImage();
385
+ relayinfo.peer1.sendPeerImage();
386
+ return;
387
}
388
389
// Get the username and make it acceptable as a filename
meshuser.js
+3
-1
@@ -492,7 +492,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
492
serverinfo.languages = parent.renderLanguages;
493
serverinfo.tlshash = Buffer.from(parent.webCertificateFullHashs[domain.id], 'binary').toString('hex').toUpperCase(); // SHA384 of server HTTPS certificate
494
serverinfo.agentCertHash = parent.agentCertificateHashBase64;
495
- if ((domain.sessionrecording) && (domain.sessionrecording.onlyselecteddevicegroups === true)) { serverinfo.devGroupSessionRecording = 1; } // Allow enabling of session recording
495
+ if ((domain.sessionrecording) && (domain.sessionrecording.onlyselectedusers === true)) { serverinfo.usersSessionRecording = 1; } // Allow enabling of session recording for user groups
496
+ if ((domain.sessionrecording) && (domain.sessionrecording.onlyselecteddevicegroups === true)) { serverinfo.devGroupSessionRecording = 1; } // Allow enabling of session recording for device groups
497
if ((parent.parent.config.domains[domain.id].amtacmactivation != null) && (parent.parent.config.domains[domain.id].amtacmactivation.acmmatch != null)) {
498
var matchingDomains = [];
499
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
1582
if (command.resetNextLogin === true) { chguser.passchange = -1; }
1583
if ((command.consent != null) && (typeof command.consent == 'number')) { if (command.consent == 0) { delete chguser.consent; } else { chguser.consent = command.consent; } change = 1; }
1584
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; }
1585
+ 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
1586
1587
// Site admins can change any server rights, user managers can only change AccountLock, NoMeshCmd and NoNewGroups
1588
if (common.validateInt(command.siteadmin) && (chguser._id !== user._id) && (chguser.siteadmin != command.siteadmin)) { // We can't change our own siteadmin permissions.
sample-config-advanced.json
+1
@@ -311,6 +311,7 @@
311
},
312
"_agentConfig": [ "webSocketMaskOverride=1", "coreDumpEnabled=1" ],
313
"_sessionRecording": {
314
+ "_onlySelectedUsers": true,
315
"_onlySelectedDeviceGroups": true,
316
"_filepath": "C:\\temp",
317
"_index": true,
views/default.handlebars
+29
@@ -14051,6 +14051,17 @@
14051
x += addDeviceAttribute("Phone Number", (user.phone?user.phone:('<i>' + "None" + '</i>')) + ' <img class=hoverButton style=cursor:pointer src="images/link5.png" onclick=p30editPhone() />');
14052
}
14053
14054
+ // Display features
14055
+ if (serverinfo.usersSessionRecording == 1) {
14056
+ var userFeatures = [];
14057
+ if (user.flags) {
14058
+ if ((serverinfo.usersSessionRecording == 1) && (user.flags & 2)) { userFeatures.push("Record Sessions"); }
14059
+ }
14060
+ userFeatures = userFeatures.join(', ');
14061
+ if (userFeatures == '') { userFeatures = '<i>' + "None" + '</i>'; }
14062
+ x += addDeviceAttribute("Features", addLink(userFeatures, 'p20edituserfeatures()'));
14063
+ }
14064
+
14065
x += addDeviceAttribute("Server Rights", premsg + msg.join(', ') + ' <img style=cursor:pointer class=hoverButton onclick=\'return showUserAdminDialog(event,"' + encodeURIComponentEx(user._id) + '")\' src="images/link5.png" />');
14066
if (user.quota) x += addDeviceAttribute("Server Quota", EscapeHtml(parseInt(user.quota) / 1024) + ' k');
14067
x += addDeviceAttribute("Creation", printDateTime(new Date(user.creation * 1000)));
@@ -14165,6 +14176,24 @@
14176
p30editPhoneValidate();
14177
}
14178
14179
+ function p20edituserfeatures() {
14180
+ if (xxdialogMode) return;
14181
+ var flags = (currentUser.flags)?currentUser.flags:0, x = ''; // Flags: 1 = Account Image, 2 = Session Recording
14182
+ if (serverinfo.usersSessionRecording == 1) {
14183
+ x += '<div><label><input type=checkbox id=d20flag2 onchange=p20edituserfeaturesValidate() ' + ((flags & 2) ? 'checked' : '') + '>' + "Record sessions" + '</label><br></div>';
14184
+ }
14185
+ setDialogMode(2, "Edit User Features", 3, p20edituserfeaturesEx, x);
14186
+ }
14187
+
14188
+ function p20edituserfeaturesValidate() { }
14189
+
14190
+ // Send to the server the new user's real name
14191
+ function p20edituserfeaturesEx() {
14192
+ var f = currentUser.flags & 1; // Flags: 1 = Account Image, 2 = Session Recording
14193
+ if (Q('d20flag2').checked) { f += 2; }
14194
+ meshserver.send({ action: 'edituser', id: currentUser._id, flags: f });
14195
+ }
14196
+
14197
function p30editPhoneValidate(x) {
14198
var ok = (Q('d2phoneinput').value == '') || (isPhoneNumber(Q('d2phoneinput').value));
14199
QE('idx_dlgOkButton', ok);
webserver.js
+15
-3
@@ -3943,9 +3943,21 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3943
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
3944
// Check again if we need to do recording
3945
var record = true;
3946
- if (domain.sessionrecording.onlyselecteddevicegroups === true) {
3947
- var mesh = obj.meshes[node.meshid];
3948
- if ((mesh.flags == null) || ((mesh.flags & 4) == 0)) { record = false; } // Do not record the session
3946
+
3947
+ // Check user or device group recording
3948
+ if ((domain.sessionrecording.onlyselectedusers === true) || (domain.sessionrecording.onlyselecteddevicegroups === true)) {
3949
+ record = false;
3950
+
3951
+ // Check device group recording
3952
+ if (domain.sessionrecording.onlyselecteddevicegroups === true) {
3953
+ var mesh = obj.meshes[node.meshid];
3954
+ if ((mesh.flags != null) && ((mesh.flags & 4) != 0)) { record = true; } // Record the session
3955
+ }
3956
+
3957
+ // Check user recording
3958
+ if (domain.sessionrecording.onlyselectedusers === true) {
3959
+ if ((user.flags != null) && ((user.flags & 2) != 0)) { record = true; } // Record the session
3960
+ }
3961
}
3962
3963
if (record == true) {