diff --git a/amtscanner.js b/amtscanner.js index 0d8dad9f..4f1c9b5f 100644 --- a/amtscanner.js +++ b/amtscanner.js @@ -112,7 +112,7 @@ module.exports.CreateAmtScanner = function (parent) { obj.performScan = function () { //console.log('performScan'); if (obj.action == false) { return false; } - obj.parent.db.getLocalAmtNodes(function (err, docs) { + obj.parent.db.getLocalAmtNodes(10, function (err, docs) { // TODO: handler more than 10 computer scan at the same time. for (var i in obj.scanTable) { obj.scanTable[i].present = false; } if (err == null && docs.length > 0) { for (var i in docs) { diff --git a/db.js b/db.js index 882855b0..79940905 100644 --- a/db.js +++ b/db.js @@ -109,7 +109,7 @@ module.exports.CreateDB = function (parent) { obj.dispose = function () { for (var x in obj) { if (obj[x].close) { obj[x].close(); } delete obj[x]; } } obj.clearOldEntries = function (type, days, domain) { var cutoff = Date.now() - (1000 * 60 * 60 * 24 * days); obj.file.remove({ type: type, time: { $lt: cutoff } }, { multi: true }); } obj.getPowerTimeline = function (nodeid, func) { if (obj.databaseType == 1) { obj.file.find({ type: 'power', node: { $in: ['*', nodeid] } }).sort({ time: 1 }).exec(func); } else { obj.file.find({ type: 'power', node: { $in: ['*', nodeid] } }).sort({ time: 1 }, func); } } - obj.getLocalAmtNodes = function (func) { obj.file.find({ type: 'node', host: { $exists: true, $ne: null }, intelamt: { $exists: true } }, func); } + obj.getLocalAmtNodes = function (limit, func) { obj.file.find({ type: 'node', host: { $exists: true, $ne: null }, intelamt: { $exists: true } }).limit(limit, func); } obj.getAmtUuidNode = function (meshid, uuid, func) { obj.file.find({ type: 'node', meshid: meshid, 'intelamt.uuid': uuid }, func); } // This is used to rate limit a number of operation per day. Returns a startValue each new days, but you can substract it and save the value in the db. diff --git a/meshcentral.js b/meshcentral.js index 796ceec8..d89f47f1 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -79,7 +79,7 @@ function CreateMeshCentralServer(config, args) { try { require('./pass').hash('test', function () { }); } catch (e) { console.log('Old version of node, must upgrade.'); return; } // TODO: Not sure if this test works or not. // Check for invalid arguments - var validArguments = ['_', 'notls', 'user', 'port', 'aliasport', 'mpsport', 'mpsaliasport', 'redirport', 'cert', 'mpscert', 'deletedomain', 'deletedefaultdomain', 'showall', 'showusers', 'shownodes', 'showmeshes', 'showevents', 'showpower', 'clearpower', 'showiplocations', 'help', 'exactports', 'install', 'uninstall', 'start', 'stop', 'restart', 'debug', 'filespath', 'datapath', 'noagentupdate', 'launch', 'noserverbackup', 'mongodb', 'mongodbcol', 'wanonly', 'lanonly', 'nousers', 'mpsdebug', 'mpspass', 'ciralocalfqdn', 'dbexport', 'dbimport', 'selfupdate', 'tlsoffload', 'userallowedip', 'fastcert', 'swarmport', 'swarmdebug', 'logintoken', 'logintokenkey', 'logintokengen', 'logintokengen', 'mailtokengen']; + var validArguments = ['_', 'notls', 'user', 'port', 'aliasport', 'mpsport', 'mpsaliasport', 'redirport', 'cert', 'mpscert', 'deletedomain', 'deletedefaultdomain', 'showall', 'showusers', 'shownodes', 'showmeshes', 'showevents', 'showpower', 'clearpower', 'showiplocations', 'help', 'exactports', 'install', 'uninstall', 'start', 'stop', 'restart', 'debug', 'filespath', 'datapath', 'noagentupdate', 'launch', 'noserverbackup', 'mongodb', 'mongodbcol', 'wanonly', 'lanonly', 'nousers', 'mpsdebug', 'mpspass', 'ciralocalfqdn', 'dbexport', 'dbimport', 'selfupdate', 'tlsoffload', 'userallowedip', 'fastcert', 'swarmport', 'swarmdebug', 'logintoken', 'logintokenkey', 'logintokengen', 'logintokengen', 'mailtokengen', 'admin', 'unadmin']; for (var arg in obj.args) { obj.args[arg.toLocaleLowerCase()] = obj.args[arg]; if (validArguments.indexOf(arg.toLocaleLowerCase()) == -1) { console.log('Invalid argument "' + arg + '", use --help.'); return; } } if (obj.args.mongodb == true) { console.log('Must specify: --mongodb [connectionstring] \r\nSee https://docs.mongodb.com/manual/reference/connection-string/ for MongoDB connection string.'); return; } for (var i in obj.config.settings) { obj.args[i] = obj.config.settings[i]; } // Place all settings into arguments, arguments have already been placed into settings so arguments take precedence. @@ -199,7 +199,7 @@ function CreateMeshCentralServer(config, args) { obj.StartEx = function () { //var wincmd = require('node-windows'); //wincmd.list(function (svc) { console.log(svc); }, true); - + // Write the server state obj.updateServerState('state', 'starting'); @@ -265,12 +265,14 @@ function CreateMeshCentralServer(config, args) { if (obj.args.dbimport) { // Import the entire database from a JSON file if (obj.args.dbimport == true) { obj.args.dbimport = obj.getConfigFilePath('meshcentral.db.json'); } - var json = null, json2 = ""; - try { json = obj.fs.readFileSync(obj.args.dbimport); } catch (e) { console.log('Invalid JSON file: ' + obj.args.dbimport + '.'); process.exit(); } - for (var i = 0; i < json.length; i++) { if (json[i] >= 32) json2 += String.fromCharCode(json[i]); } // Remove all bad chars + var json = null, json2 = "", badCharCount = 0; + try { json = obj.fs.readFileSync(obj.args.dbimport, { encoding: 'utf8' }); } catch (e) { console.log('Invalid JSON file: ' + obj.args.dbimport + '.'); process.exit(); } + for (var i = 0; i < json.length; i++) { if (json.charCodeAt(i) >= 32) { json2 += json[i]; } else { var tt = json.charCodeAt(i); if (tt != 10 && tt != 13) { badCharCount++; } } } // Remove all bad chars + if (badCharCount > 0) { console.log(badCharCount + ' invalid character(s) where removed.'); } try { json = JSON.parse(json2); } catch (e) { console.log('Invalid JSON format: ' + obj.args.dbimport + ': ' + e); process.exit(); } if ((json == null) || (typeof json.length != 'number') || (json.length < 1)) { console.log('Invalid JSON format: ' + obj.args.dbimport + '.'); } for (var i in json) { if ((json[i].type == "mesh") && (json[i].links != null)) { for (var j in json[i].links) { var esc = obj.common.escapeFieldName(j); if (esc !== j) { json[i].links[esc] = json[i].links[j]; delete json[i].links[j]; } } } } // Escape MongoDB invalid field chars + //for (var i in json) { if ((json[i].type == "node") && (json[i].host != null)) { json[i].rname = json[i].host; delete json[i].host; } } // DEBUG: Change host to rname obj.db.RemoveAll(function () { obj.db.InsertMany(json, function (err) { if (err != null) { console.log(err); } else { console.log('Imported ' + json.length + ' objects(s) from ' + obj.args.dbimport + '.'); } process.exit(); }); }); return; } @@ -279,6 +281,42 @@ function CreateMeshCentralServer(config, args) { obj.db.clearOldEntries('event', 30); // Clear all event entires that are older than 30 days. obj.db.clearOldEntries('power', 10); // Clear all event entires that are older than 10 days. If a node is connected longer than 10 days, current power state will be used for everything. + // Setup a site administrator + if ((obj.args.admin) && (typeof obj.args.admin == 'string')) { + var adminname = obj.args.admin.split('/'); + if (adminname.length == 1) { adminname = 'user//' + adminname[0]; } + else if (adminname.length == 2) { adminname = 'user/' + adminname[0] + '/' + adminname[1]; } + else { console.log('Invalid administrator name.'); process.exit(); return; } + obj.db.Get(adminname, function (err, user) { + if (user.length != 1) { console.log('Invalid user name.'); process.exit(); return; } + user[0].siteadmin = 0xFFFFFFFF; + obj.db.Set(user[0], function () { + if (user[0].domain == '') { console.log('User ' + user[0].name + ' set to site administrator.'); } else { console.log('User ' + user[0].name + ' of domain ' + user[0].domain + ' set to site administrator.'); } + process.exit(); + return; + }); + }); + return; + } + + // Remove a site administrator + if ((obj.args.unadmin) && (typeof obj.args.unadmin == 'string')) { + var adminname = obj.args.unadmin.split('/'); + if (adminname.length == 1) { adminname = 'user//' + adminname[0]; } + else if (adminname.length == 2) { adminname = 'user/' + adminname[0] + '/' + adminname[1]; } + else { console.log('Invalid administrator name.'); process.exit(); return; } + obj.db.Get(adminname, function (err, user) { + if (user.length != 1) { console.log('Invalid user name.'); process.exit(); return; } + if (user[0].siteadmin) { delete user[0].siteadmin; } + obj.db.Set(user[0], function () { + if (user[0].domain == '') { console.log('User ' + user[0].name + ' is not a site administrator.'); } else { console.log('User ' + user[0].name + ' of domain ' + user[0].domain + ' is not a site administrator.'); } + process.exit(); + return; + }); + }); + return; + } + // Perform other database cleanup obj.db.cleanup(); diff --git a/meshuser.js b/meshuser.js index df05a571..5e4977fa 100644 --- a/meshuser.js +++ b/meshuser.js @@ -717,6 +717,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { if (command.amttls == '0') { command.amttls = 0; } else if (command.amttls == '1') { command.amttls = 1; } // Check TLS flag if ((command.amttls != 1) && (command.amttls != 0)) break; + // If we are in WAN-only mode, hostname is not used + if ((obj.parent.parent.args.wanonly == true) && (command.hostname)) { delete command.hostname; } + // Get the mesh var mesh = obj.parent.meshes[command.meshid]; if (mesh) { @@ -972,6 +975,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { var changes = [], change = 0, event = { etype: 'node', username: user.name, action: 'changenode', nodeid: node._id, domain: domain.id }; event.msg = ": "; + // If we are in WAN-only mode, host is not used + if ((obj.parent.parent.args.wanonly == true) && (command.host)) { delete command.host; } + // Look for a change if (command.icon && (command.icon != node.icon)) { change = 1; node.icon = command.icon; changes.push('icon'); } if (command.name && (command.name != node.name)) { change = 1; node.name = command.name; changes.push('name'); } diff --git a/public/styles/style.css b/public/styles/style.css index b483f563..f4575fd4 100644 --- a/public/styles/style.css +++ b/public/styles/style.css @@ -531,4 +531,10 @@ a { .deskToolsBar:hover { background-color: #EFE8B6; -} \ No newline at end of file +} + +.userTableHeader { + border-bottom: 1pt solid lightgray; + padding-top: 4px; + padding-bottom: 4px; +} diff --git a/sample-config.json b/sample-config.json index 907b8e97..6aec674b 100644 --- a/sample-config.json +++ b/sample-config.json @@ -8,7 +8,8 @@ "AllowLoginToken": true, "AllowFraming": true, "WebRTC": false, - "ClickOnce": false + "ClickOnce": false, + "UserAllowedIP" : "127.0.0.1,::1,192.168.0.100" }, "_domains": { "": { diff --git a/views/default.handlebars b/views/default.handlebars index 4a168596..50e50f5b 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -246,7 +246,10 @@
Online Users |
Online Users'; addHeader = false; } + x += addUserHtml(user, sessions); + maxUsers--; + } else { + hiddenUsers++; + } } } addHeader = true; @@ -5146,12 +5177,19 @@ for (var i in sortedUserIds) { var user = users[sortedUserIds[i]], sessions = null; if (wssessions != null) { sessions = wssessions[user._id]; } - if (sessions == null) { - if (addHeader) { x += ' |
Offline Users |
Offline Users'; addHeader = false; } + x += addUserHtml(user, sessions); + maxUsers--; + } else { + hiddenUsers++; + } } } x += ' |