Merge pull request #1 from Ylianst/master
updating before my new branch
This commit is contained in:
commit
fdc087dba9
|
@ -32,7 +32,6 @@
|
||||||
<Compile Include="agents\modules_meshcmd\amt-wsman.js" />
|
<Compile Include="agents\modules_meshcmd\amt-wsman.js" />
|
||||||
<Compile Include="agents\modules_meshcmd\amt-xml.js" />
|
<Compile Include="agents\modules_meshcmd\amt-xml.js" />
|
||||||
<Compile Include="agents\modules_meshcmd\amt.js" />
|
<Compile Include="agents\modules_meshcmd\amt.js" />
|
||||||
<Compile Include="agents\modules_meshcmd\process-manager.js" />
|
|
||||||
<Compile Include="agents\modules_meshcmd\service-host.js" />
|
<Compile Include="agents\modules_meshcmd\service-host.js" />
|
||||||
<Compile Include="agents\modules_meshcmd\service-manager.js" />
|
<Compile Include="agents\modules_meshcmd\service-manager.js" />
|
||||||
<Compile Include="agents\modules_meshcmd\smbios.js" />
|
<Compile Include="agents\modules_meshcmd\smbios.js" />
|
||||||
|
@ -58,21 +57,14 @@
|
||||||
<Compile Include="agents\modules_meshcore\amt-wsman.js" />
|
<Compile Include="agents\modules_meshcore\amt-wsman.js" />
|
||||||
<Compile Include="agents\modules_meshcore\amt-xml.js" />
|
<Compile Include="agents\modules_meshcore\amt-xml.js" />
|
||||||
<Compile Include="agents\modules_meshcore\amt.js" />
|
<Compile Include="agents\modules_meshcore\amt.js" />
|
||||||
<Compile Include="agents\modules_meshcore\clipboard.js" />
|
|
||||||
<Compile Include="agents\modules_meshcore\linux-dbus.js" />
|
<Compile Include="agents\modules_meshcore\linux-dbus.js" />
|
||||||
<Compile Include="agents\modules_meshcore\monitor-border.js" />
|
<Compile Include="agents\modules_meshcore\monitor-border.js" />
|
||||||
<Compile Include="agents\modules_meshcore\monitor-info.js" />
|
|
||||||
<Compile Include="agents\modules_meshcore\power-monitor.js" />
|
<Compile Include="agents\modules_meshcore\power-monitor.js" />
|
||||||
<Compile Include="agents\modules_meshcore\process-manager.js" />
|
|
||||||
<Compile Include="agents\modules_meshcore\service-manager.js" />
|
|
||||||
<Compile Include="agents\modules_meshcore\smbios.js" />
|
<Compile Include="agents\modules_meshcore\smbios.js" />
|
||||||
<Compile Include="agents\modules_meshcore\toaster.js" />
|
<Compile Include="agents\modules_meshcore\toaster.js" />
|
||||||
<Compile Include="agents\modules_meshcore\user-sessions.js" />
|
|
||||||
<Compile Include="agents\modules_meshcore\wifi-scanner-windows.js" />
|
<Compile Include="agents\modules_meshcore\wifi-scanner-windows.js" />
|
||||||
<Compile Include="agents\modules_meshcore\wifi-scanner.js" />
|
<Compile Include="agents\modules_meshcore\wifi-scanner.js" />
|
||||||
<Compile Include="agents\modules_meshcore\win-console.js" />
|
<Compile Include="agents\modules_meshcore\win-console.js" />
|
||||||
<Compile Include="agents\modules_meshcore\win-message-pump.js" />
|
|
||||||
<Compile Include="agents\modules_meshcore\win-registry.js" />
|
|
||||||
<Compile Include="agents\modules_meshcore\win-terminal.js" />
|
<Compile Include="agents\modules_meshcore\win-terminal.js" />
|
||||||
<Compile Include="agents\modules_meshcore_min\amt-lme.min.js" />
|
<Compile Include="agents\modules_meshcore_min\amt-lme.min.js" />
|
||||||
<Compile Include="agents\modules_meshcore_min\amt-mei.min.js" />
|
<Compile Include="agents\modules_meshcore_min\amt-mei.min.js" />
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -54,12 +54,12 @@ var Small_IntelAmtWebApp = "H4sIAAAAAAAEAHq/e7+Noou/c0hkgCuA0+rcdhMHwq/CcXohAVwD
|
||||||
// Check the server certificate fingerprint
|
// Check the server certificate fingerprint
|
||||||
function onVerifyServer(clientName, certs) {
|
function onVerifyServer(clientName, certs) {
|
||||||
if (certs == null) { certs = clientName; } // Temporary thing until we fix duktape
|
if (certs == null) { certs = clientName; } // Temporary thing until we fix duktape
|
||||||
try { for (var i in certs) { if (certs[i].fingerprint.replace(/:/g, '') == settings.serverHttpsHash) { return; } } } catch (e) { }
|
try { for (var i in certs) { if (certs[i].fingerprint.replace(/:/g, '') == settings.serverhttpshash) { return; } } } catch (e) { }
|
||||||
if (serverhash != null) { console.log('Error: Failed to verify server certificate.'); throw 'Invalid server certificate'; }
|
if (serverhash != null) { console.log('Error: Failed to verify server certificate.'); throw 'Invalid server certificate'; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Various utility functions
|
// Various utility functions
|
||||||
function debug(level, message) { if ((settings.debugLevel != null) && (settings.debugLevel >= level)) { console.log(message); } }
|
function debug(level, message) { if ((settings.debuglevel != null) && (settings.debuglevel >= level)) { console.log(message); } }
|
||||||
function exit(status) { if (status == null) { status = 0; } try { process.exit(status); } catch (e) { } }
|
function exit(status) { if (status == null) { status = 0; } try { process.exit(status); } catch (e) { } }
|
||||||
function getInstance(x, y) { for (var i in x) { if (x[i]["InstanceID"] == y) return x[i]; } return null; }
|
function getInstance(x, y) { for (var i in x) { if (x[i]["InstanceID"] == y) return x[i]; } return null; }
|
||||||
function md5hex(str) { return require('MD5Stream').create().syncHash(str).toString('hex'); }
|
function md5hex(str) { return require('MD5Stream').create().syncHash(str).toString('hex'); }
|
||||||
|
@ -102,25 +102,27 @@ function run(argv) {
|
||||||
if ((actionpath != 'meshaction.txt') && (actionfile == null)) { console.log('Unable to load \"' + actionpath + '\". Create this file or specify the location using --actionfile [filename].'); exit(1); return; }
|
if ((actionpath != 'meshaction.txt') && (actionfile == null)) { console.log('Unable to load \"' + actionpath + '\". Create this file or specify the location using --actionfile [filename].'); exit(1); return; }
|
||||||
if (actionfile != null) { try { settings = JSON.parse(actionfile); } catch (e) { console.log(actionpath, e); exit(1); return; } } else { if (argv.length >= 2) { settings = { action: argv[1] } } }
|
if (actionfile != null) { try { settings = JSON.parse(actionfile); } catch (e) { console.log(actionpath, e); exit(1); return; } } else { if (argv.length >= 2) { settings = { action: argv[1] } } }
|
||||||
if (settings == null) { settings = {}; }
|
if (settings == null) { settings = {}; }
|
||||||
|
var settings2 = {}; for (var i in settings) { settings2[i.toLowerCase()] = settings[i]; } settings = settings2;
|
||||||
|
|
||||||
// Set the arguments
|
// Set the arguments
|
||||||
if ((typeof args.action) == 'string') { settings.action = args.action; }
|
if ((typeof args.action) == 'string') { settings.action = args.action; }
|
||||||
if ((typeof args.localport) == 'string') { settings.localport = parseInt(args.localport); }
|
if ((typeof args.localport) == 'string') { settings.localport = parseInt(args.localport); }
|
||||||
if ((typeof args.remotenodeid) == 'string') { settings.remoteNodeId = args.remotenodeid; }
|
if ((typeof args.remotenodeid) == 'string') { settings.remotenodeid = args.remotenodeid; }
|
||||||
if ((typeof args.username) == 'string') { settings.username = args.username; }
|
if ((typeof args.username) == 'string') { settings.username = args.username; }
|
||||||
if ((typeof args.password) == 'string') { settings.password = args.password; }
|
if ((typeof args.password) == 'string') { settings.password = args.password; }
|
||||||
if ((typeof args.user) == 'string') { settings.username = args.user; }
|
if ((typeof args.user) == 'string') { settings.username = args.user; }
|
||||||
if ((typeof args.pass) == 'string') { settings.password = args.pass; }
|
if ((typeof args.pass) == 'string') { settings.password = args.pass; }
|
||||||
if ((typeof args.host) == 'string') { settings.hostname = args.host; }
|
if ((typeof args.host) == 'string') { settings.hostname = args.host; }
|
||||||
if ((typeof args.hostname) == 'string') { settings.hostname = args.hostname; }
|
if ((typeof args.hostname) == 'string') { settings.hostname = args.hostname; }
|
||||||
if ((typeof args.serverid) == 'string') { settings.serverId = args.serverid; }
|
if ((typeof args.serverid) == 'string') { settings.serverid = args.serverid; }
|
||||||
if ((typeof args.serverhttpshash) == 'string') { settings.serverHttpsHash = args.serverhttpshash; }
|
if ((typeof args.serverhttpshash) == 'string') { settings.serverhttpshash = args.serverhttpshash; }
|
||||||
if ((typeof args.remoteport) == 'string') { settings.remotePort = parseInt(args.remoteport); }
|
if ((typeof args.remoteport) == 'string') { settings.remoteport = parseInt(args.remoteport); }
|
||||||
if ((typeof args.out) == 'string') { settings.output = args.out; }
|
if ((typeof args.out) == 'string') { settings.output = args.out; }
|
||||||
if ((typeof args.output) == 'string') { settings.output = args.output; }
|
if ((typeof args.output) == 'string') { settings.output = args.output; }
|
||||||
if ((typeof args.debug) == 'string') { settings.debugLevel = parseInt(args.debug); }
|
if ((typeof args.debug) == 'string') { settings.debuglevel = parseInt(args.debug); }
|
||||||
if ((typeof args.script) == 'string') { settings.script = args.script; }
|
if ((typeof args.script) == 'string') { settings.script = args.script; }
|
||||||
if ((typeof args.agent) == 'string') { settings.agent = args.agent; }
|
if ((typeof args.agent) == 'string') { settings.agent = args.agent; }
|
||||||
|
if ((typeof args.proxy) == 'string') { settings.proxy = args.proxy; }
|
||||||
if (args.debug) { try { waitForDebugger(); } catch (e) { } }
|
if (args.debug) { try { waitForDebugger(); } catch (e) { } }
|
||||||
if (args.noconsole) { settings.noconsole = true; }
|
if (args.noconsole) { settings.noconsole = true; }
|
||||||
if (args.nocommander) { settings.noconsole = true; }
|
if (args.nocommander) { settings.noconsole = true; }
|
||||||
|
@ -263,6 +265,15 @@ function run(argv) {
|
||||||
}
|
}
|
||||||
settings.action = settings.action.toLowerCase();
|
settings.action = settings.action.toLowerCase();
|
||||||
debug(1, "Settings: " + JSON.stringify(settings));
|
debug(1, "Settings: " + JSON.stringify(settings));
|
||||||
|
|
||||||
|
// Setup the proxy if needed
|
||||||
|
if ((typeof settings.proxy) == 'string') {
|
||||||
|
var proxy = settings.proxy.split(':'), proxyport = (proxy.length == 2) ? parseInt(proxy[1]) : 0;
|
||||||
|
if ((proxy.length != 2) || (proxy[0].length < 1) || (proxyport < 1) || (proxyport > 65535)) { console.log('Invalid \"proxy\" specified, use --proxy [hostname]:[port].'); exit(1); return; }
|
||||||
|
try { require('global-tunnel').initialize({ host: proxy[0], port: proxyport }); } catch (ex) { console.log(ex); exit(1); return; }
|
||||||
|
console.log('Proxy set to ' + proxy[0] + ':' + proxyport);
|
||||||
|
}
|
||||||
|
|
||||||
if (settings.action == 'smbios') {
|
if (settings.action == 'smbios') {
|
||||||
// Display SM BIOS tables in raw form
|
// Display SM BIOS tables in raw form
|
||||||
SMBiosTables = require('smbios');
|
SMBiosTables = require('smbios');
|
||||||
|
@ -283,14 +294,14 @@ function run(argv) {
|
||||||
});
|
});
|
||||||
} else if (settings.action == 'route') {
|
} else if (settings.action == 'route') {
|
||||||
// MeshCentral Router, port map local TCP port to a remote computer
|
// MeshCentral Router, port map local TCP port to a remote computer
|
||||||
if ((settings.localPort == null) || (typeof settings.localPort != 'number') || (settings.localPort < 0) || (settings.localPort > 65535)) { console.log('No or invalid \"localPort\" specified, use --localport [localport].'); exit(1); return; }
|
if ((settings.localport == null) || (typeof settings.localport != 'number') || (settings.localport < 0) || (settings.localport > 65535)) { console.log('No or invalid \"localPort\" specified, use --localport [localport].'); exit(1); return; }
|
||||||
if ((settings.remoteNodeId == null) || (typeof settings.remoteNodeId != 'string')) { console.log('No or invalid \"remoteNodeId\" specified.'); exit(1); return; }
|
if ((settings.remotenodeid == null) || (typeof settings.remotenodeid != 'string')) { console.log('No or invalid \"remoteNodeId\" specified.'); exit(1); return; }
|
||||||
if ((settings.username == null) || (typeof settings.username != 'string') || (settings.username == '')) { console.log('No or invalid \"username\" specified, use --username [username].'); exit(1); return; }
|
if ((settings.username == null) || (typeof settings.username != 'string') || (settings.username == '')) { console.log('No or invalid \"username\" specified, use --username [username].'); exit(1); return; }
|
||||||
if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified, use --password [password].'); exit(1); return; }
|
if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified, use --password [password].'); exit(1); return; }
|
||||||
if ((settings.serverId == null) || (typeof settings.serverId != 'string') || (settings.serverId.length != 96)) { console.log('No or invalid \"serverId\" specified.'); exit(1); return; }
|
if ((settings.serverid == null) || (typeof settings.serverid != 'string') || (settings.serverid.length != 96)) { console.log('No or invalid \"serverId\" specified.'); exit(1); return; }
|
||||||
if ((settings.serverHttpsHash == null) || (typeof settings.serverHttpsHash != 'string') || (settings.serverHttpsHash.length != 96)) { console.log('No or invalid \"serverHttpsHash\" specified.'); exit(1); return; }
|
if ((settings.serverhttpshash == null) || (typeof settings.serverhttpshash != 'string') || (settings.serverhttpshash.length != 96)) { console.log('No or invalid \"serverHttpsHash\" specified.'); exit(1); return; }
|
||||||
if ((settings.remotePort == null) || (typeof settings.remotePort != 'number') || (settings.remotePort < 0) || (settings.remotePort > 65535)) { console.log('No or invalid \"remotePort\" specified, use --remoteport [remoteport].'); exit(1); return; }
|
if ((settings.remoteport == null) || (typeof settings.remoteport != 'number') || (settings.remoteport < 0) || (settings.remoteport > 65535)) { console.log('No or invalid \"remotePort\" specified, use --remoteport [remoteport].'); exit(1); return; }
|
||||||
if (settings.serverUrl != null) { startRouter(); } else { discoverMeshServer(); } // Start MeshCentral Router
|
if (settings.serverurl != null) { startRouter(); } else { discoverMeshServer(); } // Start MeshCentral Router
|
||||||
} else if ((settings.action == 'amtloadwebapp') || (settings.action == 'amtloadsmallwebapp') || (settings.action == 'amtloadlargewebapp') || (settings.action == 'amtclearwebapp') || (settings.action == 'amtstoragestate')) { // Intel AMT Web Application Actions
|
} else if ((settings.action == 'amtloadwebapp') || (settings.action == 'amtloadsmallwebapp') || (settings.action == 'amtloadlargewebapp') || (settings.action == 'amtclearwebapp') || (settings.action == 'amtstoragestate')) { // Intel AMT Web Application Actions
|
||||||
// Intel AMT 11.6+ Load MeshCommander into firmware
|
// Intel AMT 11.6+ Load MeshCommander into firmware
|
||||||
if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified, use --password [password].'); exit(1); return; }
|
if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified, use --password [password].'); exit(1); return; }
|
||||||
|
@ -417,7 +428,7 @@ function run(argv) {
|
||||||
if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified, use --password [password].'); exit(1); return; }
|
if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified, use --password [password].'); exit(1); return; }
|
||||||
if ((settings.hostname == null) || (typeof settings.hostname != 'string') || (settings.hostname == '')) { settings.hostname = '127.0.0.1'; }
|
if ((settings.hostname == null) || (typeof settings.hostname != 'string') || (settings.hostname == '')) { settings.hostname = '127.0.0.1'; }
|
||||||
if ((settings.username == null) || (typeof settings.username != 'string') || (settings.username == '')) { settings.username = 'admin'; }
|
if ((settings.username == null) || (typeof settings.username != 'string') || (settings.username == '')) { settings.username = 'admin'; }
|
||||||
if ((settings.script == null) || (typeof settings.script != 'string') || (settings.script == '')) { if (mescriptJSON != '') { settings.scriptJSON = mescriptJSON; } else { console.log('No or invalid \"script\" file specified, use --script [filename].'); exit(1); return; } }
|
if ((settings.script == null) || (typeof settings.script != 'string') || (settings.script == '')) { if (mescriptJSON != '') { settings.scriptjson = mescriptJSON; } else { console.log('No or invalid \"script\" file specified, use --script [filename].'); exit(1); return; } }
|
||||||
startMeScript();
|
startMeScript();
|
||||||
} else if (settings.action == 'amtuuid') {
|
} else if (settings.action == 'amtuuid') {
|
||||||
// Start running
|
// Start running
|
||||||
|
@ -751,7 +762,7 @@ function startMeScriptEx() {
|
||||||
if (settings.script != null) {
|
if (settings.script != null) {
|
||||||
try { scriptData = fs.readFileSync(settings.script); } catch (e) { console.log('Unable to read script file (1): ' + settings.script + '.'); exit(1); return; }
|
try { scriptData = fs.readFileSync(settings.script); } catch (e) { console.log('Unable to read script file (1): ' + settings.script + '.'); exit(1); return; }
|
||||||
} else {
|
} else {
|
||||||
scriptData = settings.scriptJSON;
|
scriptData = settings.scriptjson;
|
||||||
}
|
}
|
||||||
if (scriptData == null) { console.log('Unable to read script file (2): ' + settings.script + '.'); exit(1); return; }
|
if (scriptData == null) { console.log('Unable to read script file (2): ' + settings.script + '.'); exit(1); return; }
|
||||||
try { scriptData = JSON.parse(scriptData); } catch (e) { console.log('Unable to read script file (3): ' + settings.script + '.'); exit(1); return; }
|
try { scriptData = JSON.parse(scriptData); } catch (e) { console.log('Unable to read script file (3): ' + settings.script + '.'); exit(1); return; }
|
||||||
|
@ -1362,12 +1373,12 @@ function processLmsControlData(data) {
|
||||||
function startRouter() {
|
function startRouter() {
|
||||||
tcpserver = net.createServer(OnTcpClientConnected);
|
tcpserver = net.createServer(OnTcpClientConnected);
|
||||||
tcpserver.on('error', function (e) { console.log('ERROR: ' + JSON.stringify(e)); exit(0); return; });
|
tcpserver.on('error', function (e) { console.log('ERROR: ' + JSON.stringify(e)); exit(0); return; });
|
||||||
tcpserver.listen(settings.localPort, function () {
|
tcpserver.listen(settings.localport, function () {
|
||||||
// We started listening.
|
// We started listening.
|
||||||
if (settings.remoteName == null) {
|
if (settings.remotename == null) {
|
||||||
console.log('Redirecting local port ' + settings.localPort + ' to remote port ' + settings.remotePort + '.');
|
console.log('Redirecting local port ' + settings.localport + ' to remote port ' + settings.remoteport + '.');
|
||||||
} else {
|
} else {
|
||||||
console.log('Redirecting local port ' + settings.localPort + ' to ' + settings.remoteName + ':' + settings.remotePort + '.');
|
console.log('Redirecting local port ' + settings.localport + ' to ' + settings.remotename + ':' + settings.remoteport + '.');
|
||||||
}
|
}
|
||||||
console.log('Press ctrl-c to exit.');
|
console.log('Press ctrl-c to exit.');
|
||||||
|
|
||||||
|
@ -1384,7 +1395,7 @@ function OnTcpClientConnected(c) {
|
||||||
c.on('end', function () { disconnectTunnel(this, this.websocket, 'Client closed'); });
|
c.on('end', function () { disconnectTunnel(this, this.websocket, 'Client closed'); });
|
||||||
c.pause();
|
c.pause();
|
||||||
try {
|
try {
|
||||||
options = http.parseUri(settings.serverUrl + '?user=' + settings.username + '&pass=' + settings.password + '&nodeid=' + settings.remoteNodeId + '&tcpport=' + settings.remotePort);
|
options = http.parseUri(settings.serverurl + '?user=' + settings.username + '&pass=' + settings.password + '&nodeid=' + settings.remotenodeid + '&tcpport=' + settings.remoteport);
|
||||||
} catch (e) { console.log('Unable to parse \"serverUrl\".'); process.exit(1); return; }
|
} catch (e) { console.log('Unable to parse \"serverUrl\".'); process.exit(1); return; }
|
||||||
options.checkServerIdentity = onVerifyServer;
|
options.checkServerIdentity = onVerifyServer;
|
||||||
options.rejectUnauthorized = false;
|
options.rejectUnauthorized = false;
|
||||||
|
@ -1440,7 +1451,7 @@ function discoverMeshServerOnce() {
|
||||||
multicastSockets[i].addMembership(membershipIPv4);
|
multicastSockets[i].addMembership(membershipIPv4);
|
||||||
//multicastSockets[i].setMulticastLoopback(true);
|
//multicastSockets[i].setMulticastLoopback(true);
|
||||||
multicastSockets[i].once('message', OnMulticastMessage);
|
multicastSockets[i].once('message', OnMulticastMessage);
|
||||||
multicastSockets[i].send(settings.serverId, 16989, membershipIPv4);
|
multicastSockets[i].send(settings.serverid, 16989, membershipIPv4);
|
||||||
} catch (e) { }
|
} catch (e) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1451,9 +1462,9 @@ function discoverMeshServerOnce() {
|
||||||
// Called when a multicast packet is received
|
// Called when a multicast packet is received
|
||||||
function OnMulticastMessage(msg, rinfo) {
|
function OnMulticastMessage(msg, rinfo) {
|
||||||
var m = msg.toString().split('|');
|
var m = msg.toString().split('|');
|
||||||
if ((m.length == 3) && (m[0] == 'MeshCentral2') && (m[1] == settings.serverId)) {
|
if ((m.length == 3) && (m[0] == 'MeshCentral2') && (m[1] == settings.serverid)) {
|
||||||
settings.serverUrl = m[2].replace('%s', rinfo.address).replace('/agent.ashx', '/meshrelay.ashx');
|
settings.serverurl = m[2].replace('%s', rinfo.address).replace('/agent.ashx', '/meshrelay.ashx');
|
||||||
console.log('Found server at ' + settings.serverUrl + '.');
|
console.log('Found server at ' + settings.serverurl + '.');
|
||||||
if (discoveryInterval != null) { clearInterval(discoveryInterval); discoveryInterval = null; }
|
if (discoveryInterval != null) { clearInterval(discoveryInterval); discoveryInterval = null; }
|
||||||
startRouter();
|
startRouter();
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -21,6 +21,7 @@ start() {
|
||||||
fi
|
fi
|
||||||
echo 'Starting service…' >&2
|
echo 'Starting service…' >&2
|
||||||
local CMD="$SCRIPT &> \"$LOGFILE\" & echo \$!"
|
local CMD="$SCRIPT &> \"$LOGFILE\" & echo \$!"
|
||||||
|
cd /usr/local/mesh
|
||||||
su -c "$CMD" $RUNAS > "$PIDFILE"
|
su -c "$CMD" $RUNAS > "$PIDFILE"
|
||||||
echo 'Service started' >&2
|
echo 'Service started' >&2
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,65 +121,68 @@ function _getChildElementsByTagName(name) { var ret = []; if (this.childNodes !=
|
||||||
function _getChildElementsByTagNameNS(ns, name) { var ret = []; if (this.childNodes != null) { for (var node in this.childNodes) { if (this.childNodes[node].localName == name && (ns == '*' || this.childNodes[node].namespace == ns)) { ret.push(this.childNodes[node]); } } } return (ret); }
|
function _getChildElementsByTagNameNS(ns, name) { var ret = []; if (this.childNodes != null) { for (var node in this.childNodes) { if (this.childNodes[node].localName == name && (ns == '*' || this.childNodes[node].namespace == ns)) { ret.push(this.childNodes[node]); } } } return (ret); }
|
||||||
function _xmlTraverseAllRec(nodes, func) { for (var i in nodes) { func(nodes[i]); if (nodes[i].childNodes) { _xmlTraverseAllRec(nodes[i].childNodes, func); } } }
|
function _xmlTraverseAllRec(nodes, func) { for (var i in nodes) { func(nodes[i]); if (nodes[i].childNodes) { _xmlTraverseAllRec(nodes[i].childNodes, func); } } }
|
||||||
function _turnToXmlRec(text) {
|
function _turnToXmlRec(text) {
|
||||||
var elementStack = new _treeBuilder(), lastElement = null, x1 = text.split('<'), ret = [], element = null, currentElementName = null;
|
try {
|
||||||
for (var i in x1) {
|
if (text == null) return null;
|
||||||
var x2 = x1[i].split('>'), x3 = x2[0].split(' '), elementName = x3[0];
|
var elementStack = new _treeBuilder(), lastElement = null, x1 = text.split('<'), ret = [], element = null, currentElementName = null;
|
||||||
if ((elementName.length > 0) && (elementName[0] != '?')) {
|
for (var i in x1) {
|
||||||
if (elementName[0] != '/') {
|
var x2 = x1[i].split('>'), x3 = x2[0].split(' '), elementName = x3[0];
|
||||||
var attributes = [], localName, localname2 = elementName.split(' ')[0].split(':'), localName = (localname2.length > 1) ? localname2[1] : localname2[0];
|
if ((elementName.length > 0) && (elementName[0] != '?')) {
|
||||||
Object.defineProperty(attributes, "get",
|
if (elementName[0] != '/') {
|
||||||
{
|
var attributes = [], localName, localname2 = elementName.split(' ')[0].split(':'), localName = (localname2.length > 1) ? localname2[1] : localname2[0];
|
||||||
value: function () {
|
Object.defineProperty(attributes, "get",
|
||||||
if (arguments.length == 1) {
|
{
|
||||||
for (var a in this) { if (this[a].name == arguments[0]) { return (this[a]); } }
|
value: function () {
|
||||||
}
|
if (arguments.length == 1) {
|
||||||
else if (arguments.length == 2) {
|
for (var a in this) { if (this[a].name == arguments[0]) { return (this[a]); } }
|
||||||
for (var a in this) { if (this[a].name == arguments[1] && (arguments[0] == '*' || this[a].namespace == arguments[0])) { return (this[a]); } }
|
}
|
||||||
}
|
else if (arguments.length == 2) {
|
||||||
else {
|
for (var a in this) { if (this[a].name == arguments[1] && (arguments[0] == '*' || this[a].namespace == arguments[0])) { return (this[a]); } }
|
||||||
throw ('attributes.get(): Invalid number of parameters');
|
}
|
||||||
}
|
else {
|
||||||
}
|
throw ('attributes.get(): Invalid number of parameters');
|
||||||
});
|
}
|
||||||
elementStack.push({ name: elementName, localName: localName, getChildElementsByTagName: _getChildElementsByTagName, getElementsByTagNameNS: _getElementsByTagNameNS, getChildElementsByTagNameNS: _getChildElementsByTagNameNS, attributes: attributes, childNodes: [], nsTable: {} });
|
}
|
||||||
// Parse Attributes
|
});
|
||||||
if (x3.length > 0) {
|
elementStack.push({ name: elementName, localName: localName, getChildElementsByTagName: _getChildElementsByTagName, getElementsByTagNameNS: _getElementsByTagNameNS, getChildElementsByTagNameNS: _getChildElementsByTagNameNS, attributes: attributes, childNodes: [], nsTable: {} });
|
||||||
var skip = false;
|
// Parse Attributes
|
||||||
for (var j in x3) {
|
if (x3.length > 0) {
|
||||||
if (x3[j] == '/') {
|
var skip = false;
|
||||||
// This is an empty Element
|
for (var j in x3) {
|
||||||
elementStack.peek().namespace = elementStack.peek().name == elementStack.peek().localName ? elementStack.getNamespace('*') : elementStack.getNamespace(elementStack.peek().name.substring(0, elementStack.peek().name.indexOf(':')));
|
if (x3[j] == '/') {
|
||||||
elementStack.peek().textContent = '';
|
// This is an empty Element
|
||||||
lastElement = elementStack.pop();
|
elementStack.peek().namespace = elementStack.peek().name == elementStack.peek().localName ? elementStack.getNamespace('*') : elementStack.getNamespace(elementStack.peek().name.substring(0, elementStack.peek().name.indexOf(':')));
|
||||||
skip = true;
|
elementStack.peek().textContent = '';
|
||||||
break;
|
lastElement = elementStack.pop();
|
||||||
}
|
skip = true;
|
||||||
var k = x3[j].indexOf('=');
|
break;
|
||||||
if (k > 0) {
|
}
|
||||||
var attrName = x3[j].substring(0, k);
|
var k = x3[j].indexOf('=');
|
||||||
var attrValue = x3[j].substring(k + 2, x3[j].length - 1);
|
if (k > 0) {
|
||||||
var attrNS = elementStack.getNamespace('*');
|
var attrName = x3[j].substring(0, k);
|
||||||
|
var attrValue = x3[j].substring(k + 2, x3[j].length - 1);
|
||||||
if (attrName == 'xmlns') {
|
var attrNS = elementStack.getNamespace('*');
|
||||||
elementStack.addNamespace('*', attrValue);
|
|
||||||
attrNS = attrValue;
|
if (attrName == 'xmlns') {
|
||||||
} else if (attrName.startsWith('xmlns:')) {
|
elementStack.addNamespace('*', attrValue);
|
||||||
elementStack.addNamespace(attrName.substring(6), attrValue);
|
attrNS = attrValue;
|
||||||
} else {
|
} else if (attrName.startsWith('xmlns:')) {
|
||||||
var ax = attrName.split(':');
|
elementStack.addNamespace(attrName.substring(6), attrValue);
|
||||||
if (ax.length == 2) { attrName = ax[1]; attrNS = elementStack.getNamespace(ax[0]); }
|
} else {
|
||||||
|
var ax = attrName.split(':');
|
||||||
|
if (ax.length == 2) { attrName = ax[1]; attrNS = elementStack.getNamespace(ax[0]); }
|
||||||
|
}
|
||||||
|
var x = { name: attrName, value: attrValue }
|
||||||
|
if (attrNS != null) x.namespace = attrNS;
|
||||||
|
elementStack.peek().attributes.push(x);
|
||||||
}
|
}
|
||||||
var x = { name: attrName, value: attrValue }
|
|
||||||
if (attrNS != null) x.namespace = attrNS;
|
|
||||||
elementStack.peek().attributes.push(x);
|
|
||||||
}
|
}
|
||||||
|
if (skip) { continue; }
|
||||||
}
|
}
|
||||||
if (skip) { continue; }
|
elementStack.peek().namespace = elementStack.peek().name == elementStack.peek().localName ? elementStack.getNamespace('*') : elementStack.getNamespace(elementStack.peek().name.substring(0, elementStack.peek().name.indexOf(':')));
|
||||||
}
|
if (x2[1]) { elementStack.peek().textContent = x2[1]; }
|
||||||
elementStack.peek().namespace = elementStack.peek().name == elementStack.peek().localName ? elementStack.getNamespace('*') : elementStack.getNamespace(elementStack.peek().name.substring(0, elementStack.peek().name.indexOf(':')));
|
} else { lastElement = elementStack.pop(); }
|
||||||
if (x2[1]) { elementStack.peek().textContent = x2[1]; }
|
}
|
||||||
} else { lastElement = elementStack.pop(); }
|
|
||||||
}
|
}
|
||||||
}
|
} catch (ex) { return null; }
|
||||||
return lastElement;
|
return lastElement;
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -492,7 +492,7 @@ function AmtManager(agent, db, isdebug) {
|
||||||
var intelAmtAdminPass, wsstack, amtstack, applyPolicyTimer, policyWsmanRetry = 0;
|
var intelAmtAdminPass, wsstack, amtstack, applyPolicyTimer, policyWsmanRetry = 0;
|
||||||
obj.applyPolicy = function () {
|
obj.applyPolicy = function () {
|
||||||
applyPolicyTimer = null;
|
applyPolicyTimer = null;
|
||||||
if ((amtMeiState != 3) || (typeof amtpolicy != 'object') || (typeof amtpolicy.type != 'number') || (amtpolicy.type == 0)) return;
|
if ((amtMeiState != 3) || (amtpolicy == null) || (typeof amtpolicy != 'object') || (typeof amtpolicy.type != 'number') || (amtpolicy.type == 0)) return;
|
||||||
if ((amtpolicy.password != null) && (amtpolicy.password != '')) { intelAmtAdminPass = amtpolicy.password; }
|
if ((amtpolicy.password != null) && (amtpolicy.password != '')) { intelAmtAdminPass = amtpolicy.password; }
|
||||||
obj.getAmtInfo(function (meinfo) {
|
obj.getAmtInfo(function (meinfo) {
|
||||||
if ((amtpolicy.type == 1) && (meinfo.ProvisioningState == 2) && ((meinfo.Flags & 2) != 0)) {
|
if ((amtpolicy.type == 1) && (meinfo.ProvisioningState == 2) && ((meinfo.Flags & 2) != 0)) {
|
||||||
|
|
|
@ -121,6 +121,7 @@ function _getChildElementsByTagName(name) { var ret = []; if (this.childNodes !=
|
||||||
function _getChildElementsByTagNameNS(ns, name) { var ret = []; if (this.childNodes != null) { for (var node in this.childNodes) { if (this.childNodes[node].localName == name && (ns == '*' || this.childNodes[node].namespace == ns)) { ret.push(this.childNodes[node]); } } } return (ret); }
|
function _getChildElementsByTagNameNS(ns, name) { var ret = []; if (this.childNodes != null) { for (var node in this.childNodes) { if (this.childNodes[node].localName == name && (ns == '*' || this.childNodes[node].namespace == ns)) { ret.push(this.childNodes[node]); } } } return (ret); }
|
||||||
function _xmlTraverseAllRec(nodes, func) { for (var i in nodes) { func(nodes[i]); if (nodes[i].childNodes) { _xmlTraverseAllRec(nodes[i].childNodes, func); } } }
|
function _xmlTraverseAllRec(nodes, func) { for (var i in nodes) { func(nodes[i]); if (nodes[i].childNodes) { _xmlTraverseAllRec(nodes[i].childNodes, func); } } }
|
||||||
function _turnToXmlRec(text) {
|
function _turnToXmlRec(text) {
|
||||||
|
if (text == null) return null;
|
||||||
var elementStack = new _treeBuilder(), lastElement = null, x1 = text.split('<'), ret = [], element = null, currentElementName = null;
|
var elementStack = new _treeBuilder(), lastElement = null, x1 = text.split('<'), ret = [], element = null, currentElementName = null;
|
||||||
for (var i in x1) {
|
for (var i in x1) {
|
||||||
var x2 = x1[i].split('>'), x3 = x2[0].split(' '), elementName = x3[0];
|
var x2 = x1[i].split('>'), x3 = x2[0].split(' '), elementName = x3[0];
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
38
db.js
38
db.js
|
@ -30,6 +30,7 @@ module.exports.CreateDB = function (parent) {
|
||||||
var Datastore = null;
|
var Datastore = null;
|
||||||
var expireEventsSeconds = (60 * 60 * 24 * 20); // By default, expire events after 20 days. (Seconds * Minutes * Hours * Days)
|
var expireEventsSeconds = (60 * 60 * 24 * 20); // By default, expire events after 20 days. (Seconds * Minutes * Hours * Days)
|
||||||
var expirePowerEventsSeconds = (60 * 60 * 24 * 10); // By default, expire power events after 10 days. (Seconds * Minutes * Hours * Days)
|
var expirePowerEventsSeconds = (60 * 60 * 24 * 10); // By default, expire power events after 10 days. (Seconds * Minutes * Hours * Days)
|
||||||
|
var expireServerStatsSeconds = (60 * 60 * 24 * 30); // By default, expire power events after 30 days. (Seconds * Minutes * Hours * Days)
|
||||||
obj.path = require('path');
|
obj.path = require('path');
|
||||||
obj.parent = parent;
|
obj.parent = parent;
|
||||||
obj.identifier = null;
|
obj.identifier = null;
|
||||||
|
@ -39,6 +40,7 @@ module.exports.CreateDB = function (parent) {
|
||||||
if (typeof obj.parent.args.dbexpire == 'object') {
|
if (typeof obj.parent.args.dbexpire == 'object') {
|
||||||
if (typeof obj.parent.args.dbexpire.events == 'number') { expireEventsSeconds = obj.parent.args.dbexpire.events; }
|
if (typeof obj.parent.args.dbexpire.events == 'number') { expireEventsSeconds = obj.parent.args.dbexpire.events; }
|
||||||
if (typeof obj.parent.args.dbexpire.powerevents == 'number') { expirePowerEventsSeconds = obj.parent.args.dbexpire.powerevents; }
|
if (typeof obj.parent.args.dbexpire.powerevents == 'number') { expirePowerEventsSeconds = obj.parent.args.dbexpire.powerevents; }
|
||||||
|
if (typeof obj.parent.args.dbexpire.statsevents == 'number') { expireServerStatsSeconds = obj.parent.args.dbexpire.statsevents; }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj.parent.args.mongodb) {
|
if (obj.parent.args.mongodb) {
|
||||||
|
@ -115,6 +117,30 @@ module.exports.CreateDB = function (parent) {
|
||||||
|
|
||||||
// Setup MongoDB smbios collection, no indexes needed
|
// Setup MongoDB smbios collection, no indexes needed
|
||||||
obj.smbiosfile = db.collection('smbios'); // Collection containing all smbios information
|
obj.smbiosfile = db.collection('smbios'); // Collection containing all smbios information
|
||||||
|
|
||||||
|
// Setup MongoDB server stats collection
|
||||||
|
obj.serverstatsfile = db.collection('serverstats'); // Collection of server stats
|
||||||
|
obj.serverstatsfile.getIndexes(function (err, indexes) {
|
||||||
|
// Check if we need to reset indexes
|
||||||
|
var indexesByName = {}, indexCount = 0;
|
||||||
|
for (var i in indexes) { indexesByName[indexes[i].name] = indexes[i]; indexCount++; }
|
||||||
|
if ((indexCount != 3) || (indexesByName['ExpireTime1'] == null)) {
|
||||||
|
// Reset all indexes
|
||||||
|
console.log('Resetting server stats indexes...');
|
||||||
|
obj.serverstatsfile.dropIndexes(function (err) {
|
||||||
|
// Create all indexes
|
||||||
|
obj.serverstatsfile.createIndex({ "time": 1 }, { expireAfterSeconds: expireServerStatsSeconds, name: 'ExpireTime1' });
|
||||||
|
obj.serverstatsfile.createIndex({ "expire": 1 }, { expireAfterSeconds: 0, name: 'ExpireTime2' }); // Auto-expire events
|
||||||
|
});
|
||||||
|
} else if (indexesByName['ExpireTime1'].expireAfterSeconds != expireServerStatsSeconds) {
|
||||||
|
// Reset the timeout index
|
||||||
|
console.log('Resetting server stats expire index...');
|
||||||
|
obj.serverstatsfile.dropIndex("ExpireTime1", function (err) {
|
||||||
|
// Reset the expire server stats index
|
||||||
|
obj.serverstatsfile.createIndex({ "time": 1 }, { expireAfterSeconds: expireServerStatsSeconds, name: 'ExpireTime1' });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// Use NeDB (The default)
|
// Use NeDB (The default)
|
||||||
obj.databaseType = 1;
|
obj.databaseType = 1;
|
||||||
|
@ -167,6 +193,12 @@ module.exports.CreateDB = function (parent) {
|
||||||
|
|
||||||
// Setup the SMBIOS collection
|
// Setup the SMBIOS collection
|
||||||
obj.smbiosfile = new Datastore({ filename: obj.parent.getConfigFilePath('meshcentral-smbios.db'), autoload: true });
|
obj.smbiosfile = new Datastore({ filename: obj.parent.getConfigFilePath('meshcentral-smbios.db'), autoload: true });
|
||||||
|
|
||||||
|
// Setup the server stats collection and setup indexes
|
||||||
|
obj.serverstatsfile = new Datastore({ filename: obj.parent.getConfigFilePath('meshcentral-stats.db'), autoload: true });
|
||||||
|
obj.serverstatsfile.persistence.setAutocompactionInterval(36000);
|
||||||
|
obj.serverstatsfile.ensureIndex({ fieldName: 'time', expireAfterSeconds: 60 * 60 * 24 * 30 }); // Limit the server stats log to 30 days (Seconds * Minutes * Hours * Days)
|
||||||
|
obj.serverstatsfile.ensureIndex({ fieldName: 'expire', expireAfterSeconds: 0 }); // Auto-expire events
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.SetupDatabase = function (func) {
|
obj.SetupDatabase = function (func) {
|
||||||
|
@ -305,6 +337,10 @@ module.exports.CreateDB = function (parent) {
|
||||||
obj.RemoveSMBIOS = function (id) { obj.smbiosfile.remove({ _id: id }); };
|
obj.RemoveSMBIOS = function (id) { obj.smbiosfile.remove({ _id: id }); };
|
||||||
obj.GetSMBIOS = function (id, func) { obj.smbiosfile.find({ _id: id }, func); };
|
obj.GetSMBIOS = function (id, func) { obj.smbiosfile.find({ _id: id }, func); };
|
||||||
|
|
||||||
|
// Database actions on the Server Stats collection
|
||||||
|
obj.SetServerStats = function (data, func) { obj.serverstatsfile.insert(data, func); };
|
||||||
|
obj.GetServerStats = function (hours, func) { var t = new Date(); t.setTime(t.getTime() - (60 * 60 * 1000 * hours)); obj.serverstatsfile.find({ time: { $gt: t } }, { _id: 0, cpu: 0 }, func); };
|
||||||
|
|
||||||
// Read a configuration file from the database
|
// Read a configuration file from the database
|
||||||
obj.getConfigFile = function (path, func) { obj.Get('cfile/' + path, func); }
|
obj.getConfigFile = function (path, func) { obj.Get('cfile/' + path, func); }
|
||||||
|
|
||||||
|
@ -376,7 +412,7 @@ module.exports.CreateDB = function (parent) {
|
||||||
obj.file.count({ type: 'mesh' }, function (err, meshCount) {
|
obj.file.count({ type: 'mesh' }, function (err, meshCount) {
|
||||||
obj.file.count({ type: 'user' }, function (err, userCount) {
|
obj.file.count({ type: 'user' }, function (err, userCount) {
|
||||||
obj.file.count({}, function (err, totalCount) {
|
obj.file.count({}, function (err, totalCount) {
|
||||||
func({ nodes: nodeCount, meshes: meshCount, powerEvents: powerCount, users: userCount, nodeInterfaces: nodeInterfaceCount, notes: noteCount, connectEvent: nodeLastConnectCount, smbios: nodeSmbiosCount, total: totalCount });
|
func({ nodes: nodeCount, meshes: meshCount, users: userCount, total: totalCount });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -60,6 +60,7 @@ function CreateMeshCentralServer(config, args) {
|
||||||
obj.serverKey = Buffer.from(obj.crypto.randomBytes(48), 'binary');
|
obj.serverKey = Buffer.from(obj.crypto.randomBytes(48), 'binary');
|
||||||
obj.loginCookieEncryptionKey = null;
|
obj.loginCookieEncryptionKey = null;
|
||||||
obj.serverSelfWriteAllowed = true;
|
obj.serverSelfWriteAllowed = true;
|
||||||
|
obj.serverStatsCounter = Math.floor(Math.random() * 1000);
|
||||||
obj.taskLimiter = obj.common.createTaskLimiterQueue(50, 20, 60); // (maxTasks, maxTaskTime, cleaningInterval) This is a task limiter queue to smooth out server work.
|
obj.taskLimiter = obj.common.createTaskLimiterQueue(50, 20, 60); // (maxTasks, maxTaskTime, cleaningInterval) This is a task limiter queue to smooth out server work.
|
||||||
try { obj.currentVer = JSON.parse(obj.fs.readFileSync(obj.path.join(__dirname, 'package.json'), 'utf8')).version; } catch (e) { } // Fetch server version
|
try { obj.currentVer = JSON.parse(obj.fs.readFileSync(obj.path.join(__dirname, 'package.json'), 'utf8')).version; } catch (e) { } // Fetch server version
|
||||||
|
|
||||||
|
@ -800,6 +801,35 @@ function CreateMeshCentralServer(config, args) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start collecting server stats every 5 minutes
|
||||||
|
setInterval(function () {
|
||||||
|
obj.serverStatsCounter++;
|
||||||
|
var hours = 720; // Start with all events lasting 30 days.
|
||||||
|
if (((obj.serverStatsCounter) % 2) == 1) { hours = 3; } // Half of the event get removed after 3 hours.
|
||||||
|
else if ((Math.floor(obj.serverStatsCounter / 2) % 2) == 1) { hours = 8; } // Another half of the event get removed after 8 hours.
|
||||||
|
else if ((Math.floor(obj.serverStatsCounter / 4) % 2) == 1) { hours = 24; } // Another half of the event get removed after 24 hours.
|
||||||
|
else if ((Math.floor(obj.serverStatsCounter / 8) % 2) == 1) { hours = 48; } // Another half of the event get removed after 48 hours.
|
||||||
|
else if ((Math.floor(obj.serverStatsCounter / 16) % 2) == 1) { hours = 72; } // Another half of the event get removed after 72 hours.
|
||||||
|
var expire = new Date();
|
||||||
|
expire.setTime(expire.getTime() + (60 * 60 * 1000 * hours));
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
time: new Date(),
|
||||||
|
expire: expire,
|
||||||
|
mem: process.memoryUsage(),
|
||||||
|
//cpu: process.cpuUsage(),
|
||||||
|
conn: {
|
||||||
|
ca: Object.keys(obj.webserver.wsagents).length,
|
||||||
|
cu: Object.keys(obj.webserver.wssessions).length,
|
||||||
|
us: Object.keys(obj.webserver.wssessions2).length,
|
||||||
|
rs: obj.webserver.relaySessionCount
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (obj.mpsserver != null) { data.conn.am = Object.keys(obj.mpsserver.ciraConnections).length; }
|
||||||
|
obj.db.SetServerStats(data); // Save the stats to the database
|
||||||
|
obj.DispatchEvent(['*'], obj, { action: 'servertimelinestats', data: data }); // Event the server stats
|
||||||
|
}, 300000);
|
||||||
|
|
||||||
//obj.debug(1, 'Server started');
|
//obj.debug(1, 'Server started');
|
||||||
if (obj.args.nousers == true) { obj.updateServerState('nousers', '1'); }
|
if (obj.args.nousers == true) { obj.updateServerState('nousers', '1'); }
|
||||||
obj.updateServerState('state', 'running');
|
obj.updateServerState('state', 'running');
|
||||||
|
@ -1598,31 +1628,34 @@ function getConfig(createSampleConfig) {
|
||||||
function InstallModules(modules, func) {
|
function InstallModules(modules, func) {
|
||||||
var missingModules = [];
|
var missingModules = [];
|
||||||
if (modules.length > 0) {
|
if (modules.length > 0) {
|
||||||
for (var i in modules) { try { var xxmodule = require(modules[i]); } catch (e) { missingModules.push(modules[i]); } }
|
for (var i in modules) {
|
||||||
if (missingModules.length > 0) { InstallModule(missingModules.join(' '), InstallModules, modules, func); } else { func(); }
|
try {
|
||||||
|
var xxmodule = require(modules[i]);
|
||||||
|
} catch (e) {
|
||||||
|
if (previouslyInstalledModules[modules[i]] !== true) { previouslyInstalledModules[modules[i]] = true; missingModules.push(modules[i]); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (missingModules.length > 0) { InstallModule(missingModules.shift(), InstallModules, modules, func); } else { func(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a module is present and install it if missing
|
// Check if a module is present and install it if missing
|
||||||
var InstallModuleChildProcess = null;
|
var InstallModuleChildProcess = null;
|
||||||
function InstallModule(modulename, func, tag1, tag2) {
|
function InstallModule(modulename, func, tag1, tag2) {
|
||||||
try {
|
console.log('Installing ' + modulename + '...');
|
||||||
var module = require(modulename);
|
var child_process = require('child_process');
|
||||||
} catch (e) {
|
|
||||||
console.log('Installing ' + modulename + '...');
|
|
||||||
var child_process = require('child_process');
|
|
||||||
|
|
||||||
// Looks like we need to keep a global reference to the child process object for this to work correctly.
|
// Looks like we need to keep a global reference to the child process object for this to work correctly.
|
||||||
InstallModuleChildProcess = child_process.exec('npm install ' + modulename + ' --no-optional --save', { maxBuffer: 512000, timeout: 10000 }, function (error, stdout, stderr) {
|
InstallModuleChildProcess = child_process.exec('npm install --no-optional --save ' + modulename, { maxBuffer: 512000, timeout: 10000 }, function (error, stdout, stderr) {
|
||||||
InstallModuleChildProcess = null;
|
InstallModuleChildProcess = null;
|
||||||
if (error != null) { console.log('ERROR: Unable to install missing package \'' + modulename + '\', make sure npm is installed: ' + error); process.exit(); return; }
|
if (error != null) {
|
||||||
func(tag1, tag2);
|
console.log('ERROR: Unable to install required module "' + modulename + '". MeshCentral may not have access to npm, or npm may not have suffisent rights to load the new module. Try "npm install ' + modulename + '" to manualy install this module.\r\n');
|
||||||
|
process.exit();
|
||||||
return;
|
return;
|
||||||
});
|
}
|
||||||
|
func(tag1, tag2);
|
||||||
return;
|
return;
|
||||||
}
|
});
|
||||||
func(tag1, tag2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect CTRL-C on Linux and stop nicely
|
// Detect CTRL-C on Linux and stop nicely
|
||||||
|
@ -1630,6 +1663,7 @@ process.on('SIGINT', function () { if (meshserver != null) { meshserver.Stop();
|
||||||
|
|
||||||
// Load the really basic modules
|
// Load the really basic modules
|
||||||
var meshserver = null;
|
var meshserver = null;
|
||||||
|
var previouslyInstalledModules = { };
|
||||||
function mainStart(args) {
|
function mainStart(args) {
|
||||||
// Check the NodeJS is version 6 or better.
|
// Check the NodeJS is version 6 or better.
|
||||||
if (Number(process.version.match(/^v(\d+\.\d+)/)[1]) < 6) { console.log("MeshCentral requires Node v6.x or above, current version is " + process.version + "."); return; }
|
if (Number(process.version.match(/^v(\d+\.\d+)/)[1]) < 6) { console.log("MeshCentral requires Node v6.x or above, current version is " + process.version + "."); return; }
|
||||||
|
@ -1640,10 +1674,12 @@ function mainStart(args) {
|
||||||
var config = getConfig(false);
|
var config = getConfig(false);
|
||||||
if (config == null) { process.exit(); }
|
if (config == null) { process.exit(); }
|
||||||
|
|
||||||
// Check is Windows SSPI will be used
|
// Check is Windows SSPI and YubiKey OTP will be used
|
||||||
var sspi = false;
|
var sspi = false;
|
||||||
var allsspi = true;
|
var allsspi = true;
|
||||||
if (require('os').platform() == 'win32') { for (var i in config.domains) { if (config.domains[i].auth == 'sspi') { sspi = true; } else { allsspi = false; } } }
|
var yubikey = false;
|
||||||
|
if (require('os').platform() == 'win32') { for (var i in config.domains) { if (config.domains[i].auth == 'sspi') { sspi = true; } else { allsspi = false; } } } else { allsspi = false; }
|
||||||
|
for (var i in config.domains) { if (config.domains[i].yubikey != null) { yubikey = true; } }
|
||||||
|
|
||||||
// Build the list of required modules
|
// Build the list of required modules
|
||||||
var modules = ['ws', 'nedb', 'https', 'yauzl', 'xmldom', 'express', 'archiver', 'multiparty', 'node-forge', 'express-ws', 'compression', 'body-parser', 'connect-redis', 'express-handlebars'];
|
var modules = ['ws', 'nedb', 'https', 'yauzl', 'xmldom', 'express', 'archiver', 'multiparty', 'node-forge', 'express-ws', 'compression', 'body-parser', 'connect-redis', 'express-handlebars'];
|
||||||
|
@ -1658,8 +1694,13 @@ function mainStart(args) {
|
||||||
// If running NodeJS < 8, install "util.promisify"
|
// If running NodeJS < 8, install "util.promisify"
|
||||||
if (nodeVersion < 8) { modules.push('util.promisify'); }
|
if (nodeVersion < 8) { modules.push('util.promisify'); }
|
||||||
|
|
||||||
// if running NodeJS 8 or higher, we can install WebAuthn/FIDO2 support
|
// Setup 2nd factor authentication
|
||||||
if ((nodeVersion >= 8) && (allsspi == false)) { modules.push('@davedoesdev/fido2-lib'); }
|
if (config.settings.no2factorauth !== true) {
|
||||||
|
// Setup YubiKey OTP if configured
|
||||||
|
if (yubikey == true) { modules.push('yubikeyotp'); } // Add YubiKey OTP support
|
||||||
|
// if not all SSPI, WebAuthn/FIDO2 or U2F support depending on the NodeJS version. FIDO2 does not work below NodeJS 8.x
|
||||||
|
if (allsspi == false) { modules.push('otplib'); if (nodeVersion >= 8) { modules.push('@davedoesdev/fido2-lib'); } else { modules.push('authdog'); } }
|
||||||
|
}
|
||||||
|
|
||||||
// Install any missing modules and launch the server
|
// Install any missing modules and launch the server
|
||||||
InstallModules(modules, function () { meshserver = CreateMeshCentralServer(config, args); meshserver.Start(); });
|
InstallModules(modules, function () { meshserver = CreateMeshCentralServer(config, args); meshserver.Start(); });
|
||||||
|
|
70
meshuser.js
70
meshuser.js
|
@ -261,17 +261,25 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||||
try { ws.send(JSON.stringify({ action: 'authcookie', cookie: parent.parent.encodeCookie({ userid: user._id, domainid: domain.id }, parent.parent.loginCookieEncryptionKey) })); } catch (ex) { }
|
try { ws.send(JSON.stringify({ action: 'authcookie', cookie: parent.parent.encodeCookie({ userid: user._id, domainid: domain.id }, parent.parent.loginCookieEncryptionKey) })); } catch (ex) { }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'servertimelinestats':
|
||||||
|
{
|
||||||
|
if ((user.siteadmin & 21) == 0) return; // Only site administrators with "site backup" or "site restore" or "site update" permissions can use this.
|
||||||
|
if (common.validateInt(command.hours, 0, 24 * 30) == false) return;
|
||||||
|
db.GetServerStats(command.hours, function (err, docs) {
|
||||||
|
if (err == null) { ws.send(JSON.stringify({ action: 'servertimelinestats', events: docs })); }
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'serverstats':
|
case 'serverstats':
|
||||||
{
|
{
|
||||||
if ((user.siteadmin & 21) != 0) { // Only site administrators with "site backup" or "site restore" or "site update" permissions can use this.
|
if ((user.siteadmin & 21) == 0) return; // Only site administrators with "site backup" or "site restore" or "site update" permissions can use this.
|
||||||
if (common.validateInt(command.interval, 1000, 1000000) == false) {
|
if (common.validateInt(command.interval, 1000, 1000000) == false) {
|
||||||
// Clear the timer
|
// Clear the timer
|
||||||
if (obj.serverStatsTimer != null) { clearInterval(obj.serverStatsTimer); delete obj.serverStatsTimer; }
|
if (obj.serverStatsTimer != null) { clearInterval(obj.serverStatsTimer); delete obj.serverStatsTimer; }
|
||||||
} else {
|
} else {
|
||||||
// Set the timer
|
// Set the timer
|
||||||
obj.SendServerStats();
|
obj.SendServerStats();
|
||||||
obj.serverStatsTimer = setInterval(obj.SendServerStats, command.interval);
|
obj.serverStatsTimer = setInterval(obj.SendServerStats, command.interval);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1773,7 +1781,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||||
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
|
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
|
||||||
if (twoStepLoginSupported) {
|
if (twoStepLoginSupported) {
|
||||||
// Request a one time password to be setup
|
// Request a one time password to be setup
|
||||||
const otplib = require('otplib');
|
var otplib = null;
|
||||||
|
try { otplib = require('otplib'); } catch (ex) { }
|
||||||
|
if (otplib == null) { break; }
|
||||||
const secret = otplib.authenticator.generateSecret(); // TODO: Check the random source of this value.
|
const secret = otplib.authenticator.generateSecret(); // TODO: Check the random source of this value.
|
||||||
ws.send(JSON.stringify({ action: 'otpauth-request', secret: secret, url: otplib.authenticator.keyuri(user.name, parent.certificates.CommonName, secret) }));
|
ws.send(JSON.stringify({ action: 'otpauth-request', secret: secret, url: otplib.authenticator.keyuri(user.name, parent.certificates.CommonName, secret) }));
|
||||||
}
|
}
|
||||||
|
@ -1785,7 +1795,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||||
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
|
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
|
||||||
if (twoStepLoginSupported) {
|
if (twoStepLoginSupported) {
|
||||||
// Perform the one time password setup
|
// Perform the one time password setup
|
||||||
const otplib = require('otplib');
|
var otplib = null;
|
||||||
|
try { otplib = require('otplib'); } catch (ex) { }
|
||||||
|
if (otplib == null) { break; }
|
||||||
otplib.authenticator.options = { window: 2 }; // Set +/- 1 minute window
|
otplib.authenticator.options = { window: 2 }; // Set +/- 1 minute window
|
||||||
if (otplib.authenticator.check(command.token, command.secret) === true) {
|
if (otplib.authenticator.check(command.token, command.secret) === true) {
|
||||||
// Token is valid, activate 2-step login on this account.
|
// Token is valid, activate 2-step login on this account.
|
||||||
|
@ -1853,8 +1865,6 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||||
}
|
}
|
||||||
case 'otp-hkey-get':
|
case 'otp-hkey-get':
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
// Check is 2-step login is supported
|
// Check is 2-step login is supported
|
||||||
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
|
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
|
||||||
if (twoStepLoginSupported == false) break;
|
if (twoStepLoginSupported == false) break;
|
||||||
|
@ -1886,11 +1896,15 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||||
}
|
}
|
||||||
case 'otp-hkey-yubikey-add':
|
case 'otp-hkey-yubikey-add':
|
||||||
{
|
{
|
||||||
|
if (parent.parent.config.settings.no2factorauth === true) return;
|
||||||
|
|
||||||
// Yubico API id and signature key can be requested from https://upgrade.yubico.com/getapikey/
|
// Yubico API id and signature key can be requested from https://upgrade.yubico.com/getapikey/
|
||||||
|
var yubikeyotp = null;
|
||||||
|
try { yubikeyotp = require('yubikeyotp'); } catch (ex) { }
|
||||||
|
|
||||||
// Check is 2-step login is supported
|
// Check is 2-step login is supported
|
||||||
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
|
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
|
||||||
if ((twoStepLoginSupported == false) || (typeof command.otp != 'string')) {
|
if ((yubikeyotp == null) || (twoStepLoginSupported == false) || (typeof command.otp != 'string')) {
|
||||||
ws.send(JSON.stringify({ action: 'otp-hkey-yubikey-add', result: false, name: command.name }));
|
ws.send(JSON.stringify({ action: 'otp-hkey-yubikey-add', result: false, name: command.name }));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1904,7 +1918,6 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||||
// TODO: Check if command.otp is modhex encoded, reject if not.
|
// TODO: Check if command.otp is modhex encoded, reject if not.
|
||||||
|
|
||||||
// Query the YubiKey server to validate the OTP
|
// Query the YubiKey server to validate the OTP
|
||||||
var yubikeyotp = require('yubikeyotp');
|
|
||||||
var request = { otp: command.otp, id: domain.yubikey.id, key: domain.yubikey.secret, timestamp: true }
|
var request = { otp: command.otp, id: domain.yubikey.id, key: domain.yubikey.secret, timestamp: true }
|
||||||
if (domain.yubikey.proxy) { request.requestParams = { proxy: domain.yubikey.proxy }; }
|
if (domain.yubikey.proxy) { request.requestParams = { proxy: domain.yubikey.proxy }; }
|
||||||
yubikeyotp.verifyOTP(request, function (err, results) {
|
yubikeyotp.verifyOTP(request, function (err, results) {
|
||||||
|
@ -1934,16 +1947,21 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||||
}
|
}
|
||||||
case 'otp-hkey-setup-request':
|
case 'otp-hkey-setup-request':
|
||||||
{
|
{
|
||||||
|
if (parent.parent.config.settings.no2factorauth === true) return;
|
||||||
|
|
||||||
|
var authdoglib = null;
|
||||||
|
try { authdoglib = require('authdog'); } catch (ex) { }
|
||||||
|
|
||||||
// Check is 2-step login is supported
|
// Check is 2-step login is supported
|
||||||
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
|
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
|
||||||
if (twoStepLoginSupported == false) break;
|
if ((authdoglib == null) || (twoStepLoginSupported == false)) break;
|
||||||
|
|
||||||
// Build list of known keys
|
// Build list of known keys
|
||||||
var knownKeys = [];
|
var knownKeys = [];
|
||||||
if (user.otphkeys != null) { for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 1) { knownKeys.push(user.otphkeys[i]); } } }
|
if (user.otphkeys != null) { for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 1) { knownKeys.push(user.otphkeys[i]); } } }
|
||||||
|
|
||||||
// Build a key registration request and send it over
|
// Build a key registration request and send it over
|
||||||
require('authdog').startRegistration('https://' + parent.parent.certificates.CommonName, knownKeys, { requestId: 556, timeoutSeconds: 100 }).then(function (registrationRequest) {
|
authdoglib.startRegistration('https://' + parent.parent.certificates.CommonName, knownKeys, { requestId: 556, timeoutSeconds: 100 }).then(function (registrationRequest) {
|
||||||
// Save registration request to session for later use
|
// Save registration request to session for later use
|
||||||
obj.hardwareKeyRegistrationRequest = registrationRequest;
|
obj.hardwareKeyRegistrationRequest = registrationRequest;
|
||||||
|
|
||||||
|
@ -1957,12 +1975,17 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||||
}
|
}
|
||||||
case 'otp-hkey-setup-response':
|
case 'otp-hkey-setup-response':
|
||||||
{
|
{
|
||||||
|
if (parent.parent.config.settings.no2factorauth === true) return;
|
||||||
|
|
||||||
|
var authdoglib = null;
|
||||||
|
try { authdoglib = require('authdog'); } catch (ex) { }
|
||||||
|
|
||||||
// Check is 2-step login is supported
|
// Check is 2-step login is supported
|
||||||
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
|
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
|
||||||
if ((twoStepLoginSupported == false) || (command.response == null) || (command.name == null) || (obj.hardwareKeyRegistrationRequest == null)) break;
|
if ((authdoglib == null) || (twoStepLoginSupported == false) || (command.response == null) || (command.name == null) || (obj.hardwareKeyRegistrationRequest == null)) break;
|
||||||
|
|
||||||
// Check the key registration request
|
// Check the key registration request
|
||||||
require('authdog').finishRegistration(obj.hardwareKeyRegistrationRequest, command.response).then(function (registrationStatus) {
|
authdoglib.finishRegistration(obj.hardwareKeyRegistrationRequest, command.response).then(function (registrationStatus) {
|
||||||
var keyIndex = parent.crypto.randomBytes(4).readUInt32BE(0);
|
var keyIndex = parent.crypto.randomBytes(4).readUInt32BE(0);
|
||||||
ws.send(JSON.stringify({ action: 'otp-hkey-setup-response', result: true, name: command.name, index: keyIndex }));
|
ws.send(JSON.stringify({ action: 'otp-hkey-setup-response', result: true, name: command.name, index: keyIndex }));
|
||||||
if (user.otphkeys == null) { user.otphkeys = []; }
|
if (user.otphkeys == null) { user.otphkeys = []; }
|
||||||
|
@ -1980,6 +2003,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||||
}
|
}
|
||||||
case 'webauthn-startregister':
|
case 'webauthn-startregister':
|
||||||
{
|
{
|
||||||
|
if (parent.parent.config.settings.no2factorauth === true) return;
|
||||||
|
|
||||||
// Check is 2-step login is supported
|
// Check is 2-step login is supported
|
||||||
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
|
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
|
||||||
if ((twoStepLoginSupported == false) || (command.name == null) || (parent.f2l == null)) break;
|
if ((twoStepLoginSupported == false) || (command.name == null) || (parent.f2l == null)) break;
|
||||||
|
@ -2002,6 +2027,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||||
}
|
}
|
||||||
case 'webauthn-endregister':
|
case 'webauthn-endregister':
|
||||||
{
|
{
|
||||||
|
if (parent.parent.config.settings.no2factorauth === true) return;
|
||||||
if ((obj.webAuthnReqistrationRequest == null) || (parent.f2l == null)) return;
|
if ((obj.webAuthnReqistrationRequest == null) || (parent.f2l == null)) return;
|
||||||
|
|
||||||
// Figure out the origin
|
// Figure out the origin
|
||||||
|
@ -2022,8 +2048,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||||
clientAttestationResponse.response.clientDataJSON = new Uint8Array(Buffer.from(clientAttestationResponse.response.clientDataJSON, 'base64')).buffer;
|
clientAttestationResponse.response.clientDataJSON = new Uint8Array(Buffer.from(clientAttestationResponse.response.clientDataJSON, 'base64')).buffer;
|
||||||
|
|
||||||
parent.f2l.attestationResult(clientAttestationResponse, attestationExpectations).then(function (regResult) {
|
parent.f2l.attestationResult(clientAttestationResponse, attestationExpectations).then(function (regResult) {
|
||||||
// If we register a WebAuthn/FIDO2 key, remove all U2F keys.
|
// Since we are registering a WebAuthn/FIDO2 key, remove all U2F keys (Type 1).
|
||||||
// TODO
|
var otphkeys2 = [];
|
||||||
|
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type != 1) { otphkeys2.push(user.otphkeys[i]); } }
|
||||||
|
user.otphkeys = otphkeys2;
|
||||||
|
|
||||||
// Add the new WebAuthn/FIDO2 keys
|
// Add the new WebAuthn/FIDO2 keys
|
||||||
var keyIndex = parent.crypto.randomBytes(4).readUInt32BE(0);
|
var keyIndex = parent.crypto.randomBytes(4).readUInt32BE(0);
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "meshcentral",
|
"name": "meshcentral",
|
||||||
"version": "0.3.0-u",
|
"version": "0.3.1-q",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"Remote Management",
|
"Remote Management",
|
||||||
"Intel AMT",
|
"Intel AMT",
|
||||||
|
@ -27,7 +27,6 @@
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"archiver": "^3.0.0",
|
"archiver": "^3.0.0",
|
||||||
"authdog": "^0.1.1",
|
|
||||||
"body-parser": "^1.18.2",
|
"body-parser": "^1.18.2",
|
||||||
"compression": "^1.7.3",
|
"compression": "^1.7.3",
|
||||||
"connect-redis": "^3.4.0",
|
"connect-redis": "^3.4.0",
|
||||||
|
@ -41,11 +40,9 @@
|
||||||
"multiparty": "^4.2.1",
|
"multiparty": "^4.2.1",
|
||||||
"nedb": "^1.8.0",
|
"nedb": "^1.8.0",
|
||||||
"node-forge": "^0.7.6",
|
"node-forge": "^0.7.6",
|
||||||
"otplib": "^10.0.1",
|
|
||||||
"ws": "^6.1.2",
|
"ws": "^6.1.2",
|
||||||
"xmldom": "^0.1.27",
|
"xmldom": "^0.1.27",
|
||||||
"yauzl": "^2.10.0",
|
"yauzl": "^2.10.0"
|
||||||
"yubikeyotp": "^0.2.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {},
|
"devDependencies": {},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 2.6 KiB |
Binary file not shown.
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 11 KiB |
|
@ -0,0 +1 @@
|
||||||
|
npm install archiver authdog body-parser compression connect-redis cookie-session express express-handlebars express-ws ipcheck minimist mongojs multiparty nedb node-forge otplib ws xmldom yauzl yubikeyotp
|
|
@ -30,6 +30,7 @@
|
||||||
},
|
},
|
||||||
"_TlsOffload": true,
|
"_TlsOffload": true,
|
||||||
"_MpsTlsOffload": true,
|
"_MpsTlsOffload": true,
|
||||||
|
"_No2FactorAuth": true,
|
||||||
"_WebRtConfig": {
|
"_WebRtConfig": {
|
||||||
"iceServers": [
|
"iceServers": [
|
||||||
{ "urls": "stun:stun.services.mozilla.com" },
|
{ "urls": "stun:stun.services.mozilla.com" },
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -25,7 +25,7 @@
|
||||||
<script type="text/javascript" src="scripts/agent-redir-rtc-0.1.0.js"></script>
|
<script type="text/javascript" src="scripts/agent-redir-rtc-0.1.0.js"></script>
|
||||||
<script type="text/javascript" src="scripts/agent-desktop-0.0.2.js"></script>
|
<script type="text/javascript" src="scripts/agent-desktop-0.0.2.js"></script>
|
||||||
<script type="text/javascript" src="scripts/qrcode.min.js"></script>
|
<script type="text/javascript" src="scripts/qrcode.min.js"></script>
|
||||||
<script type="text/javascript" src="scripts/u2f-api.js"></script>
|
<script keeplink=1 type="text/javascript" src="scripts/u2f-api.js"></script>
|
||||||
<script keeplink=1 type="text/javascript" src="scripts/charts.js"></script>
|
<script keeplink=1 type="text/javascript" src="scripts/charts.js"></script>
|
||||||
<script keeplink=1 type="text/javascript" src="scripts/filesaver.1.1.20151003.js"></script>
|
<script keeplink=1 type="text/javascript" src="scripts/filesaver.1.1.20151003.js"></script>
|
||||||
<script keeplink=1 type="text/javascript" src="scripts/ol.js"></script>
|
<script keeplink=1 type="text/javascript" src="scripts/ol.js"></script>
|
||||||
|
@ -141,6 +141,7 @@
|
||||||
<table id=ServerSubMenu style=width:100%;height:22px cellpadding=0 cellspacing=0 class=style1>
|
<table id=ServerSubMenu style=width:100%;height:22px cellpadding=0 cellspacing=0 class=style1>
|
||||||
<tr>
|
<tr>
|
||||||
<td id=ServerGeneral style=width:100px;height:24px;cursor:pointer class=style3x onclick=go(6)>General</td>
|
<td id=ServerGeneral style=width:100px;height:24px;cursor:pointer class=style3x onclick=go(6)>General</td>
|
||||||
|
<td id=ServerStats style=width:100px;height:24px;cursor:pointer class=style3x onclick=go(40)>Stats</td>
|
||||||
<td id=ServerConsole style=width:100px;height:24px;cursor:pointer class=style3x onclick=go(115)>Console</td>
|
<td id=ServerConsole style=width:100px;height:24px;cursor:pointer class=style3x onclick=go(115)>Console</td>
|
||||||
<td class=style3 style=height:24px> </td>
|
<td class=style3 style=height:24px> </td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -743,6 +744,30 @@
|
||||||
</div>
|
</div>
|
||||||
<div id=p31events style="max-height:calc(100vh - 267px);overflow-y:scroll"></div>
|
<div id=p31events style="max-height:calc(100vh - 267px);overflow-y:scroll"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id=p40 style="display:none;height:calc(100vh - 170px)">
|
||||||
|
<h1>My Server Stats</h1>
|
||||||
|
<div style=width:100%;height:24px;background-color:#d3d9d6;margin-bottom:4px>
|
||||||
|
<div style="float:right">
|
||||||
|
<select id=p40type onchange=updateServerTimelineStats()>
|
||||||
|
<option value=0>Connections</option>
|
||||||
|
<option value=1>Memory</option>
|
||||||
|
</select>
|
||||||
|
<select id=p40time onchange=updateServerTimelineHours()>
|
||||||
|
<option value=3>Last 3 hours</option>
|
||||||
|
<option value=8>Last 8 hours</option>
|
||||||
|
<option value=24>Last day</option>
|
||||||
|
<option value=168>Last week</option>
|
||||||
|
<option value=5040>Last 30 days</option>
|
||||||
|
</select>
|
||||||
|
<img src=images/link4.png height=10 width=10 title="Download data points (.csv)" style=cursor:pointer onclick=p40downloadEvents()>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input value="Refresh" type="button" onclick="refreshServerTimelineStats()" />
|
||||||
|
<input id=p40log type="checkbox" onclick="updateServerTimelineHours()" />Log-X
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<canvas id=serverMainStats style="height:calc(100vh - 250px);max-height:calc(100vh - 250px);width:100%"></canvas>
|
||||||
|
</div>
|
||||||
<br id="column_l_bottomgap" />
|
<br id="column_l_bottomgap" />
|
||||||
</div>
|
</div>
|
||||||
<div id=footer class=noselect>
|
<div id=footer class=noselect>
|
||||||
|
@ -1014,8 +1039,9 @@
|
||||||
for (var c = 1; c < 27; c++) x += "<option value='" + c + "'>Ctrl-" + String.fromCharCode(64 + c) + " (" + c + ")</option>";
|
for (var c = 1; c < 27; c++) x += "<option value='" + c + "'>Ctrl-" + String.fromCharCode(64 + c) + " (" + c + ")</option>";
|
||||||
QH('specialkeylist', x);
|
QH('specialkeylist', x);
|
||||||
|
|
||||||
// Setup server stats panel
|
// Setup server stats panels
|
||||||
setupServerStats();
|
setupGeneralServerStats();
|
||||||
|
setupServerTimelineStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle the web page to full screen
|
// Toggle the web page to full screen
|
||||||
|
@ -1209,7 +1235,11 @@
|
||||||
function onMessage(server, message) {
|
function onMessage(server, message) {
|
||||||
switch (message.action) {
|
switch (message.action) {
|
||||||
case 'serverstats': {
|
case 'serverstats': {
|
||||||
updateServerStats(message);
|
updateGeneralServerStats(message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'servertimelinestats': {
|
||||||
|
setServerTimelineStats(message.events);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'authcookie': {
|
case 'authcookie': {
|
||||||
|
@ -1519,8 +1549,10 @@
|
||||||
meshserver.send({ action: 'otp-hkey-setup-response', response: registrationResponse, name: Q('dp1keyname').value });
|
meshserver.send({ action: 'otp-hkey-setup-response', response: registrationResponse, name: Q('dp1keyname').value });
|
||||||
setDialogMode(2, "Add Security Key", 0, null, '<br />Checking...<br /><br /><br />', 'otpauth-hardware-manage');
|
setDialogMode(2, "Add Security Key", 0, null, '<br />Checking...<br /><br /><br />', 'otpauth-hardware-manage');
|
||||||
} else {
|
} else {
|
||||||
var errorCodes = ['', 'Unknown error', 'Bad request', 'Unsupported configuration', 'This key was already registered', 'Timeout'];
|
if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) {
|
||||||
setDialogMode(2, "Add Security Key", 1, null, '<br />' + errorCodes[registrationResponse.errorCode] + '.<br /><br />');
|
var errorCodes = ['', 'Unknown error', 'Bad request', 'Unsupported configuration', 'This key was already registered', 'Timeout'];
|
||||||
|
setDialogMode(2, "Add Security Key", 1, null, '<br />' + errorCodes[registrationResponse.errorCode] + '.<br /><br />');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, message.request.timeoutSeconds);
|
}, message.request.timeoutSeconds);
|
||||||
break;
|
break;
|
||||||
|
@ -1540,15 +1572,15 @@
|
||||||
setDialogMode(2, "Add Security Key", 2, null, x);
|
setDialogMode(2, "Add Security Key", 2, null, x);
|
||||||
|
|
||||||
var publicKey = message.request;
|
var publicKey = message.request;
|
||||||
message.request.challenge = Uint8Array.from(atob(message.request.challenge), c => c.charCodeAt(0))
|
message.request.challenge = Uint8Array.from(atob(message.request.challenge), function (c) { return c.charCodeAt(0) })
|
||||||
message.request.user.id = Uint8Array.from(atob(message.request.user.id), c => c.charCodeAt(0))
|
message.request.user.id = Uint8Array.from(atob(message.request.user.id), function (c) { return c.charCodeAt(0) })
|
||||||
navigator.credentials.create({ publicKey })
|
navigator.credentials.create({ publicKey: publicKey })
|
||||||
.then((newCredentialInfo) => {
|
.then(function(newCredentialInfo) {
|
||||||
// Public key credential
|
// Public key credential
|
||||||
var r = { rawId: btoa(String.fromCharCode.apply(null, new Uint8Array(newCredentialInfo.rawId))), response: { attestationObject: btoa(String.fromCharCode.apply(null, new Uint8Array(newCredentialInfo.response.attestationObject))), clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(newCredentialInfo.response.clientDataJSON))) }, type: newCredentialInfo.type };
|
var r = { rawId: btoa(String.fromCharCode.apply(null, new Uint8Array(newCredentialInfo.rawId))), response: { attestationObject: btoa(String.fromCharCode.apply(null, new Uint8Array(newCredentialInfo.response.attestationObject))), clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(newCredentialInfo.response.clientDataJSON))) }, type: newCredentialInfo.type };
|
||||||
meshserver.send({ action: 'webauthn-endregister', response: r });
|
meshserver.send({ action: 'webauthn-endregister', response: r });
|
||||||
setDialogMode(0);
|
setDialogMode(0);
|
||||||
}).catch((error) => {
|
}, function(error) {
|
||||||
// Error
|
// Error
|
||||||
setDialogMode(2, "Add Security Key", 1, null, "ERROR: " + error);
|
setDialogMode(2, "Add Security Key", 1, null, "ERROR: " + error);
|
||||||
});
|
});
|
||||||
|
@ -1562,6 +1594,7 @@
|
||||||
masterUpdate(32);
|
masterUpdate(32);
|
||||||
}
|
}
|
||||||
switch (message.event.action) {
|
switch (message.event.action) {
|
||||||
|
case 'servertimelinestats': { addServerTimelineStats(message.event.data); break; }
|
||||||
case 'accountcreate':
|
case 'accountcreate':
|
||||||
case 'accountchange': {
|
case 'accountchange': {
|
||||||
// An account was created or changed
|
// An account was created or changed
|
||||||
|
@ -6433,18 +6466,20 @@
|
||||||
var x = '', dateHeader = null;
|
var x = '', dateHeader = null;
|
||||||
for (var i in events) {
|
for (var i in events) {
|
||||||
var event = events[i], time = new Date(event.time);
|
var event = events[i], time = new Date(event.time);
|
||||||
if (time.toLocaleDateString() != dateHeader) {
|
if (event.msg) {
|
||||||
if (dateHeader != null) x += '</table>';
|
if (time.toLocaleDateString() != dateHeader) {
|
||||||
x += '<table style=width:100% cellpadding=0 cellspacing=0><tr><td colspan=4 class=DevSt>' + time.toLocaleDateString() + '</td></tr>';
|
if (dateHeader != null) x += '</table>';
|
||||||
dateHeader = time.toLocaleDateString();
|
x += '<table style=width:100% cellpadding=0 cellspacing=0><tr><td colspan=4 class=DevSt>' + time.toLocaleDateString() + '</td></tr>';
|
||||||
}
|
dateHeader = time.toLocaleDateString();
|
||||||
var icon = 'si3';
|
}
|
||||||
if (event.etype == 'user') icon = 'm2';
|
var icon = 'si3';
|
||||||
if (event.etype == 'server') icon = 'si3';
|
if (event.etype == 'user') icon = 'm2';
|
||||||
|
if (event.etype == 'server') icon = 'si3';
|
||||||
|
|
||||||
var msg = event.msg.split('(R)').join('®');
|
var msg = event.msg.split('(R)').join('®');
|
||||||
if (event.username && event.username != userinfo.name) { msg += ': ' + event.username; }
|
if (event.username && event.username != userinfo.name) { msg += ': ' + event.username; }
|
||||||
x += '<tr onmouseover=eventMouseHover(this,1) onmouseout=eventMouseHover(this,0) style=cursor:pointer><td style=width:18px><div class=' + icon + '></div></td><td class=g1 style=float:none> </td><td style=background-color:#C9C9C9>' + time.toLocaleTimeString() + ' - ' + msg + '</td><td class=g2 style=float:none> </td></tr><tr style=height:2px></tr>';
|
x += '<tr onmouseover=eventMouseHover(this,1) onmouseout=eventMouseHover(this,0) style=cursor:pointer><td style=width:18px><div class=' + icon + '></div></td><td class=g1 style=float:none> </td><td style=background-color:#C9C9C9>' + time.toLocaleTimeString() + ' - ' + msg + '</td><td class=g2 style=float:none> </td></tr><tr style=height:2px></tr>';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (dateHeader != null) x += '</table>';
|
if (dateHeader != null) x += '</table>';
|
||||||
if (x == '') x = "<br><i>No Events Found</i><br><br>";
|
if (x == '') x = "<br><i>No Events Found</i><br><br>";
|
||||||
|
@ -7169,10 +7204,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Server Statistics
|
// MyServer General
|
||||||
//
|
//
|
||||||
|
|
||||||
function setupServerStats() {
|
function setupGeneralServerStats() {
|
||||||
window.serverStatCpu = new Chart(document.getElementById('serverCpuChart').getContext('2d'), {
|
window.serverStatCpu = new Chart(document.getElementById('serverCpuChart').getContext('2d'), {
|
||||||
type: 'doughnut',
|
type: 'doughnut',
|
||||||
data: { datasets: [{ data: [0, 0], backgroundColor: ['#AAAAAA', '#00AA00'] }], labels: ['Used', 'Free'] },
|
data: { datasets: [{ data: [0, 0], backgroundColor: ['#AAAAAA', '#00AA00'] }], labels: ['Used', 'Free'] },
|
||||||
|
@ -7186,7 +7221,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastServerStats = null;
|
var lastServerStats = null;
|
||||||
function updateServerStats(message) {
|
function updateGeneralServerStats(message) {
|
||||||
if (message != null) { lastServerStats = message; } else { message = lastServerStats; }
|
if (message != null) { lastServerStats = message; } else { message = lastServerStats; }
|
||||||
if (message == null) return;
|
if (message == null) return;
|
||||||
|
|
||||||
|
@ -7220,6 +7255,131 @@
|
||||||
QH('serverStatsTable', x);
|
QH('serverStatsTable', x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// MyServer Stats
|
||||||
|
//
|
||||||
|
|
||||||
|
var serverTimelineStats = null;
|
||||||
|
var serverTimelineConfig = {
|
||||||
|
type: 'line',
|
||||||
|
data: { labels: [], datasets: [{ label: '', backgroundColor: 'rgba(255, 99, 132, .5)', borderColor: 'rgb(255, 99, 132)', data: [], fill: true }] },
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
xAxes: [{ type: 'time', time: { tooltipFormat: 'll HH:mm' }, display: true, scaleLabel: { display: false, labelString: '' } }],
|
||||||
|
yAxes: [{ type: 'linear', display: true, scaleLabel: { display: true, labelString: '' } }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function refreshServerTimelineStats(stats) { meshserver.send({ action: 'servertimelinestats', hours: 24 }); }
|
||||||
|
function pastDate(hours) { var t = new Date(); t.setTime(t.getTime() - (60 * 60 * 1000 * hours)); return t; }
|
||||||
|
function setServerTimelineStats(stats) { serverTimelineStats = stats; updateServerTimelineStats(); }
|
||||||
|
function addServerTimelineStats(stats) {
|
||||||
|
if (serverTimelineStats == null) return;
|
||||||
|
serverTimelineStats.push(stats);
|
||||||
|
var chartType = Q('p40type').value;
|
||||||
|
if (chartType == 0) {
|
||||||
|
serverTimelineConfig.data.datasets[0].data.push({ x: stats.time, y: stats.conn.ca });
|
||||||
|
serverTimelineConfig.data.datasets[1].data.push({ x: stats.time, y: stats.conn.cu });
|
||||||
|
serverTimelineConfig.data.datasets[2].data.push({ x: stats.time, y: stats.conn.us });
|
||||||
|
serverTimelineConfig.data.datasets[3].data.push({ x: stats.time, y: stats.conn.rs });
|
||||||
|
if (stats.conn.am != null) { serverTimelineConfig.data.datasets[4].data.push({ x: stats.time, y: stats.conn.am }); }
|
||||||
|
} else if (chartType == 1) {
|
||||||
|
serverTimelineConfig.data.datasets[0].data.push({ x: stats.time, y: stats.mem.external / (1024 * 1024) });
|
||||||
|
serverTimelineConfig.data.datasets[1].data.push({ x: stats.time, y: stats.mem.heapUsed / (1024 * 1024) });
|
||||||
|
serverTimelineConfig.data.datasets[2].data.push({ x: stats.time, y: stats.mem.heapTotal / (1024 * 1024) });
|
||||||
|
serverTimelineConfig.data.datasets[3].data.push({ x: stats.time, y: stats.mem.rss / (1024 * 1024) });
|
||||||
|
} /* else if (chartType == 2) {
|
||||||
|
serverTimelineConfig.data.datasets[0].data.push({ x: stats.time, y: stats.db.meshes });
|
||||||
|
serverTimelineConfig.data.datasets[1].data.push({ x: stats.time, y: stats.db.nodes });
|
||||||
|
serverTimelineConfig.data.datasets[2].data.push({ x: stats.time, y: stats.db.users });
|
||||||
|
serverTimelineConfig.data.datasets[3].data.push({ x: stats.time, y: stats.db.total });
|
||||||
|
} */
|
||||||
|
updateServerTimelineHours();
|
||||||
|
}
|
||||||
|
function updateServerTimelineHours() {
|
||||||
|
serverTimelineConfig.options.scales.yAxes[0].type = (Q('p40log').checked ? 'logarithmic' : 'linear');
|
||||||
|
serverTimelineConfig.options.scales.xAxes[0].time = { min: pastDate(Q('p40time').value) };
|
||||||
|
window.serverMainStats.update();
|
||||||
|
}
|
||||||
|
function setupServerTimelineStats() { window.serverMainStats = new Chart(document.getElementById('serverMainStats').getContext('2d'), serverTimelineConfig); }
|
||||||
|
|
||||||
|
function updateServerTimelineStats() {
|
||||||
|
var data, chartType = Q('p40type').value, timeAfter = pastDate(Q('p40time').value);
|
||||||
|
serverTimelineConfig.options.scales.xAxes[0].time = { min: timeAfter };
|
||||||
|
if (chartType == 0) { // Connections
|
||||||
|
serverTimelineConfig.options.scales.yAxes[0].scaleLabel.labelString = 'Connection Count';
|
||||||
|
data = {
|
||||||
|
labels: [pastDate(0), timeAfter],
|
||||||
|
datasets: [
|
||||||
|
{ label: 'Agents', data: [], backgroundColor: 'rgba(158, 151, 16, .1)', borderColor: 'rgb(158, 151, 16)', fill: true },
|
||||||
|
{ label: 'Users', data: [], backgroundColor: 'rgba(16, 84, 158, .1)', borderColor: 'rgb(16, 84, 158)', fill: true },
|
||||||
|
{ label: 'User Sessions', data: [], backgroundColor: 'rgba(255, 99, 132, .1)', borderColor: 'rgb(255, 99, 132)', fill: true },
|
||||||
|
{ label: 'Relay Sessions', data: [], backgroundColor: 'rgba(39, 158, 16, .1)', borderColor: 'rgb(39, 158, 16)', fill: true },
|
||||||
|
{ label: 'Intel AMT', data: [], backgroundColor: 'rgba(134, 16, 158, .1)', borderColor: 'rgb(134, 16, 158)', fill: true }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
for (var i = 0; i < serverTimelineStats.length; i++) {
|
||||||
|
var t = new Date(serverTimelineStats[i].time);
|
||||||
|
if (serverTimelineStats[i].conn) {
|
||||||
|
data.datasets[0].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].conn.ca });
|
||||||
|
data.datasets[1].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].conn.cu });
|
||||||
|
data.datasets[2].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].conn.us });
|
||||||
|
data.datasets[3].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].conn.rs });
|
||||||
|
if (serverTimelineStats[i].conn.am != null) { data.datasets[4].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].conn.am }); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (chartType == 1) { // Memory
|
||||||
|
serverTimelineConfig.options.scales.yAxes[0].scaleLabel.labelString = 'Megabytes';
|
||||||
|
data = {
|
||||||
|
labels: [pastDate(0), timeAfter],
|
||||||
|
datasets: [
|
||||||
|
{ label: 'External', data: [], backgroundColor: 'rgba(158, 151, 16, .1)', borderColor: 'rgb(158, 151, 16)', fill: true },
|
||||||
|
{ label: 'Heap Used', data: [], backgroundColor: 'rgba(16, 84, 158, .1)', borderColor: 'rgb(16, 84, 158)', fill: true },
|
||||||
|
{ label: 'Heap Total', data: [], backgroundColor: 'rgba(255, 99, 132, .1)', borderColor: 'rgb(255, 99, 132)', fill: true },
|
||||||
|
{ label: 'RSS', data: [], backgroundColor: 'rgba(39, 158, 16, .1)', borderColor: 'rgb(39, 158, 16)', fill: true }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
for (var i = 0; i < serverTimelineStats.length; i++) {
|
||||||
|
data.datasets[0].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].mem.external / (1024 * 1024) });
|
||||||
|
data.datasets[1].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].mem.heapUsed / (1024 * 1024) });
|
||||||
|
data.datasets[2].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].mem.heapTotal / (1024 * 1024) });
|
||||||
|
data.datasets[3].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].mem.rss / (1024 * 1024) });
|
||||||
|
}
|
||||||
|
} /*else if (chartType == 2) { // Database
|
||||||
|
serverTimelineConfig.options.scales.yAxes[0].scaleLabel.labelString = 'Records';
|
||||||
|
data = {
|
||||||
|
labels: [pastDate(0), timeAfter],
|
||||||
|
datasets: [
|
||||||
|
{ label: 'Groups', data: [], backgroundColor: 'rgba(158, 151, 16, .1)', borderColor: 'rgb(158, 151, 16)', fill: true },
|
||||||
|
{ label: 'Devices', data: [], backgroundColor: 'rgba(16, 84, 158, .1)', borderColor: 'rgb(16, 84, 158)', fill: true },
|
||||||
|
{ label: 'Users', data: [], backgroundColor: 'rgba(255, 99, 132, .1)', borderColor: 'rgb(255, 99, 132)', fill: true },
|
||||||
|
{ label: 'Records', data: [], backgroundColor: 'rgba(39, 158, 16, .1)', borderColor: 'rgb(39, 158, 16)', fill: true }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
for (var i = 0; i < serverTimelineStats.length; i++) {
|
||||||
|
data.datasets[0].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].db.meshes });
|
||||||
|
data.datasets[1].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].db.nodes });
|
||||||
|
data.datasets[2].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].db.users });
|
||||||
|
data.datasets[3].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].db.total });
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
serverTimelineConfig.data = data;
|
||||||
|
window.serverMainStats.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
function p40downloadEvents() {
|
||||||
|
var csv = "time, conn.agent, conn.users, conn.usersessions, conn.relaysession, conn.intelamt, mem.external, mem.heapused, mem.heaptotal, mem.rss\r\n";
|
||||||
|
for (var i = 0; i < serverTimelineStats.length; i++) {
|
||||||
|
if (serverTimelineStats[i].conn && serverTimelineStats[i].mem) {
|
||||||
|
csv += new Date(serverTimelineStats[i].time) + ', ' + serverTimelineStats[i].conn.ca + ', ' + serverTimelineStats[i].conn.cu + ', ' + serverTimelineStats[i].conn.us + ', ' + serverTimelineStats[i].conn.rs + ', ' + (serverTimelineStats[i].conn.am ? serverTimelineStats[i].conn.am : '') + ', ' + serverTimelineStats[i].mem.external + ', ' + serverTimelineStats[i].mem.heapUsed + ', ' + serverTimelineStats[i].mem.heapTotal + ', ' + serverTimelineStats[i].mem.rss + '\r\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
saveAs(new Blob([csv], { type: "application/octet-stream" }), "ServerStats.csv");
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// POPUP DIALOG
|
// POPUP DIALOG
|
||||||
//
|
//
|
||||||
|
@ -7274,7 +7434,7 @@
|
||||||
function go(x) {
|
function go(x) {
|
||||||
if (xxdialogMode || xxcurrentView == x) return;
|
if (xxdialogMode || xxcurrentView == x) return;
|
||||||
// Edit this line when adding a new screen
|
// Edit this line when adding a new screen
|
||||||
for (var i = 0; i < 32; i++) { QV('p' + i, i == x); }
|
for (var i = 0; i < 41; i++) { QV('p' + i, i == x); }
|
||||||
xxcurrentView = x;
|
xxcurrentView = x;
|
||||||
|
|
||||||
// Remove left bar selection
|
// Remove left bar selection
|
||||||
|
@ -7307,7 +7467,7 @@
|
||||||
|
|
||||||
// My Server
|
// My Server
|
||||||
QS('MainMenuMyServer').backgroundColor = (((x == 6) || (x == 115)) ? "#003366" : "#808080");
|
QS('MainMenuMyServer').backgroundColor = (((x == 6) || (x == 115)) ? "#003366" : "#808080");
|
||||||
if (((x == 6) || (x == 115))) { Q('LeftMenuMyServer').classList.add('lbbuttonsel', 'lbbuttonsel2'); }
|
if (((x == 6) || (x == 115) || (x == 40))) { Q('LeftMenuMyServer').classList.add('lbbuttonsel', 'lbbuttonsel2'); }
|
||||||
|
|
||||||
// column_l max-height
|
// column_l max-height
|
||||||
if (webPageFullScreen) {
|
if (webPageFullScreen) {
|
||||||
|
@ -7326,8 +7486,8 @@
|
||||||
QV('UserDummyMenuSpan', (x < 10) && (x != 6) && webPageFullScreen);
|
QV('UserDummyMenuSpan', (x < 10) && (x != 6) && webPageFullScreen);
|
||||||
QV('MeshSubMenuSpan', x >= 20 && x < 30);
|
QV('MeshSubMenuSpan', x >= 20 && x < 30);
|
||||||
QV('UserSubMenuSpan', x >= 30 && x < 40);
|
QV('UserSubMenuSpan', x >= 30 && x < 40);
|
||||||
QV('ServerSubMenuSpan', x == 6 || x == 115);
|
QV('ServerSubMenuSpan', x == 6 || x == 115 || x == 40);
|
||||||
var panels = { 10: 'MainDev', 11: 'MainDevDesktop', 12: 'MainDevTerminal', 13: 'MainDevFiles', 14: 'MainDevAmt', 15: 'MainDevConsole', 20: 'MeshGeneral', 30: 'UserGeneral', 31: 'UserEvents', 6: 'ServerGeneral', 115: 'ServerConsole' };
|
var panels = { 10: 'MainDev', 11: 'MainDevDesktop', 12: 'MainDevTerminal', 13: 'MainDevFiles', 14: 'MainDevAmt', 15: 'MainDevConsole', 20: 'MeshGeneral', 30: 'UserGeneral', 31: 'UserEvents', 6: 'ServerGeneral', 40: 'ServerStats', 115: 'ServerConsole' };
|
||||||
for (var i in panels) {
|
for (var i in panels) {
|
||||||
Q(panels[i]).classList.remove('style3x');
|
Q(panels[i]).classList.remove('style3x');
|
||||||
Q(panels[i]).classList.remove('style3sel');
|
Q(panels[i]).classList.remove('style3sel');
|
||||||
|
@ -7342,6 +7502,9 @@
|
||||||
|
|
||||||
if (x == 1) masterUpdate(4);
|
if (x == 1) masterUpdate(4);
|
||||||
|
|
||||||
|
// Fetch the server timeline stats if needed
|
||||||
|
if ((x == 40) && (serverTimelineStats == null)) { refreshServerTimelineStats(); }
|
||||||
|
|
||||||
// Update the web page title
|
// Update the web page title
|
||||||
if ((currentNode) && (x >= 10) && (x < 20)) { document.title = 'MeshCentral - ' + currentNode.name; } else { document.title = 'MeshCentral'; }
|
if ((currentNode) && (x >= 10) && (x < 20)) { document.title = 'MeshCentral - ' + currentNode.name; } else { document.title = 'MeshCentral'; }
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -260,7 +260,7 @@
|
||||||
var passRequirements = "{{{passRequirements}}}";
|
var passRequirements = "{{{passRequirements}}}";
|
||||||
if (passRequirements != "") { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); } else { passRequirements = {}; }
|
if (passRequirements != "") { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); } else { passRequirements = {}; }
|
||||||
var passRequirementsEx = ((passRequirements.min != null) || (passRequirements.max != null) || (passRequirements.upper != null) || (passRequirements.lower != null) || (passRequirements.numeric != null) || (passRequirements.nonalpha != null));
|
var passRequirementsEx = ((passRequirements.min != null) || (passRequirements.max != null) || (passRequirements.upper != null) || (passRequirements.lower != null) || (passRequirements.numeric != null) || (passRequirements.nonalpha != null));
|
||||||
var hardwareKeyChallenge = '{{{hkey}}}';
|
var hardwareKeyChallenge = decodeURIComponent('{{{hkey}}}');
|
||||||
var currentpanel = 0;
|
var currentpanel = 0;
|
||||||
|
|
||||||
function startup() {
|
function startup() {
|
||||||
|
@ -287,7 +287,34 @@
|
||||||
|
|
||||||
if ('{{loginmode}}' == '4') {
|
if ('{{loginmode}}' == '4') {
|
||||||
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
|
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
|
||||||
if ((hardwareKeyChallenge != null) && u2fSupported()) {
|
if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) {
|
||||||
|
hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer;
|
||||||
|
|
||||||
|
var publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout }
|
||||||
|
for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) {
|
||||||
|
publicKeyCredentialRequestOptions.allowCredentials.push(
|
||||||
|
{ id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc'], }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// New WebAuthn hardware keys
|
||||||
|
navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then(
|
||||||
|
function (rawAssertion) {
|
||||||
|
var assertion = {
|
||||||
|
id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))),
|
||||||
|
clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))),
|
||||||
|
userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))),
|
||||||
|
signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))),
|
||||||
|
authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))),
|
||||||
|
};
|
||||||
|
Q('hwtokenInput').value = JSON.stringify(assertion);
|
||||||
|
QE('tokenOkButton', true);
|
||||||
|
Q('tokenOkButton').click();
|
||||||
|
},
|
||||||
|
function (error) { console.log('credentials-get error', error); }
|
||||||
|
);
|
||||||
|
} else if ((hardwareKeyChallenge != null) && u2fSupported()) {
|
||||||
|
// Old U2F hardware keys
|
||||||
window.u2f.sign(hardwareKeyChallenge.appId, hardwareKeyChallenge.challenge, hardwareKeyChallenge.registeredKeys, function (authResponse) {
|
window.u2f.sign(hardwareKeyChallenge.appId, hardwareKeyChallenge.challenge, hardwareKeyChallenge.registeredKeys, function (authResponse) {
|
||||||
if ((currentpanel == 4) && authResponse.signatureData) {
|
if ((currentpanel == 4) && authResponse.signatureData) {
|
||||||
Q('hwtokenInput').value = JSON.stringify(authResponse);
|
Q('hwtokenInput').value = JSON.stringify(authResponse);
|
||||||
|
@ -300,7 +327,34 @@
|
||||||
|
|
||||||
if ('{{loginmode}}' == '5') {
|
if ('{{loginmode}}' == '5') {
|
||||||
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
|
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
|
||||||
if ((hardwareKeyChallenge != null) && u2fSupported()) {
|
if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) {
|
||||||
|
hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer;
|
||||||
|
|
||||||
|
var publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout }
|
||||||
|
for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) {
|
||||||
|
publicKeyCredentialRequestOptions.allowCredentials.push(
|
||||||
|
{ id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc'], }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// New WebAuthn hardware keys
|
||||||
|
navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then(
|
||||||
|
function (rawAssertion) {
|
||||||
|
var assertion = {
|
||||||
|
id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))),
|
||||||
|
clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))),
|
||||||
|
userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))),
|
||||||
|
signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))),
|
||||||
|
authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))),
|
||||||
|
};
|
||||||
|
Q('resetHwtokenInput').value = JSON.stringify(assertion);
|
||||||
|
QE('resetTokenOkButton', true);
|
||||||
|
Q('resetTokenOkButton').click();
|
||||||
|
},
|
||||||
|
function (error) { console.log('credentials-get error', error); }
|
||||||
|
);
|
||||||
|
} else if ((hardwareKeyChallenge != null) && u2fSupported()) {
|
||||||
|
// Old U2F hardware keys
|
||||||
window.u2f.sign(hardwareKeyChallenge.appId, hardwareKeyChallenge.challenge, hardwareKeyChallenge.registeredKeys, function (authResponse) {
|
window.u2f.sign(hardwareKeyChallenge.appId, hardwareKeyChallenge.challenge, hardwareKeyChallenge.registeredKeys, function (authResponse) {
|
||||||
if ((currentpanel == 5) && authResponse.signatureData) {
|
if ((currentpanel == 5) && authResponse.signatureData) {
|
||||||
Q('resetHwtokenInput').value = JSON.stringify(authResponse);
|
Q('resetHwtokenInput').value = JSON.stringify(authResponse);
|
||||||
|
|
|
@ -87,7 +87,7 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script type="text/javascript" src="scripts/common-0.0.1.js"></script>
|
<script type="text/javascript" src="scripts/common-0.0.1.js"></script>
|
||||||
<script type="text/javascript" src="scripts/u2f-api.js"></script>
|
<script keeplink=1 type="text/javascript" src="scripts/u2f-api.js"></script>
|
||||||
<title>MeshCentral - Login</title>
|
<title>MeshCentral - Login</title>
|
||||||
</head>
|
</head>
|
||||||
<body onload="if (typeof(startup) !== 'undefined') startup();">
|
<body onload="if (typeof(startup) !== 'undefined') startup();">
|
||||||
|
@ -110,7 +110,7 @@
|
||||||
<div id="welcomeText" style="display:none">Connect to your home or office devices from anywhere in the world using <a href="http://www.meshcommander.com/meshcentral2">MeshCentral</a>, the real time, open source remote monitoring and management web site. You will need to download and install a management agent on your computers. Once installed, computers will show up in the "My Devices" section of this web site and you will be able to monitor them and take control of them.</div>
|
<div id="welcomeText" style="display:none">Connect to your home or office devices from anywhere in the world using <a href="http://www.meshcommander.com/meshcentral2">MeshCentral</a>, the real time, open source remote monitoring and management web site. You will need to download and install a management agent on your computers. Once installed, computers will show up in the "My Devices" section of this web site and you will be able to monitor them and take control of them.</div>
|
||||||
<table id="centralTable" style=width:100%>
|
<table id="centralTable" style=width:100%>
|
||||||
<tr>
|
<tr>
|
||||||
<td id="welcomeimage" align="right">
|
<td id="welcomeimage" align="right" style="display:none">
|
||||||
<picture>
|
<picture>
|
||||||
<img alt="" width=359 height=310 src=welcome.jpg />
|
<img alt="" width=359 height=310 src=welcome.jpg />
|
||||||
</picture>
|
</picture>
|
||||||
|
@ -332,7 +332,7 @@
|
||||||
var newAccountPass = parseInt('{{{newAccountPass}}}');
|
var newAccountPass = parseInt('{{{newAccountPass}}}');
|
||||||
var emailCheck = ('{{{emailcheck}}}' == 'true');
|
var emailCheck = ('{{{emailcheck}}}' == 'true');
|
||||||
var passRequirements = "{{{passRequirements}}}";
|
var passRequirements = "{{{passRequirements}}}";
|
||||||
var hardwareKeyChallenge = '{{{hkey}}}';
|
var hardwareKeyChallenge = decodeURIComponent('{{{hkey}}}');
|
||||||
if (passRequirements != "") { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); } else { passRequirements = {}; }
|
if (passRequirements != "") { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); } else { passRequirements = {}; }
|
||||||
var passRequirementsEx = ((passRequirements.min != null) || (passRequirements.max != null) || (passRequirements.upper != null) || (passRequirements.lower != null) || (passRequirements.numeric != null) || (passRequirements.nonalpha != null));
|
var passRequirementsEx = ((passRequirements.min != null) || (passRequirements.max != null) || (passRequirements.upper != null) || (passRequirements.lower != null) || (passRequirements.numeric != null) || (passRequirements.nonalpha != null));
|
||||||
var features = parseInt('{{{features}}}');
|
var features = parseInt('{{{features}}}');
|
||||||
|
@ -373,12 +373,12 @@
|
||||||
if ('{{loginmode}}' == '4') {
|
if ('{{loginmode}}' == '4') {
|
||||||
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
|
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
|
||||||
if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) {
|
if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) {
|
||||||
hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), c => c.charCodeAt(0)).buffer;
|
hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer;
|
||||||
|
|
||||||
const publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout }
|
var publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout }
|
||||||
for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) {
|
for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) {
|
||||||
publicKeyCredentialRequestOptions.allowCredentials.push(
|
publicKeyCredentialRequestOptions.allowCredentials.push(
|
||||||
{ id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), c => c.charCodeAt(0)), type: 'public-key', transports: ['usb', 'ble', 'nfc'], }
|
{ id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc'], }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,11 +386,11 @@
|
||||||
navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then(
|
navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then(
|
||||||
function (rawAssertion) {
|
function (rawAssertion) {
|
||||||
var assertion = {
|
var assertion = {
|
||||||
id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))), //base64encode(rawAssertion.rawId),
|
id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))),
|
||||||
clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))), //arrayBufferToString(rawAssertion.response.clientDataJSON),
|
clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))),
|
||||||
userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))), //base64encode(rawAssertion.response.userHandle),
|
userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))),
|
||||||
signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))), //base64encode(rawAssertion.response.signature),
|
signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))),
|
||||||
authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))), //base64encode(rawAssertion.response.authenticatorData)
|
authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))),
|
||||||
};
|
};
|
||||||
Q('hwtokenInput').value = JSON.stringify(assertion);
|
Q('hwtokenInput').value = JSON.stringify(assertion);
|
||||||
QE('tokenOkButton', true);
|
QE('tokenOkButton', true);
|
||||||
|
@ -412,7 +412,34 @@
|
||||||
|
|
||||||
if ('{{loginmode}}' == '5') {
|
if ('{{loginmode}}' == '5') {
|
||||||
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
|
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
|
||||||
if ((hardwareKeyChallenge != null) && u2fSupported()) {
|
if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) {
|
||||||
|
hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer;
|
||||||
|
|
||||||
|
var publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout }
|
||||||
|
for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) {
|
||||||
|
publicKeyCredentialRequestOptions.allowCredentials.push(
|
||||||
|
{ id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc'], }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// New WebAuthn hardware keys
|
||||||
|
navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then(
|
||||||
|
function (rawAssertion) {
|
||||||
|
var assertion = {
|
||||||
|
id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))),
|
||||||
|
clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))),
|
||||||
|
userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))),
|
||||||
|
signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))),
|
||||||
|
authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))),
|
||||||
|
};
|
||||||
|
Q('resetHwtokenInput').value = JSON.stringify(assertion);
|
||||||
|
QE('resetTokenOkButton', true);
|
||||||
|
Q('resetTokenOkButton').click();
|
||||||
|
},
|
||||||
|
function (error) { console.log('credentials-get error', error); }
|
||||||
|
);
|
||||||
|
} else if ((hardwareKeyChallenge != null) && u2fSupported()) {
|
||||||
|
// Old U2F hardware keys
|
||||||
window.u2f.sign(hardwareKeyChallenge.appId, hardwareKeyChallenge.challenge, hardwareKeyChallenge.registeredKeys, function (authResponse) {
|
window.u2f.sign(hardwareKeyChallenge.appId, hardwareKeyChallenge.challenge, hardwareKeyChallenge.registeredKeys, function (authResponse) {
|
||||||
if ((currentpanel == 5) && authResponse.signatureData) {
|
if ((currentpanel == 5) && authResponse.signatureData) {
|
||||||
Q('resetHwtokenInput').value = JSON.stringify(authResponse);
|
Q('resetHwtokenInput').value = JSON.stringify(authResponse);
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -560,8 +560,7 @@
|
||||||
displayLocalVideo(true);
|
displayLocalVideo(true);
|
||||||
}
|
}
|
||||||
for (var i in tracks) { webrtc.addTrack(tracks[i], localStream); }
|
for (var i in tracks) { webrtc.addTrack(tracks[i], localStream); }
|
||||||
})
|
}, function (err) {
|
||||||
.catch(function (err) {
|
|
||||||
displayControl(err.message + '.');
|
displayControl(err.message + '.');
|
||||||
hangUpButtonClick(1);
|
hangUpButtonClick(1);
|
||||||
});
|
});
|
||||||
|
|
66
webserver.js
66
webserver.js
|
@ -62,7 +62,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||||
const constants = (obj.crypto.constants ? obj.crypto.constants : require('constants')); // require('constants') is deprecated in Node 11.10, use require('crypto').constants instead.
|
const constants = (obj.crypto.constants ? obj.crypto.constants : require('constants')); // require('constants') is deprecated in Node 11.10, use require('crypto').constants instead.
|
||||||
|
|
||||||
// Setup WebAuthn / FIDO2
|
// Setup WebAuthn / FIDO2
|
||||||
try { const { Fido2Lib } = require("@davedoesdev/fido2-lib"); obj.f2l = new Fido2Lib({ attestation: "none" }); } catch (ex) { console.log(ex); }
|
try { const { Fido2Lib } = require("@davedoesdev/fido2-lib"); obj.f2l = new Fido2Lib({ attestation: "none" }); } catch (ex) { }
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
obj.parent = parent;
|
obj.parent = parent;
|
||||||
|
@ -328,12 +328,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||||
|
|
||||||
// Return true if this user has 2-step auth active
|
// Return true if this user has 2-step auth active
|
||||||
function checkUserOneTimePasswordRequired(domain, user) {
|
function checkUserOneTimePasswordRequired(domain, user) {
|
||||||
return ((user.otpsecret != null) || ((user.otphkeys != null) && (user.otphkeys.length > 0)));
|
return ((parent.config.settings.no2factorauth !== true) && ((user.otpsecret != null) || ((user.otphkeys != null) && (user.otphkeys.length > 0))));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the 2-step auth token
|
// Check the 2-step auth token
|
||||||
function checkUserOneTimePassword(req, domain, user, token, hwtoken, func) {
|
function checkUserOneTimePassword(req, domain, user, token, hwtoken, func) {
|
||||||
const twoStepLoginSupported = ((domain.auth != 'sspi') && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.nousers !== true));
|
const twoStepLoginSupported = ((domain.auth != 'sspi') && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.nousers !== true) && (parent.config.settings.no2factorauth !== true));
|
||||||
if (twoStepLoginSupported == false) { func(true); return; };
|
if (twoStepLoginSupported == false) { func(true); return; };
|
||||||
|
|
||||||
// Check hardware key
|
// Check hardware key
|
||||||
|
@ -396,7 +396,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||||
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 1) { u2fKeys.push(user.otphkeys[i]); } }
|
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 1) { u2fKeys.push(user.otphkeys[i]); } }
|
||||||
if (u2fKeys.length > 0) {
|
if (u2fKeys.length > 0) {
|
||||||
// Check authentication response
|
// Check authentication response
|
||||||
require('authdog').finishAuthentication(req.session.u2fchallenge, authResponse, u2fKeys).then(function (authenticationStatus) { func(true); }, function (error) { func(false); });
|
var authdoglib = null;
|
||||||
|
try { authdoglib = require('authdog'); } catch (ex) { }
|
||||||
|
if (authdoglib == null) { func(false); } else {
|
||||||
|
authdoglib.finishAuthentication(req.session.u2fchallenge, authResponse, u2fKeys).then(function (authenticationStatus) { func(true); }, function (error) { console.log(error); func(false); });
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -458,24 +462,28 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all U2F keys
|
var authdoglib = null;
|
||||||
var u2fKeys = [];
|
try { authdoglib = require('authdog'); } catch (ex) { }
|
||||||
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 1) { u2fKeys.push(user.otphkeys[i]); } }
|
if (authdoglib != null) {
|
||||||
|
// Get all U2F keys
|
||||||
|
var u2fKeys = [];
|
||||||
|
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 1) { u2fKeys.push(user.otphkeys[i]); } }
|
||||||
|
|
||||||
// Generate a U2F challenge
|
// Generate a U2F challenge
|
||||||
if (u2fKeys.length > 0) {
|
if (u2fKeys.length > 0) {
|
||||||
require('authdog').startAuthentication('https://' + obj.parent.certificates.CommonName, u2fKeys, { requestId: 0, timeoutSeconds: 60 }).then(function (registrationRequest) {
|
authdoglib.startAuthentication('https://' + obj.parent.certificates.CommonName, u2fKeys, { requestId: 0, timeoutSeconds: 60 }).then(function (registrationRequest) {
|
||||||
// Save authentication request to session for later use
|
// Save authentication request to session for later use
|
||||||
req.session.u2fchallenge = registrationRequest;
|
req.session.u2fchallenge = registrationRequest;
|
||||||
|
|
||||||
// Send authentication request to client
|
// Send authentication request to client
|
||||||
func(JSON.stringify(registrationRequest));
|
func(JSON.stringify(registrationRequest));
|
||||||
}, function (error) {
|
}, function (error) {
|
||||||
// Handle authentication request error
|
// Handle authentication request error
|
||||||
|
func('');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
func('');
|
func('');
|
||||||
});
|
}
|
||||||
} else {
|
|
||||||
func('');
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
func('');
|
func('');
|
||||||
|
@ -1149,19 +1157,19 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||||
if (obj.args.nousers == true) { features += 0x00000004; } // Single user mode
|
if (obj.args.nousers == true) { features += 0x00000004; } // Single user mode
|
||||||
if (domain.userQuota == -1) { features += 0x00000008; } // No server files mode
|
if (domain.userQuota == -1) { features += 0x00000008; } // No server files mode
|
||||||
if (obj.args.mpstlsoffload) { features += 0x00000010; } // No mutual-auth CIRA
|
if (obj.args.mpstlsoffload) { features += 0x00000010; } // No mutual-auth CIRA
|
||||||
if ((parent.config != null) && (parent.config.settings != null) && (parent.config.settings.allowframing == true)) { features += 0x00000020; } // Allow site within iframe
|
if (parent.config.settings.allowframing == true) { features += 0x00000020; } // Allow site within iframe
|
||||||
if ((obj.parent.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true)) { features += 0x00000040; } // Email invites
|
if ((obj.parent.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true)) { features += 0x00000040; } // Email invites
|
||||||
if (obj.args.webrtc == true) { features += 0x00000080; } // Enable WebRTC (Default false for now)
|
if (obj.args.webrtc == true) { features += 0x00000080; } // Enable WebRTC (Default false for now)
|
||||||
if (obj.args.clickonce !== false) { features += 0x00000100; } // Enable ClickOnce (Default true)
|
if (obj.args.clickonce !== false) { features += 0x00000100; } // Enable ClickOnce (Default true)
|
||||||
if (obj.args.allowhighqualitydesktop == true) { features += 0x00000200; } // Enable AllowHighQualityDesktop (Default false)
|
if (obj.args.allowhighqualitydesktop == true) { features += 0x00000200; } // Enable AllowHighQualityDesktop (Default false)
|
||||||
if (obj.args.lanonly == true || obj.args.mpsport == 0) { features += 0x00000400; } // No CIRA
|
if (obj.args.lanonly == true || obj.args.mpsport == 0) { features += 0x00000400; } // No CIRA
|
||||||
if ((obj.parent.serverSelfWriteAllowed == true) && (user != null) && (user.siteadmin == 0xFFFFFFFF)) { features += 0x00000800; } // Server can self-write (Allows self-update)
|
if ((obj.parent.serverSelfWriteAllowed == true) && (user != null) && (user.siteadmin == 0xFFFFFFFF)) { features += 0x00000800; } // Server can self-write (Allows self-update)
|
||||||
if ((domain.auth != 'sspi') && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.nousers !== true)) { features += 0x00001000; } // 2-step login supported
|
if ((parent.config.settings.no2factorauth !== true) && (domain.auth != 'sspi') && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.nousers !== true)) { features += 0x00001000; } // 2-step login supported
|
||||||
if (domain.agentnoproxy === true) { features += 0x00002000; } // Indicates that agents should be installed without using a HTTP proxy
|
if (domain.agentnoproxy === true) { features += 0x00002000; } // Indicates that agents should be installed without using a HTTP proxy
|
||||||
if (domain.yubikey && domain.yubikey.id && domain.yubikey.secret) { features += 0x00004000; } // Indicates Yubikey support
|
if ((parent.config.settings.no2factorauth !== true) && domain.yubikey && domain.yubikey.id && domain.yubikey.secret) { features += 0x00004000; } // Indicates Yubikey support
|
||||||
if (domain.geolocation == true) { features += 0x00008000; } // Enable geo-location features
|
if (domain.geolocation == true) { features += 0x00008000; } // Enable geo-location features
|
||||||
if ((domain.passwordrequirements != null) && (domain.passwordrequirements.hint === true)) { features += 0x00010000; } // Enable password hints
|
if ((domain.passwordrequirements != null) && (domain.passwordrequirements.hint === true)) { features += 0x00010000; } // Enable password hints
|
||||||
if (obj.f2l != null) { features += 0x00020000; } // Enable WebAuthn/FIDO2 support
|
if ((parent.config.settings.no2factorauth !== true) && (obj.f2l != null)) { features += 0x00020000; } // Enable WebAuthn/FIDO2 support
|
||||||
|
|
||||||
// Create a authentication cookie
|
// Create a authentication cookie
|
||||||
const authCookie = obj.parent.encodeCookie({ userid: user._id, domainid: domain.id }, obj.parent.loginCookieEncryptionKey);
|
const authCookie = obj.parent.encodeCookie({ userid: user._id, domainid: domain.id }, obj.parent.loginCookieEncryptionKey);
|
||||||
|
@ -1187,9 +1195,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||||
// If this is a 2 factor auth request, look for a hardware key challenge.
|
// If this is a 2 factor auth request, look for a hardware key challenge.
|
||||||
// Normal login 2 factor request
|
// Normal login 2 factor request
|
||||||
if ((req.session.loginmode == '4') && (req.session.tokenusername)) {
|
if ((req.session.loginmode == '4') && (req.session.tokenusername)) {
|
||||||
var user = obj.users['user/' + domain.id + '/' + req.session.tokenusername];
|
var user = obj.users['user/' + domain.id + '/' + req.session.tokenusername.toLowerCase()];
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
getHardwareKeyChallenge(req, domain, user, function (u2fChallenge) { handleRootRequestLogin(req, res, domain, u2fChallenge, passRequirements); });
|
getHardwareKeyChallenge(req, domain, user, function (hwchallenge) { handleRootRequestLogin(req, res, domain, hwchallenge, passRequirements); });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1202,7 +1210,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||||
} else {
|
} else {
|
||||||
var user = obj.users[docs[0]._id];
|
var user = obj.users[docs[0]._id];
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
getHardwareKeyChallenge(req, domain, user, function (u2fChallenge) { handleRootRequestLogin(req, res, domain, u2fChallenge, passRequirements); });
|
getHardwareKeyChallenge(req, domain, user, function (hwchallenge) { handleRootRequestLogin(req, res, domain, hwchallenge, passRequirements); });
|
||||||
} else {
|
} else {
|
||||||
req.session = null;
|
req.session = null;
|
||||||
res.redirect(domain.url);
|
res.redirect(domain.url);
|
||||||
|
@ -1240,14 +1248,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||||
if (obj.args.minify && !req.query.nominify) {
|
if (obj.args.minify && !req.query.nominify) {
|
||||||
// Try to server the minified version if we can.
|
// Try to server the minified version if we can.
|
||||||
try {
|
try {
|
||||||
res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile-min' : 'login-min'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: hardwareKeyChallenge, message: message, passhint: passhint, welcometext: domain.welcometext?encodeURIComponent(domain.welcometext):null });
|
res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile-min' : 'login-min'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: encodeURIComponent(hardwareKeyChallenge), message: message, passhint: passhint, welcometext: domain.welcometext?encodeURIComponent(domain.welcometext):null });
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
// In case of an exception, serve the non-minified version.
|
// In case of an exception, serve the non-minified version.
|
||||||
res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile' : 'login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: hardwareKeyChallenge, message: message, passhint: passhint, welcometext: domain.welcometext ? encodeURIComponent(domain.welcometext) : null });
|
res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile' : 'login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: encodeURIComponent(hardwareKeyChallenge), message: message, passhint: passhint, welcometext: domain.welcometext ? encodeURIComponent(domain.welcometext) : null });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Serve non-minified version of web pages.
|
// Serve non-minified version of web pages.
|
||||||
res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile' : 'login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: hardwareKeyChallenge, message: message, passhint: passhint, welcometext: domain.welcometext ? encodeURIComponent(domain.welcometext) : null });
|
res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile' : 'login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: encodeURIComponent(hardwareKeyChallenge), message: message, passhint: passhint, welcometext: domain.welcometext ? encodeURIComponent(domain.welcometext) : null });
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue