mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-12-24 06:05:53 -05:00
Merge branch 'master' into moving-styles
This commit is contained in:
commit
dc0d5df9c5
@ -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();
|
||||
}
|
||||
|
2
agents/meshcmd.min.js
vendored
2
agents/meshcmd.min.js
vendored
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;
|
||||
}
|
||||
|
2
agents/modules_meshcmd_min/amt-xml.min.js
vendored
2
agents/modules_meshcmd_min/amt-xml.min.js
vendored
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
2
agents/modules_meshcore_min/amt-xml.min.js
vendored
2
agents/modules_meshcore_min/amt-xml.min.js
vendored
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 });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -637,13 +637,15 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
||||
obj.connectTime = Date.now();
|
||||
db.Set({ _id: 'lc' + obj.dbNodeKey, type: 'lastconnect', domain: domain.id, time: obj.connectTime, addr: obj.remoteaddrport });
|
||||
|
||||
// Device already exists, look if changes has occured
|
||||
// Device already exists, look if changes have occured
|
||||
var changes = [], change = 0, log = 0;
|
||||
if (device.agent == null) { device.agent = { ver: obj.agentInfo.agentVersion, id: obj.agentInfo.agentId, caps: obj.agentInfo.capabilities }; change = 1; }
|
||||
if (device.rname != obj.agentInfo.computerName) { device.rname = obj.agentInfo.computerName; change = 1; changes.push('computer name'); }
|
||||
if (device.agent.ver != obj.agentInfo.agentVersion) { device.agent.ver = obj.agentInfo.agentVersion; change = 1; changes.push('agent version'); }
|
||||
if (device.agent.id != obj.agentInfo.agentId) { device.agent.id = obj.agentInfo.agentId; change = 1; changes.push('agent type'); }
|
||||
if ((device.agent.caps & 24) != (obj.agentInfo.capabilities & 24)) { device.agent.caps = obj.agentInfo.capabilities; change = 1; changes.push('agent capabilities'); } // If agent console or javascript support changes, update capabilities
|
||||
if (mesh.flags && (mesh.flags & 2) && (device.name != obj.agentInfo.computerName)) { device.name = obj.agentInfo.computerName; change = 1; } // We want the server name to be sync'ed to the hostname
|
||||
|
||||
if (change == 1) {
|
||||
db.Set(device);
|
||||
|
||||
|
@ -60,16 +60,19 @@ 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
|
||||
|
||||
// Setup the default configuration and files paths
|
||||
if ((__dirname.endsWith('/node_modules/meshcentral')) || (__dirname.endsWith('\\node_modules\\meshcentral')) || (__dirname.endsWith('/node_modules/meshcentral/')) || (__dirname.endsWith('\\node_modules\\meshcentral\\'))) {
|
||||
obj.parentpath = obj.path.join(__dirname, '../..');
|
||||
obj.datapath = obj.path.join(__dirname, '../../meshcentral-data');
|
||||
obj.filespath = obj.path.join(__dirname, '../../meshcentral-files');
|
||||
if (obj.fs.existsSync(obj.path.join(__dirname, '../../meshcentral-web/views'))) { obj.webViewsPath = obj.path.join(__dirname, '../../meshcentral-web/views'); } else { obj.webViewsPath = obj.path.join(__dirname, 'views'); }
|
||||
if (obj.fs.existsSync(obj.path.join(__dirname, '../../meshcentral-web/public'))) { obj.webPublicPath = obj.path.join(__dirname, '../../meshcentral-web/public'); } else { obj.webPublicPath = obj.path.join(__dirname, 'public'); }
|
||||
} else {
|
||||
obj.parentpath = __dirname;
|
||||
obj.datapath = obj.path.join(__dirname, '../meshcentral-data');
|
||||
obj.filespath = obj.path.join(__dirname, '../meshcentral-files');
|
||||
if (obj.fs.existsSync(obj.path.join(__dirname, '../meshcentral-web/views'))) { obj.webViewsPath = obj.path.join(__dirname, '../meshcentral-web/views'); } else { obj.webViewsPath = obj.path.join(__dirname, 'views'); }
|
||||
@ -162,7 +165,7 @@ function CreateMeshCentralServer(config, args) {
|
||||
// Launch MeshCentral as a child server and monitor it.
|
||||
obj.launchChildServer = function (startLine) {
|
||||
var child_process = require('child_process');
|
||||
var xprocess = child_process.exec(startLine + ' --launch', { maxBuffer: Infinity }, function (error, stdout, stderr) {
|
||||
var xprocess = child_process.exec(startLine + ' --launch', { maxBuffer: Infinity, cwd: obj.parentpath }, function (error, stdout, stderr) {
|
||||
if (xprocess.xrestart == 1) {
|
||||
setTimeout(function () { obj.launchChildServer(startLine); }, 500); // This is an expected restart.
|
||||
} else if (xprocess.xrestart == 2) {
|
||||
@ -174,7 +177,7 @@ function CreateMeshCentralServer(config, args) {
|
||||
if (typeof obj.args.selfupdate == 'string') { version = '@' + obj.args.selfupdate; }
|
||||
var child_process = require('child_process');
|
||||
var npmpath = ((typeof obj.args.npmpath == 'string') ? obj.args.npmpath : 'npm');
|
||||
var xxprocess = child_process.exec(npmpath + ' install meshcentral' + version, { maxBuffer: Infinity, cwd: obj.path.join(__dirname, '../..') }, function (error, stdout, stderr) { });
|
||||
var xxprocess = child_process.exec(npmpath + ' install meshcentral' + version, { maxBuffer: Infinity, cwd: obj.parentpath }, function (error, stdout, stderr) { });
|
||||
xxprocess.data = '';
|
||||
xxprocess.stdout.on('data', function (data) { xxprocess.data += data; });
|
||||
xxprocess.stderr.on('data', function (data) { xxprocess.data += data; });
|
||||
@ -204,7 +207,7 @@ function CreateMeshCentralServer(config, args) {
|
||||
if (typeof obj.args.selfupdate == 'string') { callback(obj.currentVer, obj.args.selfupdate); return; } // If we are targetting a specific version, return that one as current.
|
||||
var child_process = require('child_process');
|
||||
var npmpath = ((typeof obj.args.npmpath == 'string') ? obj.args.npmpath : 'npm');
|
||||
var xprocess = child_process.exec(npmpath + ' view meshcentral dist-tags.latest', { maxBuffer: 512000 }, function (error, stdout, stderr) { });
|
||||
var xprocess = child_process.exec(npmpath + ' view meshcentral dist-tags.latest', { maxBuffer: 512000, cwd: obj.parentpath }, function (error, stdout, stderr) { });
|
||||
xprocess.data = '';
|
||||
xprocess.stdout.on('data', function (data) { xprocess.data += data; });
|
||||
xprocess.stderr.on('data', function (data) { });
|
||||
@ -800,6 +803,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 +1630,38 @@ 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');
|
||||
var parentpath = __dirname;
|
||||
|
||||
// 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);
|
||||
// Get the working directory
|
||||
if ((__dirname.endsWith('/node_modules/meshcentral')) || (__dirname.endsWith('\\node_modules\\meshcentral')) || (__dirname.endsWith('/node_modules/meshcentral/')) || (__dirname.endsWith('\\node_modules\\meshcentral\\'))) { parentpath = require('path').join(__dirname, '../..'); }
|
||||
|
||||
// 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, cwd: parentpath }, 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 +1669,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 +1680,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,8 +1700,13 @@ 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(); });
|
||||
|
73
meshuser.js
73
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;
|
||||
}
|
||||
@ -1032,6 +1040,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
}
|
||||
case 'createmesh':
|
||||
{
|
||||
// Check if we have new group restriction
|
||||
if ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 64) != 0)) break;
|
||||
|
||||
// In some situations, we need a verified email address to create a device group.
|
||||
if ((parent.parent.mailserver != null) && (domain.auth != 'sspi') && (user.emailVerified !== true) && (user.siteadmin != 0xFFFFFFFF)) return; // User must verify it's email first.
|
||||
|
||||
@ -1773,7 +1784,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 +1798,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 +1868,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 +1899,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 +1921,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 +1950,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 +1978,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 +2006,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 +2030,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 +2051,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);
|
||||
|
784
package-lock.json
generated
784
package-lock.json
generated
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-x",
|
||||
"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 |
@ -4,6 +4,8 @@
|
||||
* @version v0.0.2c
|
||||
*/
|
||||
|
||||
// https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
|
||||
|
||||
// Construct a MeshServer object
|
||||
var CreateAmtRemoteTerminal = function (divid) {
|
||||
var obj = {};
|
||||
@ -32,10 +34,15 @@ var CreateAmtRemoteTerminal = function (divid) {
|
||||
var _termstate = 0;
|
||||
var _escNumber = [];
|
||||
var _escNumberPtr = 0;
|
||||
var _escNumberMode = 0;
|
||||
var _scratt = [];
|
||||
var _tscreen = [];
|
||||
var _VTUNDERLINE = 1;
|
||||
var _VTREVERSE = 2;
|
||||
var _backSpaceErase = false;
|
||||
var _cursorVisible = true;
|
||||
var _scrollRegion = [0, 24];
|
||||
var _altKeypadMode = false;
|
||||
|
||||
obj.Start = function () { }
|
||||
|
||||
@ -72,6 +79,9 @@ var CreateAmtRemoteTerminal = function (divid) {
|
||||
switch (c) {
|
||||
case 27: // ESC
|
||||
_termstate = 1;
|
||||
_escNumber = [];
|
||||
_escNumberPtr = 0;
|
||||
_escNumberMode = 0;
|
||||
break;
|
||||
default:
|
||||
// Process a single char
|
||||
@ -82,8 +92,6 @@ var CreateAmtRemoteTerminal = function (divid) {
|
||||
case 1:
|
||||
switch (b) {
|
||||
case '[':
|
||||
_escNumberPtr = 0;
|
||||
_escNumber = [];
|
||||
_termstate = 2;
|
||||
break;
|
||||
case '(':
|
||||
@ -92,6 +100,16 @@ var CreateAmtRemoteTerminal = function (divid) {
|
||||
case ')':
|
||||
_termstate = 5;
|
||||
break;
|
||||
case '=':
|
||||
// Set alternate keypad mode
|
||||
_altKeypadMode = true;
|
||||
_termstate = 0;
|
||||
break;
|
||||
case '>':
|
||||
// Set numeric keypad mode
|
||||
_altKeypadMode = false;
|
||||
_termstate = 0;
|
||||
break;
|
||||
default:
|
||||
_termstate = 0;
|
||||
break;
|
||||
@ -100,23 +118,20 @@ var CreateAmtRemoteTerminal = function (divid) {
|
||||
case 2:
|
||||
if (b >= '0' && b <= '9') {
|
||||
// This is a number
|
||||
if (!_escNumber[_escNumberPtr]) {
|
||||
_escNumber[_escNumberPtr] = (b - '0');
|
||||
}
|
||||
else {
|
||||
_escNumber[_escNumberPtr] = ((_escNumber[_escNumberPtr] * 10) + (b - '0'));
|
||||
}
|
||||
if (!_escNumber[_escNumberPtr]) { _escNumber[_escNumberPtr] = (b - '0'); }
|
||||
else { _escNumber[_escNumberPtr] = ((_escNumber[_escNumberPtr] * 10) + (b - '0')); }
|
||||
break;
|
||||
}
|
||||
else if (b == ';') {
|
||||
} else if (b == ';') {
|
||||
// New number
|
||||
_escNumberPtr++;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
} else if (b == '?') {
|
||||
_escNumberMode = 1;
|
||||
break;
|
||||
} else {
|
||||
// Process Escape Sequence
|
||||
if (!_escNumber[0]) _escNumber[0] = 0;
|
||||
_ProcessEscapeHandler(b, _escNumber, _escNumberPtr + 1);
|
||||
_ProcessEscapeHandler(b, _escNumber, _escNumberPtr + 1, _escNumberMode);
|
||||
_termstate = 0;
|
||||
}
|
||||
break;
|
||||
@ -129,151 +144,207 @@ var CreateAmtRemoteTerminal = function (divid) {
|
||||
}
|
||||
}
|
||||
|
||||
function _ProcessEscapeHandler(code, args, argslen) {
|
||||
var i;
|
||||
switch (code) {
|
||||
case 'c': // ResetDevice
|
||||
// Reset
|
||||
obj.TermResetScreen();
|
||||
break;
|
||||
case 'A': // Move cursor up n lines
|
||||
if (argslen == 1) {
|
||||
_termy -= args[0];
|
||||
if (_termy < 0) _termy = 0;
|
||||
}
|
||||
break;
|
||||
case 'B': // Move cursor down n lines
|
||||
if (argslen == 1) {
|
||||
_termy += args[0];
|
||||
if (_termy > obj.height) _termy = obj.height;
|
||||
}
|
||||
break;
|
||||
case 'C': // Move cursor right n lines
|
||||
if (argslen == 1) {
|
||||
_termx += args[0];
|
||||
if (_termx > obj.width) _termx = obj.width;
|
||||
}
|
||||
break;
|
||||
case 'D': // Move cursor left n lines
|
||||
if (argslen == 1) {
|
||||
_termx -= args[0];
|
||||
if (_termx < 0) _termx = 0;
|
||||
}
|
||||
break;
|
||||
case 'd': // Set cursor to line n
|
||||
if (argslen == 1) {
|
||||
_termy = args[0] - 1;
|
||||
if (_termy > obj.height) _termy = obj.height;
|
||||
if (_termy < 0) _termy = 0;
|
||||
}
|
||||
break;
|
||||
case 'G': // Set cursor to col n
|
||||
if (argslen == 1) {
|
||||
_termx = args[0] - 1;
|
||||
if (_termx < 0) _termx = 0;
|
||||
if (_termx > 79) _termx = 79;
|
||||
}
|
||||
break;
|
||||
case 'J': // ClearScreen:
|
||||
if (argslen == 1 && args[0] == 2) {
|
||||
obj.TermClear((_TermCurrentBColor << 12) + (_TermCurrentFColor << 6)); // Erase entire screen
|
||||
_termx = 0;
|
||||
_termy = 0;
|
||||
}
|
||||
else if (argslen == 0 || argslen == 1 && args[0] == 0) // Erase cursor down
|
||||
{
|
||||
_EraseCursorToEol();
|
||||
for (i = _termy + 1; i < obj.height; i++) _EraseLine(i);
|
||||
}
|
||||
else if (argslen == 1 && args[0] == 1) // Erase cursor up
|
||||
{
|
||||
_EraseCursorToEol();
|
||||
for (i = 0; i < _termy - 1; i++) _EraseLine(i);
|
||||
}
|
||||
break;
|
||||
case 'H': // MoveCursor:
|
||||
if (argslen == 2) {
|
||||
if (args[0] < 1) args[0] = 1;
|
||||
if (args[1] < 1) args[1] = 1;
|
||||
if (args[0] > obj.height) args[0] = obj.height;
|
||||
if (args[1] > obj.width) args[1] = obj.width;
|
||||
_termy = args[0] - 1;
|
||||
_termx = args[1] - 1;
|
||||
}
|
||||
else {
|
||||
_termy = 0;
|
||||
_termx = 0;
|
||||
}
|
||||
break;
|
||||
case 'm': // ScreenAttribs:
|
||||
// Change attributes
|
||||
for (i = 0; i < argslen; i++) {
|
||||
if (!args[i] || args[i] == 0) {
|
||||
// Reset Attributes
|
||||
_TermCurrentBColor = 0;
|
||||
_TermCurrentFColor = 7;
|
||||
_TermCurrentReverse = 0;
|
||||
function _ProcessEscapeHandler(code, args, argslen, mode) {
|
||||
//console.log('process', code, args, mode);
|
||||
if (mode == 1) {
|
||||
switch (code) {
|
||||
case 'l': // Hide the cursor
|
||||
if (args[0] == 25) { _cursorVisible = false; }
|
||||
break;
|
||||
case 'h': // Show the cursor
|
||||
if (args[0] == 25) { _cursorVisible = true; }
|
||||
break;
|
||||
}
|
||||
} else if (mode == 0) {
|
||||
var i;
|
||||
switch (code) {
|
||||
case 'c': // ResetDevice
|
||||
// Reset
|
||||
obj.TermResetScreen();
|
||||
break;
|
||||
case 'A': // Move cursor up n lines
|
||||
if (argslen == 1) {
|
||||
if (args[0] == 0) { _termy--; } else { _termy -= args[0]; }
|
||||
if (_termy < 0) _termy = 0;
|
||||
}
|
||||
else if (args[i] == 1) {
|
||||
// Bright
|
||||
if (_TermCurrentFColor < 8) _TermCurrentFColor += 8;
|
||||
break;
|
||||
case 'B': // Move cursor down n lines
|
||||
if (argslen == 1) {
|
||||
if (args[0] == 0) { _termy++; } else { _termy += args[0]; }
|
||||
if (_termy > obj.height) _termy = obj.height;
|
||||
}
|
||||
else if (args[i] == 2 || args[i] == 22) {
|
||||
// Dim
|
||||
if (_TermCurrentFColor >= 8) _TermCurrentFColor -= 8;
|
||||
break;
|
||||
case 'C': // Move cursor right n lines
|
||||
if (argslen == 1) {
|
||||
if (args[0] == 0) { _termx++; } else { _termx += args[0]; }
|
||||
if (_termx > obj.width) _termx = obj.width;
|
||||
}
|
||||
else if (args[i] == 7) {
|
||||
// Set Reverse attribute true
|
||||
_TermCurrentReverse = 2;
|
||||
break;
|
||||
case 'D': // Move cursor left n lines
|
||||
if (argslen == 1) {
|
||||
if (args[0] == 0) { _termx--; } else { _termx -= args[0]; }
|
||||
if (_termx < 0) _termx = 0;
|
||||
}
|
||||
else if (args[i] == 27) {
|
||||
// Set Reverse attribute false
|
||||
_TermCurrentReverse = 0;
|
||||
break;
|
||||
case 'd': // Set cursor to line n
|
||||
if (argslen == 1) {
|
||||
_termy = args[0] - 1;
|
||||
if (_termy > obj.height) _termy = obj.height;
|
||||
if (_termy < 0) _termy = 0;
|
||||
}
|
||||
else if (args[i] >= 30 && args[i] <= 37) {
|
||||
// Set Foreground Color
|
||||
var bright = (_TermCurrentFColor >= 8);
|
||||
_TermCurrentFColor = (args[i] - 30);
|
||||
if (bright && _TermCurrentFColor <= 8) _TermCurrentFColor += 8;
|
||||
break;
|
||||
case 'G': // Set cursor to col n
|
||||
if (argslen == 1) {
|
||||
_termx = args[0] - 1;
|
||||
if (_termx < 0) _termx = 0;
|
||||
if (_termx > 79) _termx = 79;
|
||||
}
|
||||
else if (args[i] >= 40 && args[i] <= 47) {
|
||||
// Set Background Color
|
||||
_TermCurrentBColor = (args[i] - 40);
|
||||
break;
|
||||
case 'P': // Delete X Character(s), default 1 char
|
||||
var x = 1;
|
||||
if (argslen == 1) { x = args[0]; }
|
||||
for (i = _termx; i < (_termx + x) ; i++) { _tscreen[_termy][i] = ' '; _scratt[_termy][i] = (7 << 6); }
|
||||
break;
|
||||
case 'L': // Insert X Line(s), default 1 char
|
||||
var linecount = 1;
|
||||
if (argslen == 1) { linecount = args[0]; }
|
||||
if (linecount == 0) { linecount = 1; }
|
||||
for (y = _scrollRegion[1]; y >= _termy + linecount; y--) {
|
||||
_tscreen[y] = _tscreen[y - linecount];
|
||||
_scratt[y] = _scratt[y - linecount];
|
||||
}
|
||||
else if (args[i] >= 90 && args[i] <= 99) {
|
||||
// Set Bright Foreground Color
|
||||
_TermCurrentFColor = (args[i] - 82);
|
||||
for (y = _termy; y < _termy + linecount; y++) {
|
||||
_tscreen[y] = [];
|
||||
_scratt[y] = [];
|
||||
for (x = 0; x < obj.width; x++) { _tscreen[y][x] = ' '; _scratt[y][x] = (7 << 6); }
|
||||
}
|
||||
else if (args[i] >= 100 && args[i] <= 109) {
|
||||
// Set Bright Background Color
|
||||
_TermCurrentBColor = (args[i] - 92);
|
||||
break;
|
||||
case 'J': // ClearScreen:
|
||||
if (argslen == 1 && args[0] == 2) {
|
||||
obj.TermClear((_TermCurrentBColor << 12) + (_TermCurrentFColor << 6)); // Erase entire screen
|
||||
_termx = 0;
|
||||
_termy = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'K': // EraseLine:
|
||||
if (argslen == 0 || (argslen == 1 && (!args[0] || args[0] == 0))) {
|
||||
_EraseCursorToEol(); // Erase from the cursor to the end of the line
|
||||
}
|
||||
else if (argslen == 1) {
|
||||
if (args[0] == 1) // Erase from the beginning of the line to the cursor
|
||||
else if (argslen == 0 || argslen == 1 && args[0] == 0) // Erase cursor down
|
||||
{
|
||||
_EraseBolToCursor();
|
||||
_EraseCursorToEol();
|
||||
for (i = _termy + 1; i < obj.height; i++) _EraseLine(i);
|
||||
}
|
||||
else if (args[0] == 2) // Erase the line with the cursor
|
||||
else if (argslen == 1 && args[0] == 1) // Erase cursor up
|
||||
{
|
||||
_EraseLine(_termy);
|
||||
_EraseCursorToEol();
|
||||
for (i = 0; i < _termy - 1; i++) _EraseLine(i);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'h': // EnableLineWrap:
|
||||
_TermLineWrap = true;
|
||||
break;
|
||||
case 'l': // DisableLineWrap:
|
||||
_TermLineWrap = false;
|
||||
break;
|
||||
default:
|
||||
//if (code != '@') alert(code);
|
||||
break;
|
||||
break;
|
||||
case 'H': // MoveCursor:
|
||||
if (argslen == 2) {
|
||||
if (args[0] < 1) args[0] = 1;
|
||||
if (args[1] < 1) args[1] = 1;
|
||||
if (args[0] > obj.height) args[0] = obj.height;
|
||||
if (args[1] > obj.width) args[1] = obj.width;
|
||||
_termy = args[0] - 1;
|
||||
_termx = args[1] - 1;
|
||||
}
|
||||
else {
|
||||
_termy = 0;
|
||||
_termx = 0;
|
||||
}
|
||||
break;
|
||||
case 'm': // ScreenAttribs:
|
||||
// Change attributes
|
||||
for (i = 0; i < argslen; i++) {
|
||||
if (!args[i] || args[i] == 0) {
|
||||
// Reset Attributes
|
||||
_TermCurrentBColor = 0;
|
||||
_TermCurrentFColor = 7;
|
||||
_TermCurrentReverse = 0;
|
||||
}
|
||||
else if (args[i] == 1) {
|
||||
// Bright
|
||||
if (_TermCurrentFColor < 8) _TermCurrentFColor += 8;
|
||||
}
|
||||
else if (args[i] == 2 || args[i] == 22) {
|
||||
// Dim
|
||||
if (_TermCurrentFColor >= 8) _TermCurrentFColor -= 8;
|
||||
}
|
||||
else if (args[i] == 7) {
|
||||
// Set Reverse attribute true
|
||||
_TermCurrentReverse = 2;
|
||||
}
|
||||
else if (args[i] == 27) {
|
||||
// Set Reverse attribute false
|
||||
_TermCurrentReverse = 0;
|
||||
}
|
||||
else if (args[i] >= 30 && args[i] <= 37) {
|
||||
// Set Foreground Color
|
||||
var bright = (_TermCurrentFColor >= 8);
|
||||
_TermCurrentFColor = (args[i] - 30);
|
||||
if (bright && _TermCurrentFColor <= 8) _TermCurrentFColor += 8;
|
||||
}
|
||||
else if (args[i] >= 40 && args[i] <= 47) {
|
||||
// Set Background Color
|
||||
_TermCurrentBColor = (args[i] - 40);
|
||||
}
|
||||
else if (args[i] >= 90 && args[i] <= 99) {
|
||||
// Set Bright Foreground Color
|
||||
_TermCurrentFColor = (args[i] - 82);
|
||||
}
|
||||
else if (args[i] >= 100 && args[i] <= 109) {
|
||||
// Set Bright Background Color
|
||||
_TermCurrentBColor = (args[i] - 92);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'K': // EraseLine:
|
||||
if (argslen == 0 || (argslen == 1 && (!args[0] || args[0] == 0))) {
|
||||
_EraseCursorToEol(); // Erase from the cursor to the end of the line
|
||||
} else if (argslen == 1) {
|
||||
if (args[0] == 1) { // Erase from the beginning of the line to the cursor
|
||||
_EraseBolToCursor();
|
||||
} else if (args[0] == 2) { // Erase the line with the cursor
|
||||
_EraseLine(_termy);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'h': // EnableLineWrap:
|
||||
_TermLineWrap = true;
|
||||
break;
|
||||
case 'l': // DisableLineWrap:
|
||||
_TermLineWrap = false;
|
||||
break;
|
||||
case 'r': // Set the scroll region
|
||||
if (argslen == 2) { _scrollRegion = [args[0] - 1, args[1] - 1]; }
|
||||
if (_scrollRegion[0] < 0) { _scrollRegion[0] = 0; }
|
||||
if (_scrollRegion[0] > 24) { _scrollRegion[0] = 24; }
|
||||
if (_scrollRegion[1] < 0) { _scrollRegion[1] = 0; }
|
||||
if (_scrollRegion[1] > 24) { _scrollRegion[1] = 24; }
|
||||
if (_scrollRegion[0] > _scrollRegion[1]) { _scrollRegion[0] = _scrollRegion[1]; }
|
||||
break;
|
||||
case 'S': // Scroll up the scroll region X lines, default 1
|
||||
var x = 1;
|
||||
if (argslen == 1) { x = args[0] }
|
||||
for (var y = _scrollRegion[0]; y <= _scrollRegion[1] - x; y++) {
|
||||
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = _tscreen[y + x][z]; _scratt[y][z] = _scratt[y + x][z]; }
|
||||
}
|
||||
for (var y = _scrollRegion[1] - x + 1; y < _scrollRegion[1]; y++) {
|
||||
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); }
|
||||
}
|
||||
break;
|
||||
case 'T': // Scroll down the scroll region X lines, default 1
|
||||
var x = 1;
|
||||
if (argslen == 1) { x = args[0] }
|
||||
for (var y = _scrollRegion[1]; y > _scrollRegion[0] + x; y--) {
|
||||
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = _tscreen[y - x][z]; _scratt[y][z] = _scratt[y - x][z]; }
|
||||
}
|
||||
for (var y = _scrollRegion[0] + x; y > _scrollRegion[0]; y--) {
|
||||
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); }
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//if (code != '@') alert(code);
|
||||
//console.log('unknown terminal code', code, args, mode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -366,6 +437,7 @@ var CreateAmtRemoteTerminal = function (divid) {
|
||||
function _ProcessVt100Char(c) {
|
||||
if (c == '\0' || c.charCodeAt() == 7) return; // Ignore null & bell
|
||||
var ch = c.charCodeAt();
|
||||
//console.log('_ProcessVt100Char', ch, c);
|
||||
|
||||
// ###BEGIN###{Terminal-Enumation-All}
|
||||
// UTF8 Terminal
|
||||
@ -401,8 +473,8 @@ var CreateAmtRemoteTerminal = function (divid) {
|
||||
switch (c) {
|
||||
case '\b': // Backspace
|
||||
if (_termx > 0) {
|
||||
_termx = _termx - 1;
|
||||
_TermDrawChar(' ');
|
||||
_termx--;
|
||||
if (_backSpaceErase) { _TermDrawChar(' '); }
|
||||
}
|
||||
break;
|
||||
case '\t': // tab
|
||||
@ -411,12 +483,12 @@ var CreateAmtRemoteTerminal = function (divid) {
|
||||
break;
|
||||
case '\n': // Linefeed
|
||||
_termy++;
|
||||
if (_termy > (obj.height - 1)) {
|
||||
if (_termy > _scrollRegion[1]) {
|
||||
// Move everything up one line
|
||||
_TermMoveUp(1);
|
||||
_termy = (obj.height - 1);
|
||||
_termy = _scrollRegion[1];
|
||||
}
|
||||
if (obj.lineFeed = '\n') { _termx = 0; } // *** If we are in Linux mode, \n will also return the cursor to the first col
|
||||
if (obj.lineFeed = '\r') { _termx = 0; } // *** If we are in Linux mode, \n will also return the cursor to the first col
|
||||
break;
|
||||
case '\r': // Carriage Return
|
||||
_termx = 0;
|
||||
@ -452,14 +524,16 @@ var CreateAmtRemoteTerminal = function (divid) {
|
||||
_TermCurrentReverse = 0;
|
||||
_TermCurrentFColor = 7;
|
||||
_TermCurrentBColor = 0;
|
||||
_TermLineWrap = true;
|
||||
_termx = 0;
|
||||
_termy = 0;
|
||||
_TermLineWrap = _cursorVisible = true;
|
||||
_termx = _termy = 0;
|
||||
_backSpaceErase = false;
|
||||
_scrollRegion = [0, 24];
|
||||
_altKeypadMode = false;
|
||||
obj.TermClear(7 << 6);
|
||||
}
|
||||
|
||||
function _EraseCursorToEol() {
|
||||
var t = (_TermCurrentBColor << 12);
|
||||
var t = (_TermCurrentFColor << 6) + (_TermCurrentBColor << 12) + _TermCurrentReverse;
|
||||
for (var x = _termx; x < obj.width; x++) {
|
||||
_tscreen[_termy][x] = ' ';
|
||||
_scratt[_termy][x] = t;
|
||||
@ -467,7 +541,7 @@ var CreateAmtRemoteTerminal = function (divid) {
|
||||
}
|
||||
|
||||
function _EraseBolToCursor() {
|
||||
var t = (_TermCurrentBColor << 12);
|
||||
var t = (_TermCurrentFColor << 6) + (_TermCurrentBColor << 12) + _TermCurrentReverse;
|
||||
for (var x = 0; x < _termx; x++) {
|
||||
_tscreen[_termy][x] = ' ';
|
||||
_scratt[_termy][x] = t;
|
||||
@ -475,7 +549,7 @@ var CreateAmtRemoteTerminal = function (divid) {
|
||||
}
|
||||
|
||||
function _EraseLine(line) {
|
||||
var t = (_TermCurrentBColor << 12);
|
||||
var t = (_TermCurrentFColor << 6) + (_TermCurrentBColor << 12) + _TermCurrentReverse;
|
||||
for (var x = 0; x < obj.width; x++) {
|
||||
_tscreen[line][x] = ' ';
|
||||
_scratt[line][x] = t;
|
||||
@ -487,11 +561,11 @@ var CreateAmtRemoteTerminal = function (divid) {
|
||||
|
||||
function _TermMoveUp(linecount) {
|
||||
var x, y;
|
||||
for (y = 0; y < obj.height - linecount; y++) {
|
||||
for (y = _scrollRegion[0]; y <= _scrollRegion[1] - linecount; y++) {
|
||||
_tscreen[y] = _tscreen[y + linecount];
|
||||
_scratt[y] = _scratt[y + linecount];
|
||||
}
|
||||
for (y = obj.height - linecount; y < obj.height; y++) {
|
||||
for (y = _scrollRegion[1] - linecount + 1; y <= _scrollRegion[1]; y++) {
|
||||
_tscreen[y] = [];
|
||||
_scratt[y] = [];
|
||||
for (x = 0; x < obj.width; x++) {
|
||||
@ -504,7 +578,7 @@ var CreateAmtRemoteTerminal = function (divid) {
|
||||
obj.TermHandleKeys = function (e) {
|
||||
if (!e.ctrlKey) {
|
||||
if (e.which == 127) obj.TermSendKey(8);
|
||||
else if (e.which == 13) obj.TermSendKeys(obj.lineFeed);
|
||||
else if (e.which == 13) { obj.TermSendKeys(obj.lineFeed); }
|
||||
else if (e.which != 0) obj.TermSendKey(e.which);
|
||||
return false;
|
||||
}
|
||||
@ -527,10 +601,19 @@ var CreateAmtRemoteTerminal = function (divid) {
|
||||
return;
|
||||
}
|
||||
if (e.which == 27) { obj.TermSendKeys(String.fromCharCode(27)); return true; }; // ESC
|
||||
if (e.which == 37) { obj.TermSendKeys(String.fromCharCode(27, 91, 68)); return true; }; // Left
|
||||
if (e.which == 38) { obj.TermSendKeys(String.fromCharCode(27, 91, 65)); return true; }; // Up
|
||||
if (e.which == 39) { obj.TermSendKeys(String.fromCharCode(27, 91, 67)); return true; }; // Right
|
||||
if (e.which == 40) { obj.TermSendKeys(String.fromCharCode(27, 91, 66)); return true; }; // Down
|
||||
|
||||
if (_altKeypadMode == true) {
|
||||
if (e.which == 37) { obj.TermSendKeys(String.fromCharCode(27, 79, 68)); return true; }; // Left
|
||||
if (e.which == 38) { obj.TermSendKeys(String.fromCharCode(27, 79, 65)); return true; }; // Up
|
||||
if (e.which == 39) { obj.TermSendKeys(String.fromCharCode(27, 79, 67)); return true; }; // Right
|
||||
if (e.which == 40) { obj.TermSendKeys(String.fromCharCode(27, 79, 66)); return true; }; // Down
|
||||
} else {
|
||||
if (e.which == 37) { obj.TermSendKeys(String.fromCharCode(27, 91, 68)); return true; }; // Left
|
||||
if (e.which == 38) { obj.TermSendKeys(String.fromCharCode(27, 91, 65)); return true; }; // Up
|
||||
if (e.which == 39) { obj.TermSendKeys(String.fromCharCode(27, 91, 67)); return true; }; // Right
|
||||
if (e.which == 40) { obj.TermSendKeys(String.fromCharCode(27, 91, 66)); return true; }; // Down
|
||||
}
|
||||
|
||||
if (e.which == 9) { obj.TermSendKeys("\t"); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return true; }; // TAB
|
||||
|
||||
// F1 to F12 keys
|
||||
@ -569,12 +652,12 @@ var CreateAmtRemoteTerminal = function (divid) {
|
||||
for (var y = 0; y < obj.height; ++y) {
|
||||
for (var x = 0; x < obj.width; ++x) {
|
||||
newat = _scratt[y][x];
|
||||
if (_termx == x && _termy == y) { newat |= _VTREVERSE; } // If this is the cursor location, reverse the color.
|
||||
if (_termx == x && _termy == y && _cursorVisible) { newat |= _VTREVERSE; } // If this is the cursor location, reverse the color.
|
||||
if (newat != oldat) {
|
||||
buf += closetag;
|
||||
closetag = '';
|
||||
x1 = 6; x2 = 12;
|
||||
if (newat & _VTREVERSE) { x1 = 12; x2 = 6;}
|
||||
if (newat & _VTREVERSE) { x1 = 12; x2 = 6; }
|
||||
buf += '<span style="color:#' + _TermColors[(newat >> x1) & 0x3F] + ';background-color:#' + _TermColors[(newat >> x2) & 0x3F];
|
||||
if (newat & _VTUNDERLINE) buf += ';text-decoration:underline';
|
||||
buf += ';">';
|
||||
@ -584,17 +667,11 @@ var CreateAmtRemoteTerminal = function (divid) {
|
||||
|
||||
c = _tscreen[y][x];
|
||||
switch (c) {
|
||||
case '&':
|
||||
buf += '&'; break;
|
||||
case '<':
|
||||
buf += '<'; break;
|
||||
case '>':
|
||||
buf += '>'; break;
|
||||
case ' ':
|
||||
buf += ' '; break;
|
||||
default:
|
||||
buf += c;
|
||||
break;
|
||||
case '&': buf += '&'; break;
|
||||
case '<': buf += '<'; break;
|
||||
case '>': buf += '>'; break;
|
||||
case ' ': buf += ' '; break;
|
||||
default: buf += c; break;
|
||||
}
|
||||
}
|
||||
if (y != (obj.height - 1)) buf += '<br>';
|
||||
|
@ -180,7 +180,7 @@
|
||||
margin: 0;
|
||||
padding: 0 15px;
|
||||
background-color: #fff;
|
||||
max-height: calc(100vh - 111px);
|
||||
/*max-height: calc(100vh - 111px);*/
|
||||
min-width: unset;
|
||||
}
|
||||
.room4submenu {
|
||||
@ -188,7 +188,7 @@
|
||||
}
|
||||
|
||||
.fullscreen #column_l {
|
||||
height: calc(100vh - 135px);
|
||||
height: calc(100vh - 111px);
|
||||
width: calc(100% - 30px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
1
reinstall-modules.bat
Normal file
1
reinstall-modules.bat
Normal file
@ -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
File diff suppressed because one or more lines are too long
@ -1216,6 +1216,7 @@
|
||||
QV('authAppSetupCheck', userinfo.otpsecret == 1);
|
||||
QV('authKeySetupCheck', userinfo.otphkeys > 0);
|
||||
QV('authCodesSetupCheck', userinfo.otpkeys > 0);
|
||||
masterUpdate(4 + 128);
|
||||
|
||||
if (typeof userinfo.passchange == 'number') {
|
||||
if (userinfo.passchange == -1) { QH('p2nextPasswordUpdateTime', ' - Reset on next login.'); }
|
||||
@ -1655,6 +1656,7 @@
|
||||
}
|
||||
}
|
||||
masterUpdate(4 + 128);
|
||||
if (currentNode && (currentNode.meshid == message.event.meshid)) { refreshDevice(currentNode._id); }
|
||||
//meshserver.send({ action: 'files' }); // TODO: Why do we need to do this??
|
||||
|
||||
// If we are looking at a mesh that is now deleted, move back to "My Account"
|
||||
@ -1925,8 +1927,8 @@
|
||||
var meshrights = mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights;
|
||||
var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
|
||||
if (inputAllowed == false) return false;
|
||||
var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
|
||||
if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || (e.keyCode < 32) || (e.keyCode > 90)) return false; }
|
||||
var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
|
||||
if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; }
|
||||
}
|
||||
return desktop.m.handleKeys(e);
|
||||
}
|
||||
@ -1984,8 +1986,8 @@
|
||||
var meshrights = mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights;
|
||||
var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
|
||||
if (inputAllowed == false) return false;
|
||||
var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
|
||||
if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || (e.keyCode < 32) || (e.keyCode > 90)) return false; }
|
||||
var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
|
||||
if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; }
|
||||
}
|
||||
return desktop.m.handleKeyDown(e);
|
||||
}
|
||||
@ -2018,8 +2020,8 @@
|
||||
var meshrights = mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights;
|
||||
var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
|
||||
if (inputAllowed == false) return false;
|
||||
var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
|
||||
if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || (e.keyCode < 32) || (e.keyCode > 90)) return false; }
|
||||
var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
|
||||
if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; }
|
||||
}
|
||||
return desktop.m.handleKeyUp(e);
|
||||
}
|
||||
@ -2259,8 +2261,8 @@
|
||||
|
||||
// Add a "Add Device Group" option
|
||||
r += '<div style=border-top-style:solid;border-top-width:1px;border-top-color:#DDDDDD;cursor:pointer;font-size:10px>';
|
||||
if ((view < 3) && (sort == 0) && (meshcount > 0)) { r += '<a onclick=account_createMesh() title="Create a new group of devices." style=cursor:pointer>Add Device Group</a> '; }
|
||||
r += '<a onclick=p10showMeshCmdDialog(0) style=cursor:pointer title="Download MeshCmd, a command line tool that performs many functions.">MeshCmd</a></div>';
|
||||
if ((view < 3) && (sort == 0) && (meshcount > 0) && ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 64) == 0))) { r += '<a onclick=account_createMesh() title="Create a new group of devices." style=cursor:pointer>Add Device Group</a> '; }
|
||||
if ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 128) == 0)) { r += '<a onclick=p10showMeshCmdDialog(0) style=cursor:pointer title="Download MeshCmd, a command line tool that performs many functions.">MeshCmd</a></div>'; }
|
||||
r += '</div><br/>';
|
||||
|
||||
QH('xdevices', r);
|
||||
@ -3539,7 +3541,6 @@
|
||||
//
|
||||
// MY DEVICE
|
||||
//
|
||||
|
||||
function refreshDevice(nodeid) {
|
||||
if (!currentNode || currentNode._id != nodeid) return;
|
||||
gotoDevice(nodeid, xxcurrentView, true);
|
||||
@ -3577,7 +3578,7 @@
|
||||
// Add node name
|
||||
var nname = EscapeHtml(node.name);
|
||||
if (nname.length == 0) { nname = '<i>None</i>'; }
|
||||
if ((meshrights & 4) != 0) { nname = '<span title="Click here to edit the server-side device name" onclick=showEditNodeValueDialog(0) style=cursor:pointer>' + nname + ' <img class=hoverButton src="images/link5.png" /></span>'; }
|
||||
if (((meshrights & 4) != 0) && ((!mesh.flags) || ((mesh.flags & 2) == 0))) { nname = '<span title="Click here to edit the server-side device name" onclick=showEditNodeValueDialog(0) style=cursor:pointer>' + nname + ' <img class=hoverButton src="images/link5.png" /></span>'; }
|
||||
QH('p10deviceName', nname);
|
||||
QH('p11deviceName', nname);
|
||||
QH('p12deviceName', nname);
|
||||
@ -4850,7 +4851,7 @@
|
||||
terminal = CreateAgentRedirect(meshserver, CreateAmtRemoteTerminal('Term'), serverPublicNamePort, authCookie);
|
||||
terminal.debugmode = debugmode;
|
||||
terminal.m.debugmode = debugmode;
|
||||
terminal.m.lineFeed = ([1,2,3,4,21,22].indexOf(currentNode.agent.id) >= 0)?'\r\n':'\n'; // On windows, send \r\n, on Linux only \n
|
||||
terminal.m.lineFeed = ([1, 2, 3, 4, 21, 22].indexOf(currentNode.agent.id) >= 0) ? '\r\n' : '\r'; // On windows, send \r\n, on Linux only \r
|
||||
terminal.attemptWebRTC = attemptWebRTC;
|
||||
terminal.onStateChanged = onTerminalStateChange;
|
||||
terminal.Start(terminalNode._id);
|
||||
@ -5780,6 +5781,9 @@
|
||||
function account_createMesh() {
|
||||
if (xxdialogMode) return;
|
||||
|
||||
// Check if we are disallowed from creating a device group
|
||||
if ((userinfo.siteadmin != 0xFFFFFFFF) && ((userinfo.siteadmin & 64) != 0)) { setDialogMode(2, "New Device Group", 1, null, "This account does not have the rights to create a new device group."); return; }
|
||||
|
||||
// Check if we are allowed to create a new device group
|
||||
if ((userinfo.emailVerified !== true) && (serverinfo.emailcheck == true) && (userinfo.siteadmin != 0xFFFFFFFF)) { setDialogMode(2, "New Device Group", 1, null, "Unable to create a new device group until a email address is verified. This is required for password recovery. Go to the \"My Account\" tab to change and verify an email address."); return; }
|
||||
|
||||
@ -5934,7 +5938,10 @@
|
||||
|
||||
// Display features
|
||||
var meshFeatures = [];
|
||||
if (currentMesh.flags) { if (currentMesh.flags & 1) { meshFeatures.push('Auto-Remove'); } }
|
||||
if (currentMesh.flags) {
|
||||
if (currentMesh.flags & 1) { meshFeatures.push('Auto-Remove'); }
|
||||
if (currentMesh.flags & 2) { meshFeatures.push('Hostname Sync'); }
|
||||
}
|
||||
meshFeatures = meshFeatures.join(', ');
|
||||
if (meshFeatures == '') { meshFeatures = '<i>None</i>'; }
|
||||
x += addHtmlValue('Features', addLinkConditional(meshFeatures, 'p20editmeshfeatures()', (meshrights & 1) != 0));
|
||||
@ -6101,12 +6108,14 @@
|
||||
if (xxdialogMode) return;
|
||||
var flags = (currentMesh.flags)?currentMesh.flags:0;
|
||||
var x = "<div><input type=checkbox id=d20flag1 " + ((flags & 1)?'checked':'') + ">Remove device on disconnect<br></div>";
|
||||
x += "<div><input type=checkbox id=d20flag2 " + ((flags & 2) ? 'checked' : '') + ">Sync server device name to hostname<br></div>";
|
||||
setDialogMode(2, "Edit Device Group Features", 3, p20editmeshfeaturesEx, x);
|
||||
}
|
||||
|
||||
function p20editmeshfeaturesEx() {
|
||||
var flags = 0;
|
||||
if (Q('d20flag1').checked) { flags += 1; }
|
||||
if (Q('d20flag2').checked) { flags += 2; }
|
||||
meshserver.send({ action: 'editmesh', meshid: currentMesh._id, flags: flags });
|
||||
}
|
||||
|
||||
@ -6594,7 +6603,7 @@
|
||||
if (self) { permissions += "<a style=cursor:pointer onclick=showUserAdminDialog(event,\"" + encodeURIComponent(user._id) + "\")>"; }
|
||||
if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { permissions += "Locked, "; }
|
||||
permissions += "<span title='Server Permissions'>";
|
||||
if ((user.siteadmin == null) || (user.siteadmin == 0) || (user.siteadmin == 32)) {
|
||||
if ((user.siteadmin == null) || (user.siteadmin == 0) || ((user.siteadmin & (0xFFFFFFFF - 224)) == 0)) {
|
||||
permissions += "User";
|
||||
} else if (user.siteadmin == 8) {
|
||||
permissions += "User + Files";
|
||||
@ -6603,6 +6612,7 @@
|
||||
} else {
|
||||
permissions += "Partial";
|
||||
}
|
||||
if ((user.siteadmin != null) && (user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & (64 + 128)) != 0)) { permissions += "*"; }
|
||||
permissions += "</span>";
|
||||
//if ((user.quota != null) && ((user.siteadmin & 8) != 0)) { msg += ", " + (user.quota / 1024) + " k"; }
|
||||
if (self) { permissions += "</a>"; }
|
||||
@ -6714,6 +6724,8 @@
|
||||
x += '<input type=checkbox onchange=showUserAdminDialogValidate() id=ua_serverupdate>Server Updates<br>';
|
||||
x += '<input type=checkbox onchange=showUserAdminDialogValidate() id=ua_manageusers>Manage Users<br>';
|
||||
x += '<hr/><input type=checkbox onchange=showUserAdminDialogValidate() id=ua_lockedaccount>Lock Account<br>';
|
||||
x += '<input type=checkbox onchange=showUserAdminDialogValidate() id=ua_nonewgroups>No New Device Groups<br>';
|
||||
x += '<input type=checkbox onchange=showUserAdminDialogValidate() id=ua_nomeshcmd>No MeshCmd<br>';
|
||||
x += '</div>';
|
||||
var user = users[userid.toLowerCase()];
|
||||
setDialogMode(2, "Server Permissions", 3, showUserAdminDialogEx, x, user);
|
||||
@ -6725,6 +6737,8 @@
|
||||
Q('ua_fileaccess').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 8) != 0)); // Server Files
|
||||
Q('ua_serverupdate').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 16) != 0)); // Server Update
|
||||
Q('ua_lockedaccount').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 32) != 0)); // Account locked
|
||||
Q('ua_nonewgroups').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 64) != 0)); // No New Groups
|
||||
Q('ua_nomeshcmd').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 128) != 0)); // No MeshCmd
|
||||
}
|
||||
QE('ua_fulladmin', userinfo.siteadmin == 0xFFFFFFFF);
|
||||
QE('ua_serverbackup', userinfo.siteadmin == 0xFFFFFFFF);
|
||||
@ -6732,6 +6746,9 @@
|
||||
QE('ua_serverrestore', userinfo.siteadmin == 0xFFFFFFFF);
|
||||
QE('ua_fileaccess', userinfo.siteadmin == 0xFFFFFFFF);
|
||||
QE('ua_serverupdate', userinfo.siteadmin == 0xFFFFFFFF);
|
||||
QE('ua_lockedaccount', userinfo.siteadmin == 0xFFFFFFFF);
|
||||
QE('ua_nonewgroups', userinfo.siteadmin == 0xFFFFFFFF);
|
||||
QE('ua_nomeshcmd', userinfo.siteadmin == 0xFFFFFFFF);
|
||||
Q('ua_fileaccessquota').value = (user.quota != null)?(user.quota / 1024):'';
|
||||
showUserAdminDialogValidate();
|
||||
return false;
|
||||
@ -6744,6 +6761,9 @@
|
||||
QE('ua_serverrestore', !Q('ua_fulladmin').checked);
|
||||
QE('ua_fileaccess', !Q('ua_fulladmin').checked);
|
||||
QE('ua_serverupdate', !Q('ua_fulladmin').checked);
|
||||
QE('ua_lockedaccount', !Q('ua_fulladmin').checked);
|
||||
QE('ua_nonewgroups', !Q('ua_fulladmin').checked);
|
||||
QE('ua_nomeshcmd', !Q('ua_fulladmin').checked);
|
||||
QE('ua_fileaccessquota', Q('ua_fileaccess').checked && !Q('ua_fulladmin').checked);
|
||||
}
|
||||
}
|
||||
@ -6757,6 +6777,8 @@
|
||||
if (Q('ua_fileaccess').checked == true) siteadmin += 8;
|
||||
if (Q('ua_serverupdate').checked == true) siteadmin += 16;
|
||||
if (Q('ua_lockedaccount').checked == true) siteadmin += 32;
|
||||
if (Q('ua_nonewgroups').checked == true) siteadmin += 64;
|
||||
if (Q('ua_nomeshcmd').checked == true) siteadmin += 128;
|
||||
}
|
||||
var x = { action: 'edituser', name: user.name, siteadmin: siteadmin };
|
||||
if (isNaN(quota) == false) { x.quota = (quota * 1024); }
|
||||
@ -6784,16 +6806,17 @@
|
||||
if (activeSessions == 0) { Q('MainUserImage').classList.add('gray'); }
|
||||
|
||||
// Server permissions
|
||||
var msg = '', premsg = '';
|
||||
if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { premsg = '<img src="images/padlock12.png" height=12 width=8 title="Account is locked" style="margin-top:2px" /> '; msg += 'Locked account, '; }
|
||||
if ((user.siteadmin == null) || (user.siteadmin == 0) || (user.siteadmin == 32)) { msg += "No server rights"; } else if (user.siteadmin == 8) { msg += "Access to server files"; } else if (user.siteadmin == 0xFFFFFFFF) { msg += "Full administrator"; } else { msg += "Partial rights"; }
|
||||
var msg = [], premsg = '';
|
||||
if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { premsg = '<img src="images/padlock12.png" height=12 width=8 title="Account is locked" style="margin-top:2px" /> '; msg.push("Locked account"); }
|
||||
if ((user.siteadmin == null) || ((user.siteadmin & (0xFFFFFFFF - 224)) == 0)) { msg.push("No server rights"); } else if (user.siteadmin == 8) { msg.push("Access to server files"); } else if (user.siteadmin == 0xFFFFFFFF) { msg.push("Full administrator"); } else { msg.push("Partial rights"); }
|
||||
if ((user.siteadmin != null) && ((user.siteadmin & (64 + 128)) != 0)) { msg.push("Restrictions"); }
|
||||
|
||||
// Show user attributes
|
||||
var x = '<div style=min-height:80px><table style=width:100%>';
|
||||
var email = user.email?EscapeHtml(user.email):'<i>Not set</i>', everify = '';
|
||||
if (serverinfo.emailcheck) { everify = ((user.emailVerified == true)?'<b style=color:green;cursor:pointer title="Email is verified">🗸</b> ':'<b style=color:red;cursor:pointer title="Email not verified">🗴</b> '); }
|
||||
x += addDeviceAttribute('Email', everify + "<a style=cursor:pointer onclick=p30showUserEmailChangeDialog(event,\"" + userid + "\")>" + email + '</a> <a style=cursor:pointer onclick=doemail(event,\"' + user.email + '\")><img class=hoverButton src="images/link1.png" /></a>');
|
||||
x += addDeviceAttribute('Server Rights', premsg + "<a style=cursor:pointer onclick=showUserAdminDialog(event,\"" + userid + "\")>" + msg + "</a>");
|
||||
x += addDeviceAttribute('Server Rights', premsg + "<a style=cursor:pointer onclick=showUserAdminDialog(event,\"" + userid + "\")>" + msg.join(', ') + "</a>");
|
||||
if (user.quota) x += addDeviceAttribute('Server Quota', EscapeHtml(parseInt(user.quota) / 1024) + ' k');
|
||||
x += addDeviceAttribute('Creation', new Date(user.creation * 1000).toLocaleString());
|
||||
if (user.login) x += addDeviceAttribute('Last Login', new Date(user.login * 1000).toLocaleString());
|
||||
@ -7274,7 +7297,7 @@
|
||||
}
|
||||
};
|
||||
|
||||
function refreshServerTimelineStats(stats) { meshserver.send({ action: 'servertimelinestats', hours: 24 }); }
|
||||
function refreshServerTimelineStats(stats) { meshserver.send({ action: 'servertimelinestats', hours: 24 * 30 }); }
|
||||
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) {
|
||||
@ -7479,7 +7502,7 @@
|
||||
|
||||
// My Server
|
||||
if ((x == 6) || (x == 115)) QC('MainMenuMyServer').add(mainMenuActiveClass);
|
||||
if ((x == 6) || (x == 115)) QC('LeftMenuMyServer').add(leftMenuActiveClass);
|
||||
if ((x == 6) || (x == 115) || (x == 40)) QC('LeftMenuMyServer').add(leftMenuActiveClass);
|
||||
|
||||
// column_l max-height
|
||||
if (!webPageFullScreen && x >= 10) {
|
||||
|
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);
|
||||
|
@ -8,7 +8,7 @@
|
||||
<meta name="format-detection" content="telephone=no" />
|
||||
<link type="text/css" href="styles/style.css" media="screen" rel="stylesheet" title="CSS" />
|
||||
<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>{{{title}}} - Login</title>
|
||||
</head>
|
||||
<body id="body" onload="if (typeof(startup) !== 'undefined') startup();" class="arg_hide">
|
||||
@ -243,7 +243,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}}}');
|
||||
@ -284,12 +284,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'], }
|
||||
);
|
||||
}
|
||||
|
||||
@ -297,11 +297,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);
|
||||
@ -323,7 +323,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);
|
||||
});
|
||||
|
70
webserver.js
70
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;
|
||||
@ -138,6 +138,10 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
// If this domain has a DNS and a matching DNS cert, use it. This case works for wildcard certs.
|
||||
obj.webCertificateFullHashs[i] = parent.certificateOperations.getCertHashBinary(obj.certificates.dns[i].cert);
|
||||
obj.webCertificateHashs[i] = parent.certificateOperations.getPublicKeyHashBinary(obj.certificates.dns[i].cert);
|
||||
} else if (i != '') {
|
||||
// For any other domain, use the default cert.
|
||||
obj.webCertificateFullHashs[i] = obj.webCertificateFullHashs[''];
|
||||
obj.webCertificateHashs[i] = obj.webCertificateHashs[''];
|
||||
}
|
||||
}
|
||||
|
||||
@ -328,12 +332,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 +400,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 +466,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 +1161,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 +1199,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 +1214,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 +1252,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
Block a user