Added user authentication option.

This commit is contained in:
Ylian Saint-Hilaire 2018-10-11 13:29:50 -07:00
parent 6f87d8aaaf
commit 207bad322a
13 changed files with 172 additions and 117 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -342,9 +342,9 @@ function run(argv) {
var amtMeiModule = require('amt-mei');
var amtMei = new amtMeiModule();
amtMei.on('error', function (e) { console.log('ERROR: ' + e); exit(1); return; });
amtMei.getVersion(function (val) { for (var version in val.Versions) { if (val.Versions[version].Description == 'AMT') { mestate.ver = val.Versions[version].Version; } } });
amtMei.getProvisioningState(function (result) { mestate.ProvisioningState = result; });
amtMei.getProvisioningMode(function (result) { mestate.ProvisioningMode = result; });
amtMei.getVersion(function (result) { if (result) { for (var version in result.Versions) { if (result.Versions[version].Description == 'AMT') { mestate.ver = result.Versions[version].Version; } } } });
amtMei.getProvisioningState(function (result) { if (result) { mestate.ProvisioningState = result; } });
amtMei.getProvisioningMode(function (result) { if (result) { mestate.ProvisioningMode = result; } });
amtMei.getEHBCState(function (result) { if (result) { mestate.ehbc = result; } });
amtMei.getControlMode(function (result) { if (result) { mestate.controlmode = result; } });
amtMei.getMACAddresses(function (result) { if (result) { mestate.mac = result; } });
@ -352,24 +352,28 @@ function run(argv) {
amtMei.getLanInterfaceSettings(1, function (result) { if (result) { mestate.net1 = result; } });
amtMei.getUuid(function (result) { if ((result != null) && (result.uuid != null)) { mestate.uuid = result.uuid; } });
amtMei.getDnsSuffix(function (result) {
mestate.dns = result;
var str = 'Intel AMT v' + mestate.ver;
if (mestate.ProvisioningState.stateStr == 'PRE') { str += ', pre-provisioning state'; }
else if (mestate.ProvisioningState.stateStr == 'IN') { str += ', in-provisioning state'; }
else if (mestate.ProvisioningState.stateStr == 'POST') {
if (mestate.ProvisioningMode) {
if (mestate.controlmode) {
if (mestate.ProvisioningMode.modeStr == 'ENTERPRISE') { str += ', activated in ' + ["none", "client control mode", "admin control mode", "remote assistance mode"][mestate.controlmode.controlMode]; } else { str += ', activated in ' + mestate.ProvisioningMode.modeStr; }
} else {
str += ', activated in ' + mestate.ProvisioningMode.modeStr;
if (result) { mestate.dns = result; }
if (mestate.ver && mestate.ProvisioningState && mestate.ProvisioningMode) {
var str = 'Intel AMT v' + mestate.ver;
if (mestate.ProvisioningState.stateStr == 'PRE') { str += ', pre-provisioning state'; }
else if (mestate.ProvisioningState.stateStr == 'IN') { str += ', in-provisioning state'; }
else if (mestate.ProvisioningState.stateStr == 'POST') {
if (mestate.ProvisioningMode) {
if (mestate.controlmode) {
if (mestate.ProvisioningMode.modeStr == 'ENTERPRISE') { str += ', activated in ' + ["none", "client control mode", "admin control mode", "remote assistance mode"][mestate.controlmode.controlMode]; } else { str += ', activated in ' + mestate.ProvisioningMode.modeStr; }
} else {
str += ', activated in ' + mestate.ProvisioningMode.modeStr;
}
}
}
if ((mestate.ehbc) && (mestate.ehbc.EHBC == true)) { str += ', EHBC enabled'; }
str += '.';
if (mestate.net0 != null) { str += '\r\nWired ' + ((mestate.net0.enabled == 1) ? 'Enabled' : 'Disabled') + ((mestate.net0.dhcpEnabled == 1) ? ', DHCP' : ', Static') + ', ' + mestate.net0.mac + (mestate.net0.address == '0.0.0.0' ? '' : (', ' + mestate.net0.address)); }
if (mestate.net1 != null) { str += '\r\nWireless ' + ((mestate.net1.enabled == 1) ? 'Enabled' : 'Disabled') + ((mestate.net1.dhcpEnabled == 1) ? ', DHCP' : ', Static') + ', ' + mestate.net1.mac + (mestate.net1.address == '0.0.0.0' ? '' : (', ' + mestate.net1.address)); }
console.log(str + '.');
} else {
console.log('Intel(R) AMT not supported.');
}
if ((mestate.ehbc) && (mestate.ehbc.EHBC == true)) { str += ', EHBC enabled'; }
str += '.';
if (mestate.net0 != null) { str += '\r\nWired ' + ((mestate.net0.enabled == 1) ? 'Enabled' : 'Disabled') + ((mestate.net0.dhcpEnabled == 1) ? ', DHCP' : ', Static') + ', ' + mestate.net0.mac + (mestate.net0.address == '0.0.0.0' ? '' : (', ' + mestate.net0.address)); }
if (mestate.net1 != null) { str += '\r\nWireless ' + ((mestate.net1.enabled == 1) ? 'Enabled' : 'Disabled') + ((mestate.net1.dhcpEnabled == 1) ? ', DHCP' : ', Static') + ', ' + mestate.net1.mac + (mestate.net1.address == '0.0.0.0' ? '' : (', ' + mestate.net1.address)); }
console.log(str + '.');
exit(1);
});
} else if (settings.action == 'amtinfodebug') {

View File

@ -25,7 +25,7 @@ function dbus(address, uid)
.createEvent('signal');
Object.defineProperty(this, "uid", { value: uid });
this._child = require('child_process').execFile("/bin/sh", ["sh"], { type: require('child_process').SpawnTypes.TERM, uid: uid == null ? -1 : uid });
this._child.stdin.write('dbus-monitor --session "type=\'signal\', interface=\'' + address + '\'" | ( while true; do read X; echo "$X"; done )\n');
this._child.stdin.write('dbus-monitor --session "type=\'signal\', interface=\'' + address + '\'" | ( while read X; do echo "$X"; done )\n');
this._child.stdout.dbus = this;
this._child.stdout.on('data', function (chunk)
{

View File

@ -86,7 +86,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
req.session.currentNode = '';
} else {
// Close the websocket connection
try { obj.ws.close(); } catch (e) { } return;
console.log('NOAUTH1');
ws.send(JSON.stringify({ action: 'close', cause: 'noauth' }));
try { obj.ws.close(); } catch (e) { }
return;
}
}
req.session.ws = obj.ws; // Associate this websocket session with the web session
@ -120,8 +123,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
}
};
user.subscriptions = obj.parent.subscribe(user._id, ws); // Subscribe to events
obj.ws._socket.setKeepAlive(true, 240000); // Set TCP keep alive
user.subscriptions = obj.parent.subscribe(user._id, ws); // Subscribe to events
try { obj.ws._socket.setKeepAlive(true, 240000); } catch (ex) { } // Set TCP keep alive
// Send current server statistics
obj.SendServerStats = function () {
@ -329,7 +332,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
command.sessionid = ws.sessionId; // Set the session id, required for responses.
command.rights = rights.rights; // Add user rights flags to the message
delete command.nodeid; // Remove the nodeid since it's implyed.
agent.send(JSON.stringify(command));
try { agent.send(JSON.stringify(command)); } catch (ex) { }
}
} else {
// Check if a peer server is connected to this agent
@ -601,7 +604,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
// Get the list of sessions for this user
var sessions = obj.parent.wssessions[command.userid];
if (sessions != null) { for (i in sessions) { sessions[i].send(JSON.stringify(notification)); } }
if (sessions != null) { for (i in sessions) { try { sessions[i].send(JSON.stringify(notification)); } catch (ex) { } } }
if (obj.parent.parent.multiServer != null) {
// TODO: Add multi-server support
@ -925,7 +928,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
var agent = obj.parent.wsagents[i];
if ((targetMeshes.indexOf(agent.dbMeshKey) >= 0) && (agent.authenticated == 2)) {
//console.log('Asking agent ' + agent.dbNodeKey + ' to wake ' + macs.join(','));
agent.send(JSON.stringify({ action: 'wakeonlan', macs: macs }));
try { agent.send(JSON.stringify({ action: 'wakeonlan', macs: macs })); } catch (ex) { }
wakeActions++;
}
}
@ -966,7 +969,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
var agent = obj.parent.wsagents[node._id];
if (agent != null) {
// Send the power command
agent.send(JSON.stringify({ action: 'poweraction', actiontype: command.actiontype }));
try { agent.send(JSON.stringify({ action: 'poweraction', actiontype: command.actiontype })); } catch (ex) { }
powerActions++;
}
}
@ -1004,7 +1007,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
var agent = obj.parent.wsagents[node._id];
if (agent != null) {
// Send the power command
agent.send(JSON.stringify({ action: 'toast', title: command.title, msg: command.msg }));
try { agent.send(JSON.stringify({ action: 'toast', title: command.title, msg: command.msg })); } catch (ex) { }
}
}
}

View File

@ -679,7 +679,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
};
function ChangeHostname(socket, host) {
if (socket.tag.host == host) return; // Nothing to change
if (socket.tag.host === host) return; // Nothing to change
socket.tag.host = host;
// Change the device

View File

@ -1,6 +1,6 @@
{
"name": "meshcentral",
"version": "0.2.2-c",
"version": "0.2.2-d",
"keywords": [
"Remote Management",
"Intel AMT",

View File

@ -155,58 +155,59 @@
<div id=devViewButton3 class=viewSelector onclick=onDeviceViewChange(3) title="Desktops"><div class="viewSelector3"></div></div>
<div id=devViewButton4 class=viewSelector onclick=onDeviceViewChange(4) title="Map"><div class="viewSelector4"></div></div>
</div><div><h1>My Devices</h1></div>
<div style=width:100%;height:24px;background-color:#d3d9d6>
<div class=h1 style=height:100%;float:left>&nbsp;</div>
<div id=devListToolbar class=style14 style="height:100%;float:left;vertical-align:middle">
&nbsp;&nbsp;<input type="button" id="SelectAllButton" onclick="selectallButtonFunction();" value="Select All" />&nbsp;
<input type=button id=GroupActionButton disabled="disabled" value="Group Action" onclick=groupActionFunction() />&nbsp;
<input id=SearchInput type=text style=width:120px placeholder=Search onchange=onSearchInputChanged() onkeyup=onSearchInputChanged() autocomplete=off onfocus=onSearchFocus(1) onblur=onSearchFocus(0) />&nbsp;
<input type=checkbox id=RealNameCheckBox onclick=onRealNameCheckBox() /><span title="Show devices operating system name">OS Name</span>
</div>
<div id=kvmListToolbar class=style14 style=height:100%;float:left>
&nbsp;&nbsp;<input type="button" onclick="connectAllKvmFunction()" value="Connect All" />&nbsp;
<input type="button" onclick="disconnectAllKvmFunction()" value="Disconnect All" />&nbsp;
<input type="checkbox" id="autoConnectDesktopCheckbox" onclick="autoConnectDesktops(event)" title="Automatic connect" />Auto&nbsp;
<input type="button" onclick="showMultiDesktopSettings()" value="Settings" />&nbsp;
</div>
<div id=devMapToolbar class=style14 style=height:100%;float:left>
&nbsp;&nbsp;<input type=text id=mapSearchLocation placeholder="Search Location" onfocus=onMapSearchFocus(1) onblur=onMapSearchFocus(0) />
<input type=button value=Search title="Search for location" onclick=getSearchLocation() />
<input type=button id=refreshmap title="Reset map view" value=Reset style=margin-left:5px onclick=refreshMap(false,true) />
</div>
<div class="auto-style1" style=height:100%;float:right>
<div style="height:100%;width:4px;float:right;background-color:#ffffff"></div>
<div class=h2 style="height:100%;float:right">&nbsp;</div>
<div style="float:right;display:none" id=devListToolbarView>
View
<select id=viewselect onchange=onDeviceViewChange()>
<option value=1>Columns</option>
<option value=2>List</option>
<option value=3>Desktops</option>
<option id=viewselectmapoption value=4>Map</option>
</select>
</div>
<div style=float:right id=devListToolbarSort>
Sort
<select id=sortselect onchange=onSortSelectChange()>
<option>Group</option>
<option>Power</option>
<option>Device</option>
<option>Tags</option>
</select>
&nbsp;
</div>
<div style=float:right id=devListToolbarSize>
Size
<select id=sizeselect onchange=onDeviceViewChange()>
<option value=0>Small</option>
<option value=1>Medium</option>
<option value=2>Large</option>
</select>
&nbsp;
</div>
</div>
</div>
<table style=width:100%;height:24px;background-color:#d3d9d6;vertical-align:middle;border-spacing:0>
<tr>
<td class=h1></td>
<td id=devListToolbar class=style14>
&nbsp;&nbsp;<input type="button" id="SelectAllButton" onclick="selectallButtonFunction();" value="Select All" />&nbsp;
<input type=button id=GroupActionButton disabled="disabled" value="Group Action" onclick=groupActionFunction() />&nbsp;
<input id=SearchInput type=text style=width:120px placeholder=Search onchange=onSearchInputChanged() onkeyup=onSearchInputChanged() autocomplete=off onfocus=onSearchFocus(1) onblur=onSearchFocus(0) />&nbsp;
<input type=checkbox id=RealNameCheckBox onclick=onRealNameCheckBox() /><span title="Show devices operating system name">OS Name</span>
</td>
<td id=kvmListToolbar class=style14 style=height:100%>
&nbsp;&nbsp;<input type="button" onclick="connectAllKvmFunction()" value="Connect All" />&nbsp;
<input type="button" onclick="disconnectAllKvmFunction()" value="Disconnect All" />&nbsp;
<input type="checkbox" id="autoConnectDesktopCheckbox" onclick="autoConnectDesktops(event)" title="Automatic connect" />Auto&nbsp;
<input type="button" onclick="showMultiDesktopSettings()" value="Settings" />&nbsp;
</td>
<td id=devMapToolbar class=style14 style=height:100%>
&nbsp;&nbsp;<input type=text id=mapSearchLocation placeholder="Search Location" onfocus=onMapSearchFocus(1) onblur=onMapSearchFocus(0) />
<input type=button value=Search title="Search for location" onclick=getSearchLocation() />
<input type=button id=refreshmap title="Reset map view" value=Reset style=margin-left:5px onclick=refreshMap(false,true) />
</td>
<td class="auto-style1" style=height:100%>
<div style="float:right;display:none" id=devListToolbarView>
View
<select id=viewselect onchange=onDeviceViewChange()>
<option value=1>Columns</option>
<option value=2>List</option>
<option value=3>Desktops</option>
<option id=viewselectmapoption value=4>Map</option>
</select>
</div>
<div style=float:right id=devListToolbarSort>
Sort
<select id=sortselect onchange=onSortSelectChange()>
<option>Group</option>
<option>Power</option>
<option>Device</option>
<option>Tags</option>
</select>
&nbsp;
</div>
<div style=float:right id=devListToolbarSize>
Size
<select id=sizeselect onchange=onDeviceViewChange()>
<option value=0>Small</option>
<option value=1>Medium</option>
<option value=2>Large</option>
</select>
&nbsp;
</div>
</td>
<td class=h2></td>
</tr>
</table>
<div id=NoMeshesPanel style=display:none>
<table style="width:100%;padding:20px">
<tr>
@ -253,40 +254,38 @@
</div>
<div id=p3 style=display:none>
<h1>My Events</h1>
<div style=width:100%;height:24px;background-color:#d3d9d6;margin-bottom:4px>
<div class=style7 style=width:16px;height:100%;float:left>&nbsp;</div>
<div class=h1 style=height:100%;float:left>&nbsp;</div>
<div class=style14 style=height:100%;float:left>&nbsp;&nbsp;<input id=p2deleteall type=button onclick=showDeleteAllEventsDialog() style=display:none value="Delete All..." />&nbsp;</div>
<div class="auto-style1" style="height:100%;float:right">
Show
<select id=p3limitdropdown onchange=refreshEvents()>
<option value=60>Last 60</option>
<option value=120>Last 120</option>
<option value=250>Last 250</option>
<option value=500>Last 500</option>
<option value=1000>Last 1000</option>
</select>
<div style="height:100%;width:20px;float:right;background-color:#ffffff"></div>
<div class="h2" style="height:100%;float:right;">&nbsp;</div>
</div>
</div>
<table style=width:100%;height:24px;background-color:#d3d9d6;margin-bottom:4px;vertical-align:middle;border-spacing:0>
<tr>
<td class=h1></td>
<td>&nbsp;<input id=p2deleteall type=button onclick=showDeleteAllEventsDialog() style=display:none value="Delete All..." /></td>
<td class=auto-style1>
Show
<select id=p3limitdropdown onchange=refreshEvents()>
<option value=60>Last 60</option>
<option value=120>Last 120</option>
<option value=250>Last 250</option>
<option value=500>Last 500</option>
<option value=1000>Last 1000</option>
</select>
</td>
<td class=h2></td>
</tr>
</table>
<div id=p3events style="height:calc(100vh - 243px);overflow-y:scroll"></div>
</div>
<div id=p4 style=display:none>
<h1>My Users</h1>
<div style=width:100%;height:24px;background-color:#d3d9d6;margin-bottom:4px>
<div class=style7 style=width:16px;height:100%;float:left>&nbsp;</div>
<div class=h1 style=height:100%;float:left>&nbsp;</div>
<div class=style14 style=height:100%;float:left>
&nbsp;&nbsp;
<input type=button onclick=showCreateNewAccountDialog() value="New Account..." />&nbsp;
<input id=UserSearchInput type=text style=width:120px placeholder=Search onchange=onUserSearchInputChanged() onkeyup=onUserSearchInputChanged() autocomplete=off onfocus=onUserSearchFocus(1) onblur=onUserSearchFocus(0) />&nbsp;
</div>
<div class=auto-style1 style="height:100%;float:right">
<div style="height:100%;width:20px;float:right;background-color:#ffffff"></div>
<div class=h2 style="height:100%;float:right">&nbsp;</div>
</div>
</div>
<table style=width:100%;height:24px;background-color:#d3d9d6;margin-bottom:4px;vertical-align:middle;border-spacing:0>
<tr>
<td class=h1></td>
<td class=style14>
&nbsp;
<input type=button onclick=showCreateNewAccountDialog() value="New Account..." />&nbsp;
<input id=UserSearchInput type=text style=width:120px placeholder=Search onchange=onUserSearchInputChanged() onkeyup=onUserSearchInputChanged() autocomplete=off onfocus=onUserSearchFocus(1) onblur=onUserSearchFocus(0) />&nbsp;
</td>
<td class=h2></td>
</tr>
</table>
<div id="p3users" style="max-height:calc(100vh - 243px);overflow-y:auto"></div>
</div>
<div id=p5 style=display:none>
@ -1235,7 +1234,7 @@
x += addHtmlValue2('Current Version', '<b>' + EscapeHtml(message.current) + '</b>');
x += addHtmlValue2('Latest Version', '<b>' + EscapeHtml(message.latest) + '</b>');
x += '</div>';
if ((message.current == message.latest) || ((features & 2048) == 0)) {
if ((message.latest.indexOf('.') == -1) || (message.current == message.latest) || ((features & 2048) == 0)) {
setDialogMode(2, "MeshCentral Version", 1, null, x);
} else {
setDialogMode(2, "MeshCentral Version", 3, server_showVersionDlgEx, x + '<br /><input id=d2updateCheck type=checkbox onclick=server_showVersionDlgUpdate() /> Check and click OK to start server self-update.');
@ -2595,6 +2594,7 @@
} catch (ex) {
console.log(ex);
QV('viewselectmapoption', false);
QV('devViewButton4', false);
xxmap = null;
}
}
@ -6176,9 +6176,7 @@
for (var i in message.values) {
x += '<div class=userTableHeader style=margin-bottom:4px;width:200px>' + i + '</div>';
for (var j in message.values[i]) {
x += '<div style=width:300px;display:inline-block><div class=bar style=height:24px;width:100%;font-size:medium>';
x += '<div class=g1 style=height:24px;float:left></div><div class=g2 style=height:24px;float:right></div>';
x += '<div><span>' + j + '</span><span style=float:right>' + message.values[i][j] + '</span></div></div></div>';
x += '<div style=display:inline-block><table style=width:300px;height:24px;background-color:#d3d9d6;margin-bottom:4px;vertical-align:middle;border-spacing:0><tr><td class=h1></td><td><span>' + j + '</span><span style=float:right>' + message.values[i][j] + '</span></td><td class=h2></td></tr></table></div>';
}
}
}

View File

@ -214,7 +214,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (!module.parent) console.log('authenticating %s:%s:%s', domain.id, name, pass);
var user = obj.users['user/' + domain.id + '/' + name.toLowerCase()];
// Query the db for the given username
if (!user) return fn(new Error('cannot find user'));
if (!user) { fn(new Error('cannot find user')); return; }
// Apply the same algorithm to the POSTed password, applying the hash against the pass / salt, if there is a match we found the user
if (user.salt == null) {
fn(new Error('invalid password'));
@ -1803,7 +1803,6 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.app.post(url + 'amtevents.ashx', obj.handleAmtEventRequest);
obj.app.get(url + 'webrelay.ashx', function (req, res) { res.send('Websocket connection expected'); });
obj.app.ws(url + 'webrelay.ashx', handleRelayWebSocket);
obj.app.ws(url + 'control.ashx', function (ws, req) { try { var domain = checkUserIpAddress(ws, req); if (domain != null) { obj.meshUserHandler.CreateMeshUser(obj, obj.db, ws, req, obj.args, domain); } } catch (e) { console.log(e); } });
obj.app.get(url + 'meshagents', obj.handleMeshAgentRequest);
obj.app.get(url + 'meshosxagent', obj.handleMeshOsxAgentRequest);
obj.app.get(url + 'meshsettings', obj.handleMeshSettingsRequest);
@ -1814,6 +1813,57 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.app.ws(url + 'echo.ashx', handleEchoWebSocket);
obj.app.ws(url + 'meshrelay.ashx', function (ws, req) { try { obj.meshRelayHandler.CreateMeshRelay(obj, ws, req, getDomain(req)); } catch (e) { console.log(e); } });
// User login
obj.app.ws(url + 'control.ashx', function (ws, req) {
try {
var domain = checkUserIpAddress(ws, req);
if (domain != null) {
var loginok = false;
// Check if the user is logged in
if ((!req.session) || (!req.session.userid) || (req.session.domainid != domain.id)) {
// If a default user is active, setup the session here.
if (obj.args.user && obj.users['user/' + domain.id + '/' + obj.args.user.toLowerCase()]) {
if (req.session && req.session.loginmode) { delete req.session.loginmode; }
req.session.userid = 'user/' + domain.id + '/' + obj.args.user.toLowerCase();
req.session.domainid = domain.id;
req.session.currentNode = '';
obj.meshUserHandler.CreateMeshUser(obj, obj.db, ws, req, obj.args, domain); // Accept the connection
loginok = true;
} else {
// See the the user/pass is provided in URL arguments
if ((req.query.user != null) && (req.query.pass != null)) {
loginok = true;
obj.authenticate(req.query.user, req.query.pass, domain, function (err, userid) {
var loginok2 = false;
if (err == null) {
var user = obj.users[userid];
if (user) {
req.session.userid = userid;
req.session.domainid = domain.id;
req.session.currentNode = '';
obj.meshUserHandler.CreateMeshUser(obj, obj.db, ws, req, obj.args, domain); // Accept the connection
loginok2 = true;
}
}
if (loginok2 == false) {
// Close the websocket connection
try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth' })); ws.close(); } catch (e) { }
}
});
}
}
} else {
obj.meshUserHandler.CreateMeshUser(obj, obj.db, ws, req, obj.args, domain); // Accept the connection
loginok = true;
}
if (loginok == false) {
// Close the websocket connection
try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth' })); ws.close(); } catch (e) { }
}
}
} catch (e) { console.log(e); }
});
// Server picture
obj.app.get(url + 'serverpic.ashx', function (req, res) {
// Check if we have "server.png" in the data folder, if so, use that.