mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-12-23 05:42:13 +03:00
Added recording space/count quota.
This commit is contained in:
parent
9f9fdf7b92
commit
ea9edefad1
@ -633,7 +633,7 @@ function CreateMeshCentralServer(config, args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check top level configuration for any unreconized values
|
// Check top level configuration for any unreconized values
|
||||||
if (config) { for (var i in config) { if ((typeof i == 'string') && (i.length > 0) && (i[0] != '_') && (['settings', 'domains', 'configfiles', 'smtp', 'letsencrypt', 'peers', 'sms'].indexOf(i) == -1)) { addServerWarning('Unrecognized configuration option \"' + i + '\".'); } } }
|
if (config) { for (var i in config) { if ((typeof i == 'string') && (i.length > 0) && (i[0] != '_') && (['settings', 'domaindefaults', 'domains', 'configfiles', 'smtp', 'letsencrypt', 'peers', 'sms'].indexOf(i) == -1)) { addServerWarning('Unrecognized configuration option \"' + i + '\".'); } } }
|
||||||
|
|
||||||
if (typeof obj.args.userallowedip == 'string') { if (obj.args.userallowedip == '') { config.settings.userallowedip = obj.args.userallowedip = null; } else { config.settings.userallowedip = obj.args.userallowedip = obj.args.userallowedip.split(','); } }
|
if (typeof obj.args.userallowedip == 'string') { if (obj.args.userallowedip == '') { config.settings.userallowedip = obj.args.userallowedip = null; } else { config.settings.userallowedip = obj.args.userallowedip = obj.args.userallowedip.split(','); } }
|
||||||
if (typeof obj.args.userblockedip == 'string') { if (obj.args.userblockedip == '') { config.settings.userblockedip = obj.args.userblockedip = null; } else { config.settings.userblockedip = obj.args.userblockedip = obj.args.userblockedip.split(','); } }
|
if (typeof obj.args.userblockedip == 'string') { if (obj.args.userblockedip == '') { config.settings.userblockedip = obj.args.userblockedip = null; } else { config.settings.userblockedip = obj.args.userblockedip = obj.args.userblockedip.split(','); } }
|
||||||
@ -1009,6 +1009,10 @@ function CreateMeshCentralServer(config, args) {
|
|||||||
var bannedDomains = ['public', 'private', 'images', 'scripts', 'styles', 'views']; // List of banned domains
|
var bannedDomains = ['public', 'private', 'images', 'scripts', 'styles', 'views']; // List of banned domains
|
||||||
for (i in obj.config.domains) { for (var j in bannedDomains) { if (i == bannedDomains[j]) { console.log("ERROR: Domain '" + i + "' is not allowed domain name in config.json."); return; } } }
|
for (i in obj.config.domains) { for (var j in bannedDomains) { if (i == bannedDomains[j]) { console.log("ERROR: Domain '" + i + "' is not allowed domain name in config.json."); return; } } }
|
||||||
for (i in obj.config.domains) {
|
for (i in obj.config.domains) {
|
||||||
|
// Apply default domain settings if present
|
||||||
|
if (typeof obj.config.domaindefaults == 'object') { for (var j in obj.config.domaindefaults) { if (obj.config.domains[i][j] == null) { obj.config.domains[i][j] = obj.config.domaindefaults[j]; } } }
|
||||||
|
|
||||||
|
// Perform domain setup
|
||||||
if (typeof obj.config.domains[i] != 'object') { console.log("ERROR: Invalid domain configuration in config.json."); process.exit(); return; }
|
if (typeof obj.config.domains[i] != 'object') { console.log("ERROR: Invalid domain configuration in config.json."); process.exit(); return; }
|
||||||
if ((i.length > 0) && (i[0] == '_')) { delete obj.config.domains[i]; continue; } // Remove any domains with names that start with _
|
if ((i.length > 0) && (i[0] == '_')) { delete obj.config.domains[i]; continue; } // Remove any domains with names that start with _
|
||||||
if (typeof config.domains[i].auth == 'string') { config.domains[i].auth = config.domains[i].auth.toLowerCase(); }
|
if (typeof config.domains[i].auth == 'string') { config.domains[i].auth = config.domains[i].auth.toLowerCase(); }
|
||||||
|
@ -216,6 +216,7 @@ function CreateDesktopMultiplexor(parent, domain, nodeid, func) {
|
|||||||
parent.parent.fs.close(fd);
|
parent.parent.fs.close(fd);
|
||||||
// Now that the recording file is closed, check if we need to index this file.
|
// Now that the recording file is closed, check if we need to index this file.
|
||||||
if (domain.sessionrecording.index !== false) { parent.parent.certificateOperations.acceleratorPerformOperation('indexMcRec', filename); }
|
if (domain.sessionrecording.index !== false) { parent.parent.certificateOperations.acceleratorPerformOperation('indexMcRec', filename); }
|
||||||
|
cleanUpRecordings();
|
||||||
}, rf.filename);
|
}, rf.filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -663,6 +664,34 @@ function CreateDesktopMultiplexor(parent, domain, nodeid, func) {
|
|||||||
} catch (ex) { console.log(ex); func(fd, tag); }
|
} catch (ex) { console.log(ex); func(fd, tag); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there is a recording quota, remove any old recordings if needed
|
||||||
|
function cleanUpRecordings() {
|
||||||
|
if (domain.sessionrecording && ((typeof domain.sessionrecording.maxrecordings == 'number') || (typeof domain.sessionrecording.maxrecordingsizemegabytes == 'number'))) {
|
||||||
|
var recPath = null, fs = require('fs');
|
||||||
|
if (domain.sessionrecording.filepath) { recPath = domain.sessionrecording.filepath; } else { recPath = parent.parent.recordpath; }
|
||||||
|
fs.readdir(recPath, function (err, files) {
|
||||||
|
if ((err != null) || (files == null)) return;
|
||||||
|
var recfiles = [];
|
||||||
|
for (var i in files) {
|
||||||
|
if (files[i].endsWith('.mcrec')) {
|
||||||
|
var j = files[i].indexOf('-');
|
||||||
|
if (j > 0) { recfiles.push({ n: files[i], r: files[i].substring(j + 1), s: fs.statSync(parent.parent.path.join(recPath, files[i])).size }); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
recfiles.sort(function (a, b) { if (a.r < b.r) return 1; if (a.r > b.r) return -1; return 0; });
|
||||||
|
var totalFiles = 0, totalSize = 0;
|
||||||
|
for (var i in recfiles) {
|
||||||
|
var overQuota = false;
|
||||||
|
if ((typeof domain.sessionrecording.maxrecordings == 'number') && (totalFiles >= domain.sessionrecording.maxrecordings)) { overQuota = true; }
|
||||||
|
else if ((typeof domain.sessionrecording.maxrecordingsizemegabytes == 'number') && (totalSize >= (domain.sessionrecording.maxrecordingsizemegabytes * 1048576))) { overQuota = true; }
|
||||||
|
if (overQuota) { fs.unlinkSync(parent.parent.path.join(recPath, recfiles[i].n)); }
|
||||||
|
totalFiles++;
|
||||||
|
totalSize += recfiles[i].s;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
recordingSetup(domain, function () { func(obj); });
|
recordingSetup(domain, function () { func(obj); });
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
29
meshrelay.js
29
meshrelay.js
@ -408,6 +408,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
|||||||
parent.parent.fs.close(fd);
|
parent.parent.fs.close(fd);
|
||||||
// Now that the recording file is closed, check if we need to index this file.
|
// Now that the recording file is closed, check if we need to index this file.
|
||||||
if (domain.sessionrecording.index !== false) { parent.parent.certificateOperations.acceleratorPerformOperation('indexMcRec', tag.logfile.filename); }
|
if (domain.sessionrecording.index !== false) { parent.parent.certificateOperations.acceleratorPerformOperation('indexMcRec', tag.logfile.filename); }
|
||||||
|
cleanUpRecordings();
|
||||||
}, { ws: ws, pws: peer.ws, logfile: logfile });
|
}, { ws: ws, pws: peer.ws, logfile: logfile });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -500,6 +501,34 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there is a recording quota, remove any old recordings if needed
|
||||||
|
function cleanUpRecordings() {
|
||||||
|
if (domain.sessionrecording && ((typeof domain.sessionrecording.maxrecordings == 'number') || (typeof domain.sessionrecording.maxrecordingsizemegabytes == 'number'))) {
|
||||||
|
var recPath = null, fs = require('fs');
|
||||||
|
if (domain.sessionrecording.filepath) { recPath = domain.sessionrecording.filepath; } else { recPath = parent.parent.recordpath; }
|
||||||
|
fs.readdir(recPath, function (err, files) {
|
||||||
|
if ((err != null) || (files == null)) return;
|
||||||
|
var recfiles = [];
|
||||||
|
for (var i in files) {
|
||||||
|
if (files[i].endsWith('.mcrec')) {
|
||||||
|
var j = files[i].indexOf('-');
|
||||||
|
if (j > 0) { recfiles.push({ n: files[i], r: files[i].substring(j + 1), s: fs.statSync(parent.parent.path.join(recPath, files[i])).size }); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
recfiles.sort(function (a, b) { if (a.r < b.r) return 1; if (a.r > b.r) return -1; return 0; });
|
||||||
|
var totalFiles = 0, totalSize = 0;
|
||||||
|
for (var i in recfiles) {
|
||||||
|
var overQuota = false;
|
||||||
|
if ((typeof domain.sessionrecording.maxrecordings == 'number') && (totalFiles >= domain.sessionrecording.maxrecordings)) { overQuota = true; }
|
||||||
|
else if ((typeof domain.sessionrecording.maxrecordingsizemegabytes == 'number') && (totalSize >= (domain.sessionrecording.maxrecordingsizemegabytes * 1048576))) { overQuota = true; }
|
||||||
|
if (overQuota) { fs.unlinkSync(parent.parent.path.join(recPath, recfiles[i].n)); }
|
||||||
|
totalFiles++;
|
||||||
|
totalSize += recfiles[i].s;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If this is not an authenticated session, or the session does not have routing instructions, just go ahead an connect to existing session.
|
// If this is not an authenticated session, or the session does not have routing instructions, just go ahead an connect to existing session.
|
||||||
performRelay();
|
performRelay();
|
||||||
return obj;
|
return obj;
|
||||||
|
15
meshuser.js
15
meshuser.js
@ -3569,7 +3569,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||||||
if (actionTaken) { parent.db.SetUser(user); }
|
if (actionTaken) { parent.db.SetUser(user); }
|
||||||
|
|
||||||
// Return one time passwords for this user
|
// Return one time passwords for this user
|
||||||
if (user.otpsecret || ((user.otphkeys != null) && (user.otphkeys.length > 0))) {
|
if (count2factoraAuths() > 0) {
|
||||||
ws.send(JSON.stringify({ action: 'otpauth-getpasswords', passwords: user.otpkeys ? user.otpkeys.keys : null }));
|
ws.send(JSON.stringify({ action: 'otpauth-getpasswords', passwords: user.otpkeys ? user.otpkeys.keys : null }));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4260,5 +4260,18 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the number of 2nd factor for this account
|
||||||
|
function count2factoraAuths() {
|
||||||
|
var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (parent.parent.mailserver != null));
|
||||||
|
var sms2fa = ((parent.parent.smsserver != null) && ((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)));
|
||||||
|
var authFactorCount = 0;
|
||||||
|
if (user.otpsecret == 1) { authFactorCount++; } // Authenticator time factor
|
||||||
|
if (email2fa && (user.otpekey != null)) { authFactorCount++; } // EMail factor
|
||||||
|
if (sms2fa && (user.phone != null)) { authFactorCount++; } // SMS factor
|
||||||
|
if (user.otphkeys != null) { authFactorCount += user.otphkeys.length; } // FIDO hardware factor
|
||||||
|
if ((authFactorCount > 0) && (user.otpkeys != null)) { authFactorCount++; } // Backup keys
|
||||||
|
return authFactorCount;
|
||||||
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "meshcentral",
|
"name": "meshcentral",
|
||||||
"version": "0.5.17",
|
"version": "0.5.18",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"Remote Management",
|
"Remote Management",
|
||||||
"Intel AMT",
|
"Intel AMT",
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
"_NpmPath": "c:\\npm.exe",
|
"_NpmPath": "c:\\npm.exe",
|
||||||
"_NpmProxy": "http://1.2.3.4:80",
|
"_NpmProxy": "http://1.2.3.4:80",
|
||||||
"_AllowHighQualityDesktop": true,
|
"_AllowHighQualityDesktop": true,
|
||||||
|
"_DesktopMultiplex": true,
|
||||||
"_UserAllowedIP": "127.0.0.1,192.168.1.0/24",
|
"_UserAllowedIP": "127.0.0.1,192.168.1.0/24",
|
||||||
"_UserBlockedIP": "127.0.0.1,::1,192.168.0.100",
|
"_UserBlockedIP": "127.0.0.1,::1,192.168.0.100",
|
||||||
"_AgentAllowedIP": "192.168.0.100/24",
|
"_AgentAllowedIP": "192.168.0.100/24",
|
||||||
@ -78,6 +79,12 @@
|
|||||||
"_MaxInvalidLogin": { "time": 10, "count": 10, "coolofftime": 10 },
|
"_MaxInvalidLogin": { "time": 10, "count": 10, "coolofftime": 10 },
|
||||||
"_Plugins": { "enabled": true }
|
"_Plugins": { "enabled": true }
|
||||||
},
|
},
|
||||||
|
"_domaindefaults": {
|
||||||
|
"__comment__": "Any settings in this section is used as default setting for all domains",
|
||||||
|
"Title": "MyDefaultTitle",
|
||||||
|
"Footer": "Default page footer",
|
||||||
|
"NewAccounts": false
|
||||||
|
},
|
||||||
"_domains": {
|
"_domains": {
|
||||||
"": {
|
"": {
|
||||||
"Title": "MyServer",
|
"Title": "MyServer",
|
||||||
@ -137,6 +144,8 @@
|
|||||||
"_SessionRecording": {
|
"_SessionRecording": {
|
||||||
"_filepath": "C:\\temp",
|
"_filepath": "C:\\temp",
|
||||||
"_index": true,
|
"_index": true,
|
||||||
|
"_maxRecordings": 10,
|
||||||
|
"_maxRecordingSizeMegabytes": 3,
|
||||||
"__protocols__": "Is an array: 1 = Terminal, 2 = Desktop, 5 = Files, 100 = Intel AMT WSMAN, 101 = Intel AMT Redirection",
|
"__protocols__": "Is an array: 1 = Terminal, 2 = Desktop, 5 = Files, 100 = Intel AMT WSMAN, 101 = Intel AMT Redirection",
|
||||||
"protocols": [ 1, 2, 101 ]
|
"protocols": [ 1, 2, 101 ]
|
||||||
}
|
}
|
||||||
|
@ -1694,11 +1694,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the number of 2nd factor for this account
|
||||||
|
function count2factoraAuths() {
|
||||||
|
var authFactorCount = 0;
|
||||||
|
if (userinfo.otpsecret == 1) { authFactorCount++; } // Authenticator time factor
|
||||||
|
if ((features & 0x00800000) && (userinfo.otpekey == 1)) { authFactorCount++; } // EMail factor
|
||||||
|
if ((features & 0x04000000) && (userinfo.phone != null)) { authFactorCount++; } // SMS factor
|
||||||
|
if (userinfo.otphkeys != null) { authFactorCount += userinfo.otphkeys; } // FIDO hardware factor
|
||||||
|
if ((authFactorCount > 0) && (userinfo.otpkeys == 1)) { authFactorCount++; } // Backup keys
|
||||||
|
return authFactorCount;
|
||||||
|
}
|
||||||
|
|
||||||
var backupCodesWarningDone = false;
|
var backupCodesWarningDone = false;
|
||||||
function updateSelf() {
|
function updateSelf() {
|
||||||
|
var authFactorCount = count2factoraAuths(); // Get the number of 2nd factors
|
||||||
QV('verifyEmailId', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true));
|
QV('verifyEmailId', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true));
|
||||||
QV('verifyEmailId2', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true));
|
QV('verifyEmailId2', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true));
|
||||||
QV('manageOtp', (userinfo.otpsecret == 1) || (userinfo.otphkeys > 0));
|
QV('manageOtp', authFactorCount > 0);
|
||||||
QV('authPhoneNumberCheck', (userinfo.phone != null));
|
QV('authPhoneNumberCheck', (userinfo.phone != null));
|
||||||
QV('authEmailSetupCheck', (userinfo.otpekey == 1) && (userinfo.email != null) && (userinfo.emailVerified == true));
|
QV('authEmailSetupCheck', (userinfo.otpekey == 1) && (userinfo.email != null) && (userinfo.emailVerified == true));
|
||||||
QV('authAppSetupCheck', userinfo.otpsecret == 1);
|
QV('authAppSetupCheck', userinfo.otpsecret == 1);
|
||||||
@ -1707,12 +1719,6 @@
|
|||||||
masterUpdate(4 + 128 + 4096);
|
masterUpdate(4 + 128 + 4096);
|
||||||
|
|
||||||
// Check if none or at least 2 factors are enabled.
|
// Check if none or at least 2 factors are enabled.
|
||||||
var authFactorCount = 0;
|
|
||||||
if ((features & 0x00800000) && (userinfo.otpekey == 1)) { authFactorCount += 1; }
|
|
||||||
if ((features & 0x02000000) && (features & 0x04000000) && (userinfo.phone != null)) { authFactorCount += 1; }
|
|
||||||
if (userinfo.otpkeys == 1) { authFactorCount += 1; }
|
|
||||||
if (userinfo.otpsecret == 1) { authFactorCount += 1; }
|
|
||||||
if (userinfo.otphkeys != null) { authFactorCount += userinfo.otphkeys; }
|
|
||||||
if ((backupCodesWarningDone == false) && (authFactorCount == 1)) {
|
if ((backupCodesWarningDone == false) && (authFactorCount == 1)) {
|
||||||
var n = { text: "Please add two-factor backup codes. If the current factor is lost, there is not way to recover this account.", title: "Two factor authentication" };
|
var n = { text: "Please add two-factor backup codes. If the current factor is lost, there is not way to recover this account.", title: "Two factor authentication" };
|
||||||
addNotification(n);
|
addNotification(n);
|
||||||
@ -4838,7 +4844,7 @@
|
|||||||
if ((userinfo.emailVerified !== true) && (serverinfo.emailcheck == true) && (userinfo.siteadmin != 0xFFFFFFFF)) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until a email address is verified. This is required for password recovery. Go to the \"My Account\" tab to change and verify an email address."); return; }
|
if ((userinfo.emailVerified !== true) && (serverinfo.emailcheck == true) && (userinfo.siteadmin != 0xFFFFFFFF)) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until a email address is verified. This is required for password recovery. Go to the \"My Account\" tab to change and verify an email address."); return; }
|
||||||
|
|
||||||
// Remind the user to add two factor authentication
|
// Remind the user to add two factor authentication
|
||||||
if ((features & 0x00040000) && !((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0) || (userinfo.otpkeys > 0) || ((features & 0x00800000) && (userinfo.otpekey == 1)))) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until two-factor authentication is enabled. This is required for extra security. Go to the \"My Account\" tab and look at the \"Account Security\" section."); return; }
|
if ((features & 0x00040000) && (count2factoraAuths() == 0)) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until two-factor authentication is enabled. This is required for extra security. Go to the \"My Account\" tab and look at the \"Account Security\" section."); return; }
|
||||||
|
|
||||||
if (event && (event.shiftKey == true)) {
|
if (event && (event.shiftKey == true)) {
|
||||||
// Open the device in a different tab
|
// Open the device in a different tab
|
||||||
@ -7987,7 +7993,7 @@
|
|||||||
function account_manageOtp(action) {
|
function account_manageOtp(action) {
|
||||||
if ((xxdialogMode == 2) && (xxdialogTag == 'otpauth-manage')) { dialogclose(0); }
|
if ((xxdialogMode == 2) && (xxdialogTag == 'otpauth-manage')) { dialogclose(0); }
|
||||||
if (xxdialogMode || ((features & 4096) == 0)) return false;
|
if (xxdialogMode || ((features & 4096) == 0)) return false;
|
||||||
if ((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0)) { meshserver.send({ action: 'otpauth-getpasswords', subaction: action }); }
|
if (count2factoraAuths() > 0) { meshserver.send({ action: 'otpauth-getpasswords', subaction: action }); }
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user