Merge pull request #1 from Ylianst/master
updating before my new branch
This commit is contained in:
commit
c1fb7529cf
|
@ -32,7 +32,6 @@
|
|||
<Compile Include="agents\modules_meshcmd\amt-wsman.js" />
|
||||
<Compile Include="agents\modules_meshcmd\amt-xml.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-manager.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-xml.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\monitor-border.js" />
|
||||
<Compile Include="agents\modules_meshcore\monitor-info.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\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.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_min\amt-lme.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
|
||||
function onVerifyServer(clientName, certs) {
|
||||
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'; }
|
||||
}
|
||||
|
||||
// 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 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'); }
|
||||
|
@ -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 (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 = {}; }
|
||||
var settings2 = {}; for (var i in settings) { settings2[i.toLowerCase()] = settings[i]; } settings = settings2;
|
||||
|
||||
// Set the arguments
|
||||
if ((typeof args.action) == 'string') { settings.action = args.action; }
|
||||
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.password) == 'string') { settings.password = args.password; }
|
||||
if ((typeof args.user) == 'string') { settings.username = args.user; }
|
||||
if ((typeof args.pass) == 'string') { settings.password = args.pass; }
|
||||
if ((typeof args.host) == 'string') { settings.hostname = args.host; }
|
||||
if ((typeof args.hostname) == 'string') { settings.hostname = args.hostname; }
|
||||
if ((typeof args.serverid) == 'string') { settings.serverId = args.serverid; }
|
||||
if ((typeof args.serverhttpshash) == 'string') { settings.serverHttpsHash = args.serverhttpshash; }
|
||||
if ((typeof args.remoteport) == 'string') { settings.remotePort = parseInt(args.remoteport); }
|
||||
if ((typeof args.serverid) == 'string') { settings.serverid = args.serverid; }
|
||||
if ((typeof args.serverhttpshash) == 'string') { settings.serverhttpshash = args.serverhttpshash; }
|
||||
if ((typeof args.remoteport) == 'string') { settings.remoteport = parseInt(args.remoteport); }
|
||||
if ((typeof args.out) == 'string') { settings.output = args.out; }
|
||||
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.agent) == 'string') { settings.agent = args.agent; }
|
||||
if ((typeof args.proxy) == 'string') { settings.proxy = args.proxy; }
|
||||
if (args.debug) { try { waitForDebugger(); } catch (e) { } }
|
||||
if (args.noconsole) { settings.noconsole = true; }
|
||||
if (args.nocommander) { settings.noconsole = true; }
|
||||
|
@ -263,6 +265,15 @@ function run(argv) {
|
|||
}
|
||||
settings.action = settings.action.toLowerCase();
|
||||
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') {
|
||||
// Display SM BIOS tables in raw form
|
||||
SMBiosTables = require('smbios');
|
||||
|
@ -283,14 +294,14 @@ function run(argv) {
|
|||
});
|
||||
} else if (settings.action == 'route') {
|
||||
// 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.remoteNodeId == null) || (typeof settings.remoteNodeId != 'string')) { console.log('No or invalid \"remoteNodeId\" specified.'); 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.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.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.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.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.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
|
||||
} 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
|
||||
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.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.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();
|
||||
} else if (settings.action == 'amtuuid') {
|
||||
// Start running
|
||||
|
@ -751,7 +762,7 @@ function startMeScriptEx() {
|
|||
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; }
|
||||
} else {
|
||||
scriptData = settings.scriptJSON;
|
||||
scriptData = settings.scriptjson;
|
||||
}
|
||||
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; }
|
||||
|
@ -1362,12 +1373,12 @@ function processLmsControlData(data) {
|
|||
function startRouter() {
|
||||
tcpserver = net.createServer(OnTcpClientConnected);
|
||||
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.
|
||||
if (settings.remoteName == null) {
|
||||
console.log('Redirecting local port ' + settings.localPort + ' to remote port ' + settings.remotePort + '.');
|
||||
if (settings.remotename == null) {
|
||||
console.log('Redirecting local port ' + settings.localport + ' to remote port ' + settings.remoteport + '.');
|
||||
} 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.');
|
||||
|
||||
|
@ -1384,7 +1395,7 @@ function OnTcpClientConnected(c) {
|
|||
c.on('end', function () { disconnectTunnel(this, this.websocket, 'Client closed'); });
|
||||
c.pause();
|
||||
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; }
|
||||
options.checkServerIdentity = onVerifyServer;
|
||||
options.rejectUnauthorized = false;
|
||||
|
@ -1440,7 +1451,7 @@ function discoverMeshServerOnce() {
|
|||
multicastSockets[i].addMembership(membershipIPv4);
|
||||
//multicastSockets[i].setMulticastLoopback(true);
|
||||
multicastSockets[i].once('message', OnMulticastMessage);
|
||||
multicastSockets[i].send(settings.serverId, 16989, membershipIPv4);
|
||||
multicastSockets[i].send(settings.serverid, 16989, membershipIPv4);
|
||||
} catch (e) { }
|
||||
}
|
||||
}
|
||||
|
@ -1451,9 +1462,9 @@ function discoverMeshServerOnce() {
|
|||
// Called when a multicast packet is received
|
||||
function OnMulticastMessage(msg, rinfo) {
|
||||
var m = msg.toString().split('|');
|
||||
if ((m.length == 3) && (m[0] == 'MeshCentral2') && (m[1] == settings.serverId)) {
|
||||
settings.serverUrl = m[2].replace('%s', rinfo.address).replace('/agent.ashx', '/meshrelay.ashx');
|
||||
console.log('Found server at ' + settings.serverUrl + '.');
|
||||
if ((m.length == 3) && (m[0] == 'MeshCentral2') && (m[1] == settings.serverid)) {
|
||||
settings.serverurl = m[2].replace('%s', rinfo.address).replace('/agent.ashx', '/meshrelay.ashx');
|
||||
console.log('Found server at ' + settings.serverurl + '.');
|
||||
if (discoveryInterval != null) { clearInterval(discoveryInterval); discoveryInterval = null; }
|
||||
startRouter();
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -21,6 +21,7 @@ start() {
|
|||
fi
|
||||
echo 'Starting service…' >&2
|
||||
local CMD="$SCRIPT &> \"$LOGFILE\" & echo \$!"
|
||||
cd /usr/local/mesh
|
||||
su -c "$CMD" $RUNAS > "$PIDFILE"
|
||||
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 _xmlTraverseAllRec(nodes, func) { for (var i in nodes) { func(nodes[i]); if (nodes[i].childNodes) { _xmlTraverseAllRec(nodes[i].childNodes, func); } } }
|
||||
function _turnToXmlRec(text) {
|
||||
var elementStack = new _treeBuilder(), lastElement = null, x1 = text.split('<'), ret = [], element = null, currentElementName = null;
|
||||
for (var i in x1) {
|
||||
var x2 = x1[i].split('>'), x3 = x2[0].split(' '), elementName = x3[0];
|
||||
if ((elementName.length > 0) && (elementName[0] != '?')) {
|
||||
if (elementName[0] != '/') {
|
||||
var attributes = [], localName, localname2 = elementName.split(' ')[0].split(':'), localName = (localname2.length > 1) ? localname2[1] : localname2[0];
|
||||
Object.defineProperty(attributes, "get",
|
||||
{
|
||||
value: function () {
|
||||
if (arguments.length == 1) {
|
||||
for (var a in this) { if (this[a].name == arguments[0]) { return (this[a]); } }
|
||||
}
|
||||
else if (arguments.length == 2) {
|
||||
for (var a in this) { if (this[a].name == arguments[1] && (arguments[0] == '*' || this[a].namespace == arguments[0])) { return (this[a]); } }
|
||||
}
|
||||
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) {
|
||||
var skip = false;
|
||||
for (var j in x3) {
|
||||
if (x3[j] == '/') {
|
||||
// This is an empty Element
|
||||
elementStack.peek().namespace = elementStack.peek().name == elementStack.peek().localName ? elementStack.getNamespace('*') : elementStack.getNamespace(elementStack.peek().name.substring(0, elementStack.peek().name.indexOf(':')));
|
||||
elementStack.peek().textContent = '';
|
||||
lastElement = elementStack.pop();
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
var k = x3[j].indexOf('=');
|
||||
if (k > 0) {
|
||||
var attrName = x3[j].substring(0, k);
|
||||
var attrValue = x3[j].substring(k + 2, x3[j].length - 1);
|
||||
var attrNS = elementStack.getNamespace('*');
|
||||
|
||||
if (attrName == 'xmlns') {
|
||||
elementStack.addNamespace('*', attrValue);
|
||||
attrNS = attrValue;
|
||||
} else if (attrName.startsWith('xmlns:')) {
|
||||
elementStack.addNamespace(attrName.substring(6), attrValue);
|
||||
} else {
|
||||
var ax = attrName.split(':');
|
||||
if (ax.length == 2) { attrName = ax[1]; attrNS = elementStack.getNamespace(ax[0]); }
|
||||
try {
|
||||
if (text == null) return null;
|
||||
var elementStack = new _treeBuilder(), lastElement = null, x1 = text.split('<'), ret = [], element = null, currentElementName = null;
|
||||
for (var i in x1) {
|
||||
var x2 = x1[i].split('>'), x3 = x2[0].split(' '), elementName = x3[0];
|
||||
if ((elementName.length > 0) && (elementName[0] != '?')) {
|
||||
if (elementName[0] != '/') {
|
||||
var attributes = [], localName, localname2 = elementName.split(' ')[0].split(':'), localName = (localname2.length > 1) ? localname2[1] : localname2[0];
|
||||
Object.defineProperty(attributes, "get",
|
||||
{
|
||||
value: function () {
|
||||
if (arguments.length == 1) {
|
||||
for (var a in this) { if (this[a].name == arguments[0]) { return (this[a]); } }
|
||||
}
|
||||
else if (arguments.length == 2) {
|
||||
for (var a in this) { if (this[a].name == arguments[1] && (arguments[0] == '*' || this[a].namespace == arguments[0])) { return (this[a]); } }
|
||||
}
|
||||
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) {
|
||||
var skip = false;
|
||||
for (var j in x3) {
|
||||
if (x3[j] == '/') {
|
||||
// This is an empty Element
|
||||
elementStack.peek().namespace = elementStack.peek().name == elementStack.peek().localName ? elementStack.getNamespace('*') : elementStack.getNamespace(elementStack.peek().name.substring(0, elementStack.peek().name.indexOf(':')));
|
||||
elementStack.peek().textContent = '';
|
||||
lastElement = elementStack.pop();
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
var k = x3[j].indexOf('=');
|
||||
if (k > 0) {
|
||||
var attrName = x3[j].substring(0, k);
|
||||
var attrValue = x3[j].substring(k + 2, x3[j].length - 1);
|
||||
var attrNS = elementStack.getNamespace('*');
|
||||
|
||||
if (attrName == 'xmlns') {
|
||||
elementStack.addNamespace('*', attrValue);
|
||||
attrNS = attrValue;
|
||||
} else if (attrName.startsWith('xmlns:')) {
|
||||
elementStack.addNamespace(attrName.substring(6), attrValue);
|
||||
} 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]; }
|
||||
} else { 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(':')));
|
||||
if (x2[1]) { elementStack.peek().textContent = x2[1]; }
|
||||
} else { lastElement = elementStack.pop(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ex) { return null; }
|
||||
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;
|
||||
obj.applyPolicy = function () {
|
||||
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; }
|
||||
obj.getAmtInfo(function (meinfo) {
|
||||
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 _xmlTraverseAllRec(nodes, func) { for (var i in nodes) { func(nodes[i]); if (nodes[i].childNodes) { _xmlTraverseAllRec(nodes[i].childNodes, func); } } }
|
||||
function _turnToXmlRec(text) {
|
||||
if (text == null) return null;
|
||||
var elementStack = new _treeBuilder(), lastElement = null, x1 = text.split('<'), ret = [], element = null, currentElementName = null;
|
||||
for (var i in x1) {
|
||||
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 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 expireServerStatsSeconds = (60 * 60 * 24 * 30); // By default, expire power events after 30 days. (Seconds * Minutes * Hours * Days)
|
||||
obj.path = require('path');
|
||||
obj.parent = parent;
|
||||
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.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.statsevents == 'number') { expireServerStatsSeconds = obj.parent.args.dbexpire.statsevents; }
|
||||
}
|
||||
|
||||
if (obj.parent.args.mongodb) {
|
||||
|
@ -115,6 +117,30 @@ module.exports.CreateDB = function (parent) {
|
|||
|
||||
// Setup MongoDB smbios collection, no indexes needed
|
||||
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 {
|
||||
// Use NeDB (The default)
|
||||
obj.databaseType = 1;
|
||||
|
@ -167,6 +193,12 @@ module.exports.CreateDB = function (parent) {
|
|||
|
||||
// Setup the SMBIOS collection
|
||||
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) {
|
||||
|
@ -305,6 +337,10 @@ module.exports.CreateDB = function (parent) {
|
|||
obj.RemoveSMBIOS = function (id) { obj.smbiosfile.remove({ _id: id }); };
|
||||
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
|
||||
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: 'user' }, function (err, userCount) {
|
||||
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.loginCookieEncryptionKey = null;
|
||||
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.
|
||||
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');
|
||||
if (obj.args.nousers == true) { obj.updateServerState('nousers', '1'); }
|
||||
obj.updateServerState('state', 'running');
|
||||
|
@ -1598,31 +1628,34 @@ function getConfig(createSampleConfig) {
|
|||
function InstallModules(modules, func) {
|
||||
var missingModules = [];
|
||||
if (modules.length > 0) {
|
||||
for (var i in modules) { try { var xxmodule = require(modules[i]); } catch (e) { missingModules.push(modules[i]); } }
|
||||
if (missingModules.length > 0) { InstallModule(missingModules.join(' '), InstallModules, modules, func); } else { func(); }
|
||||
for (var i in modules) {
|
||||
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
|
||||
var InstallModuleChildProcess = null;
|
||||
function InstallModule(modulename, func, tag1, tag2) {
|
||||
try {
|
||||
var module = require(modulename);
|
||||
} catch (e) {
|
||||
console.log('Installing ' + modulename + '...');
|
||||
var child_process = require('child_process');
|
||||
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.
|
||||
InstallModuleChildProcess = child_process.exec('npm install ' + modulename + ' --no-optional --save', { maxBuffer: 512000, timeout: 10000 }, function (error, stdout, stderr) {
|
||||
InstallModuleChildProcess = null;
|
||||
if (error != null) { console.log('ERROR: Unable to install missing package \'' + modulename + '\', make sure npm is installed: ' + error); process.exit(); return; }
|
||||
func(tag1, tag2);
|
||||
// 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 --no-optional --save ' + modulename, { maxBuffer: 512000, timeout: 10000 }, function (error, stdout, stderr) {
|
||||
InstallModuleChildProcess = null;
|
||||
if (error != null) {
|
||||
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;
|
||||
});
|
||||
|
||||
}
|
||||
func(tag1, tag2);
|
||||
return;
|
||||
}
|
||||
func(tag1, tag2);
|
||||
});
|
||||
}
|
||||
|
||||
// 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
|
||||
var meshserver = null;
|
||||
var previouslyInstalledModules = { };
|
||||
function mainStart(args) {
|
||||
// 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; }
|
||||
|
@ -1640,10 +1674,12 @@ function mainStart(args) {
|
|||
var config = getConfig(false);
|
||||
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 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
|
||||
var modules = ['ws', 'nedb', 'https', 'yauzl', 'xmldom', 'express', 'archiver', 'multiparty', 'node-forge', 'express-ws', 'compression', 'body-parser', 'connect-redis', 'express-handlebars'];
|
||||
|
@ -1658,9 +1694,14 @@ function mainStart(args) {
|
|||
// If running NodeJS < 8, install "util.promisify"
|
||||
if (nodeVersion < 8) { modules.push('util.promisify'); }
|
||||
|
||||
// if running NodeJS 8 or higher, we can install WebAuthn/FIDO2 support
|
||||
if ((nodeVersion >= 8) && (allsspi == false)) { modules.push('@davedoesdev/fido2-lib'); }
|
||||
|
||||
// Setup 2nd factor authentication
|
||||
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
|
||||
InstallModules(modules, function () { meshserver = CreateMeshCentralServer(config, args); meshserver.Start(); });
|
||||
});
|
||||
|
@ -1670,4 +1711,4 @@ if (require.main === module) {
|
|||
mainStart(require('minimist')(process.argv.slice(2))); // Called directly, launch normally.
|
||||
} else {
|
||||
module.exports.mainStart = mainStart; // Required as a module, useful for winservice.js
|
||||
}
|
||||
}
|
||||
|
|
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) { }
|
||||
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':
|
||||
{
|
||||
if ((user.siteadmin & 21) != 0) { // Only site administrators with "site backup" or "site restore" or "site update" permissions can use this.
|
||||
if (common.validateInt(command.interval, 1000, 1000000) == false) {
|
||||
// Clear the timer
|
||||
if (obj.serverStatsTimer != null) { clearInterval(obj.serverStatsTimer); delete obj.serverStatsTimer; }
|
||||
} else {
|
||||
// Set the timer
|
||||
obj.SendServerStats();
|
||||
obj.serverStatsTimer = setInterval(obj.SendServerStats, command.interval);
|
||||
}
|
||||
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) {
|
||||
// Clear the timer
|
||||
if (obj.serverStatsTimer != null) { clearInterval(obj.serverStatsTimer); delete obj.serverStatsTimer; }
|
||||
} else {
|
||||
// Set the timer
|
||||
obj.SendServerStats();
|
||||
obj.serverStatsTimer = setInterval(obj.SendServerStats, command.interval);
|
||||
}
|
||||
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));
|
||||
if (twoStepLoginSupported) {
|
||||
// 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.
|
||||
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));
|
||||
if (twoStepLoginSupported) {
|
||||
// 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
|
||||
if (otplib.authenticator.check(command.token, command.secret) === true) {
|
||||
// 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':
|
||||
{
|
||||
|
||||
|
||||
// Check is 2-step login is supported
|
||||
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
|
||||
if (twoStepLoginSupported == false) break;
|
||||
|
@ -1886,11 +1896,15 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||
}
|
||||
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/
|
||||
var yubikeyotp = null;
|
||||
try { yubikeyotp = require('yubikeyotp'); } catch (ex) { }
|
||||
|
||||
// Check is 2-step login is supported
|
||||
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 }));
|
||||
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.
|
||||
|
||||
// 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 }
|
||||
if (domain.yubikey.proxy) { request.requestParams = { proxy: domain.yubikey.proxy }; }
|
||||
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':
|
||||
{
|
||||
if (parent.parent.config.settings.no2factorauth === true) return;
|
||||
|
||||
var authdoglib = null;
|
||||
try { authdoglib = require('authdog'); } catch (ex) { }
|
||||
|
||||
// Check is 2-step login is supported
|
||||
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
|
||||
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]); } } }
|
||||
|
||||
// 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
|
||||
obj.hardwareKeyRegistrationRequest = registrationRequest;
|
||||
|
||||
|
@ -1957,12 +1975,17 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||
}
|
||||
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
|
||||
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
|
||||
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);
|
||||
ws.send(JSON.stringify({ action: 'otp-hkey-setup-response', result: true, name: command.name, index: keyIndex }));
|
||||
if (user.otphkeys == null) { user.otphkeys = []; }
|
||||
|
@ -1980,6 +2003,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||
}
|
||||
case 'webauthn-startregister':
|
||||
{
|
||||
if (parent.parent.config.settings.no2factorauth === true) return;
|
||||
|
||||
// Check is 2-step login is supported
|
||||
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;
|
||||
|
@ -2002,6 +2027,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||
}
|
||||
case 'webauthn-endregister':
|
||||
{
|
||||
if (parent.parent.config.settings.no2factorauth === true) return;
|
||||
if ((obj.webAuthnReqistrationRequest == null) || (parent.f2l == null)) return;
|
||||
|
||||
// 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;
|
||||
|
||||
parent.f2l.attestationResult(clientAttestationResponse, attestationExpectations).then(function (regResult) {
|
||||
// If we register a WebAuthn/FIDO2 key, remove all U2F keys.
|
||||
// TODO
|
||||
// Since we are registering a WebAuthn/FIDO2 key, remove all U2F keys (Type 1).
|
||||
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
|
||||
var keyIndex = parent.crypto.randomBytes(4).readUInt32BE(0);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "meshcentral",
|
||||
"version": "0.3.0-u",
|
||||
"version": "0.3.1-q",
|
||||
"keywords": [
|
||||
"Remote Management",
|
||||
"Intel AMT",
|
||||
|
@ -27,7 +27,6 @@
|
|||
],
|
||||
"dependencies": {
|
||||
"archiver": "^3.0.0",
|
||||
"authdog": "^0.1.1",
|
||||
"body-parser": "^1.18.2",
|
||||
"compression": "^1.7.3",
|
||||
"connect-redis": "^3.4.0",
|
||||
|
@ -41,11 +40,9 @@
|
|||
"multiparty": "^4.2.1",
|
||||
"nedb": "^1.8.0",
|
||||
"node-forge": "^0.7.6",
|
||||
"otplib": "^10.0.1",
|
||||
"ws": "^6.1.2",
|
||||
"xmldom": "^0.1.27",
|
||||
"yauzl": "^2.10.0",
|
||||
"yubikeyotp": "^0.2.0"
|
||||
"yauzl": "^2.10.0"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"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,
|
||||
"_MpsTlsOffload": true,
|
||||
"_No2FactorAuth": true,
|
||||
"_WebRtConfig": {
|
||||
"iceServers": [
|
||||
{ "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-desktop-0.0.2.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/filesaver.1.1.20151003.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>
|
||||
<tr>
|
||||
<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 class=style3 style=height:24px> </td>
|
||||
</tr>
|
||||
|
@ -743,6 +744,30 @@
|
|||
</div>
|
||||
<div id=p31events style="max-height:calc(100vh - 267px);overflow-y:scroll"></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" />
|
||||
</div>
|
||||
<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>";
|
||||
QH('specialkeylist', x);
|
||||
|
||||
// Setup server stats panel
|
||||
setupServerStats();
|
||||
// Setup server stats panels
|
||||
setupGeneralServerStats();
|
||||
setupServerTimelineStats();
|
||||
}
|
||||
|
||||
// Toggle the web page to full screen
|
||||
|
@ -1209,7 +1235,11 @@
|
|||
function onMessage(server, message) {
|
||||
switch (message.action) {
|
||||
case 'serverstats': {
|
||||
updateServerStats(message);
|
||||
updateGeneralServerStats(message);
|
||||
break;
|
||||
}
|
||||
case 'servertimelinestats': {
|
||||
setServerTimelineStats(message.events);
|
||||
break;
|
||||
}
|
||||
case 'authcookie': {
|
||||
|
@ -1519,8 +1549,10 @@
|
|||
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');
|
||||
} else {
|
||||
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 />');
|
||||
if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) {
|
||||
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);
|
||||
break;
|
||||
|
@ -1540,15 +1572,15 @@
|
|||
setDialogMode(2, "Add Security Key", 2, null, x);
|
||||
|
||||
var publicKey = message.request;
|
||||
message.request.challenge = Uint8Array.from(atob(message.request.challenge), c => c.charCodeAt(0))
|
||||
message.request.user.id = Uint8Array.from(atob(message.request.user.id), c => c.charCodeAt(0))
|
||||
navigator.credentials.create({ publicKey })
|
||||
.then((newCredentialInfo) => {
|
||||
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), function (c) { return c.charCodeAt(0) })
|
||||
navigator.credentials.create({ publicKey: publicKey })
|
||||
.then(function(newCredentialInfo) {
|
||||
// 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 };
|
||||
meshserver.send({ action: 'webauthn-endregister', response: r });
|
||||
setDialogMode(0);
|
||||
}).catch((error) => {
|
||||
}, function(error) {
|
||||
// Error
|
||||
setDialogMode(2, "Add Security Key", 1, null, "ERROR: " + error);
|
||||
});
|
||||
|
@ -1562,6 +1594,7 @@
|
|||
masterUpdate(32);
|
||||
}
|
||||
switch (message.event.action) {
|
||||
case 'servertimelinestats': { addServerTimelineStats(message.event.data); break; }
|
||||
case 'accountcreate':
|
||||
case 'accountchange': {
|
||||
// An account was created or changed
|
||||
|
@ -6433,18 +6466,20 @@
|
|||
var x = '', dateHeader = null;
|
||||
for (var i in events) {
|
||||
var event = events[i], time = new Date(event.time);
|
||||
if (time.toLocaleDateString() != dateHeader) {
|
||||
if (dateHeader != null) x += '</table>';
|
||||
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';
|
||||
if (event.etype == 'server') icon = 'si3';
|
||||
if (event.msg) {
|
||||
if (time.toLocaleDateString() != dateHeader) {
|
||||
if (dateHeader != null) x += '</table>';
|
||||
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';
|
||||
if (event.etype == 'server') icon = 'si3';
|
||||
|
||||
var msg = event.msg.split('(R)').join('®');
|
||||
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>';
|
||||
var msg = event.msg.split('(R)').join('®');
|
||||
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>';
|
||||
}
|
||||
}
|
||||
if (dateHeader != null) x += '</table>';
|
||||
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'), {
|
||||
type: 'doughnut',
|
||||
data: { datasets: [{ data: [0, 0], backgroundColor: ['#AAAAAA', '#00AA00'] }], labels: ['Used', 'Free'] },
|
||||
|
@ -7186,7 +7221,7 @@
|
|||
}
|
||||
|
||||
var lastServerStats = null;
|
||||
function updateServerStats(message) {
|
||||
function updateGeneralServerStats(message) {
|
||||
if (message != null) { lastServerStats = message; } else { message = lastServerStats; }
|
||||
if (message == null) return;
|
||||
|
||||
|
@ -7220,6 +7255,131 @@
|
|||
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
|
||||
//
|
||||
|
@ -7274,7 +7434,7 @@
|
|||
function go(x) {
|
||||
if (xxdialogMode || xxcurrentView == x) return;
|
||||
// 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;
|
||||
|
||||
// Remove left bar selection
|
||||
|
@ -7307,7 +7467,7 @@
|
|||
|
||||
// My Server
|
||||
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
|
||||
if (webPageFullScreen) {
|
||||
|
@ -7326,8 +7486,8 @@
|
|||
QV('UserDummyMenuSpan', (x < 10) && (x != 6) && webPageFullScreen);
|
||||
QV('MeshSubMenuSpan', x >= 20 && x < 30);
|
||||
QV('UserSubMenuSpan', x >= 30 && x < 40);
|
||||
QV('ServerSubMenuSpan', x == 6 || x == 115);
|
||||
var panels = { 10: 'MainDev', 11: 'MainDevDesktop', 12: 'MainDevTerminal', 13: 'MainDevFiles', 14: 'MainDevAmt', 15: 'MainDevConsole', 20: 'MeshGeneral', 30: 'UserGeneral', 31: 'UserEvents', 6: 'ServerGeneral', 115: 'ServerConsole' };
|
||||
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', 40: 'ServerStats', 115: 'ServerConsole' };
|
||||
for (var i in panels) {
|
||||
Q(panels[i]).classList.remove('style3x');
|
||||
Q(panels[i]).classList.remove('style3sel');
|
||||
|
@ -7342,6 +7502,9 @@
|
|||
|
||||
if (x == 1) masterUpdate(4);
|
||||
|
||||
// Fetch the server timeline stats if needed
|
||||
if ((x == 40) && (serverTimelineStats == null)) { refreshServerTimelineStats(); }
|
||||
|
||||
// Update the web page title
|
||||
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}}}";
|
||||
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 hardwareKeyChallenge = '{{{hkey}}}';
|
||||
var hardwareKeyChallenge = decodeURIComponent('{{{hkey}}}');
|
||||
var currentpanel = 0;
|
||||
|
||||
function startup() {
|
||||
|
@ -287,7 +287,34 @@
|
|||
|
||||
if ('{{loginmode}}' == '4') {
|
||||
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) {
|
||||
if ((currentpanel == 4) && authResponse.signatureData) {
|
||||
Q('hwtokenInput').value = JSON.stringify(authResponse);
|
||||
|
@ -300,7 +327,34 @@
|
|||
|
||||
if ('{{loginmode}}' == '5') {
|
||||
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) {
|
||||
if ((currentpanel == 5) && authResponse.signatureData) {
|
||||
Q('resetHwtokenInput').value = JSON.stringify(authResponse);
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
}
|
||||
</style>
|
||||
<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>
|
||||
</head>
|
||||
<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>
|
||||
<table id="centralTable" style=width:100%>
|
||||
<tr>
|
||||
<td id="welcomeimage" align="right">
|
||||
<td id="welcomeimage" align="right" style="display:none">
|
||||
<picture>
|
||||
<img alt="" width=359 height=310 src=welcome.jpg />
|
||||
</picture>
|
||||
|
@ -332,7 +332,7 @@
|
|||
var newAccountPass = parseInt('{{{newAccountPass}}}');
|
||||
var emailCheck = ('{{{emailcheck}}}' == 'true');
|
||||
var passRequirements = "{{{passRequirements}}}";
|
||||
var hardwareKeyChallenge = '{{{hkey}}}';
|
||||
var hardwareKeyChallenge = decodeURIComponent('{{{hkey}}}');
|
||||
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 features = parseInt('{{{features}}}');
|
||||
|
@ -373,12 +373,12 @@
|
|||
if ('{{loginmode}}' == '4') {
|
||||
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
|
||||
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++) {
|
||||
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(
|
||||
function (rawAssertion) {
|
||||
var assertion = {
|
||||
id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))), //base64encode(rawAssertion.rawId),
|
||||
clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))), //arrayBufferToString(rawAssertion.response.clientDataJSON),
|
||||
userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))), //base64encode(rawAssertion.response.userHandle),
|
||||
signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))), //base64encode(rawAssertion.response.signature),
|
||||
authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))), //base64encode(rawAssertion.response.authenticatorData)
|
||||
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);
|
||||
|
@ -412,7 +412,34 @@
|
|||
|
||||
if ('{{loginmode}}' == '5') {
|
||||
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) {
|
||||
if ((currentpanel == 5) && authResponse.signatureData) {
|
||||
Q('resetHwtokenInput').value = JSON.stringify(authResponse);
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -560,8 +560,7 @@
|
|||
displayLocalVideo(true);
|
||||
}
|
||||
for (var i in tracks) { webrtc.addTrack(tracks[i], localStream); }
|
||||
})
|
||||
.catch(function (err) {
|
||||
}, function (err) {
|
||||
displayControl(err.message + '.');
|
||||
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.
|
||||
|
||||
// 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
|
||||
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
|
||||
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
|
||||
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; };
|
||||
|
||||
// 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]); } }
|
||||
if (u2fKeys.length > 0) {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
@ -458,24 +462,28 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
}
|
||||
}
|
||||
|
||||
// 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]); } }
|
||||
var authdoglib = null;
|
||||
try { authdoglib = require('authdog'); } catch (ex) { }
|
||||
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
|
||||
if (u2fKeys.length > 0) {
|
||||
require('authdog').startAuthentication('https://' + obj.parent.certificates.CommonName, u2fKeys, { requestId: 0, timeoutSeconds: 60 }).then(function (registrationRequest) {
|
||||
// Save authentication request to session for later use
|
||||
req.session.u2fchallenge = registrationRequest;
|
||||
// Generate a U2F challenge
|
||||
if (u2fKeys.length > 0) {
|
||||
authdoglib.startAuthentication('https://' + obj.parent.certificates.CommonName, u2fKeys, { requestId: 0, timeoutSeconds: 60 }).then(function (registrationRequest) {
|
||||
// Save authentication request to session for later use
|
||||
req.session.u2fchallenge = registrationRequest;
|
||||
|
||||
// Send authentication request to client
|
||||
func(JSON.stringify(registrationRequest));
|
||||
}, function (error) {
|
||||
// Handle authentication request error
|
||||
// Send authentication request to client
|
||||
func(JSON.stringify(registrationRequest));
|
||||
}, function (error) {
|
||||
// Handle authentication request error
|
||||
func('');
|
||||
});
|
||||
} else {
|
||||
func('');
|
||||
});
|
||||
} else {
|
||||
func('');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
func('');
|
||||
|
@ -1149,19 +1157,19 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
if (obj.args.nousers == true) { features += 0x00000004; } // Single user mode
|
||||
if (domain.userQuota == -1) { features += 0x00000008; } // No server files mode
|
||||
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.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.allowhighqualitydesktop == true) { features += 0x00000200; } // Enable AllowHighQualityDesktop (Default false)
|
||||
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 ((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.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.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
|
||||
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.
|
||||
// Normal login 2 factor request
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -1202,7 +1210,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
} else {
|
||||
var user = obj.users[docs[0]._id];
|
||||
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 {
|
||||
req.session = null;
|
||||
res.redirect(domain.url);
|
||||
|
@ -1240,14 +1248,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
if (obj.args.minify && !req.query.nominify) {
|
||||
// Try to server the minified version if we can.
|
||||
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) {
|
||||
// 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 {
|
||||
// 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