More work on device 2FA.
This commit is contained in:
parent
36b5163534
commit
48d5abca40
12
meshagent.js
12
meshagent.js
|
@ -1525,9 +1525,6 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
|||
try { url = require('url').parse(command.url); } catch (ex) { }
|
||||
if (url == null) return;
|
||||
|
||||
// For now, do nothing if authentication is not approved.
|
||||
if (command.approve == false) return;
|
||||
|
||||
// Decode the cookie
|
||||
var urlSplit = url.query.split('&c=');
|
||||
if (urlSplit.length != 2) return;
|
||||
|
@ -1541,6 +1538,10 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
|||
|
||||
// Add this device as the authentication push notification device for this user
|
||||
if (authCookie.a == 'addAuth') {
|
||||
// Do nothing if authentication is not approved.
|
||||
// We do not want to indicate that the remote user responded to this.
|
||||
if (command.approved !== true) return;
|
||||
|
||||
// Change the user
|
||||
user.otpdev = obj.dbNodeKey;
|
||||
parent.db.SetUser(user);
|
||||
|
@ -1555,8 +1556,9 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
|||
|
||||
// Complete 2FA checking
|
||||
if (authCookie.a == 'checkAuth') {
|
||||
// TODO
|
||||
//console.log(authCookie);
|
||||
if (typeof authCookie.s != 'string') return;
|
||||
// Notify 2FA response
|
||||
parent.parent.DispatchEvent(['2fadev-' + authCookie.s], obj, { etype: '2fadev', action: '2faresponse', domain: domain.id, nodeid: obj.dbNodeKey, code: authCookie.a, userid: user._id, approved: command.approved, sessionid: authCookie.s, nolog: 1 });
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Binary file not shown.
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
|
@ -2032,8 +2032,7 @@
|
|||
QV('authKeySetupCheck', userinfo.otphkeys > 0);
|
||||
QV('authPushAuthDevCheck', (userinfo.otpdev > 0) && ((features2 & 2) != 0));
|
||||
QV('authCodesSetupCheck', userinfo.otpkeys > 0);
|
||||
//QV('managePushAuthDev', (features2 & 2) && (count2factoraAuths() > 0));
|
||||
QV('managePushAuthDev', false);
|
||||
QV('managePushAuthDev', (features2 & 2) && (count2factoraAuths() > 0));
|
||||
mainUpdate(4 + 128 + 4096);
|
||||
|
||||
// Check if none or at least 2 factors are enabled.
|
||||
|
|
|
@ -284,20 +284,26 @@
|
|||
</form>
|
||||
</div>
|
||||
<div id=waitpushpanel style="display:none">
|
||||
<div id=message8></div>
|
||||
<table style="width:100%">
|
||||
<tr>
|
||||
<td style="align-content:center;padding-top:10px">
|
||||
<img src="images/login/push-150.png" srcset="images/login/push-300.png 2x" loading="lazy" width="265" height="150" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="align-content:center;padding-top:10px">
|
||||
<label id=tokenInputRememberLabel2><input id=tokenInputRemember2 name=remembertoken type=checkbox /><span id=tokenInputRememberSpan2></span></label>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr /><a onclick="return xgo(1,event);" href="#" style=cursor:pointer>Back to login</a>
|
||||
<form method=post>
|
||||
<input type=hidden name=action value=pushlogin />
|
||||
<div id=message8></div>
|
||||
<table style="width:100%">
|
||||
<tr>
|
||||
<td style="align-content:center;padding-top:10px">
|
||||
<img id="waitpushpanelimage" src="images/login/push-150.png" srcset="images/login/push-300.png 2x" style="opacity:0.3" loading="lazy" width="265" height="150" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="align-content:center;padding-top:10px">
|
||||
<label id=tokenInputRememberLabel2><input id=tokenInputRemember2 name=remembertoken type=checkbox /><span id=tokenInputRememberSpan2></span></label>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr /><a onclick="return xgo(1,event);" href="#" style=cursor:pointer>Back to login</a>
|
||||
<input id=pushtokenformargs name="urlargs" type="hidden" value="" />
|
||||
<input id=pushtokenInput name="hwstate" type="hidden" value="" />
|
||||
<input id=pushOkButton type=submit style="display:none" />
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -351,6 +357,7 @@
|
|||
var otppush = (decodeURIComponent('{{{otppush}}}') === 'true');
|
||||
var twoFactorCookieDays = parseInt('{{{twoFactorCookieDays}}}');
|
||||
var authStrategies = '{{{authStrategies}}}'.split(',');
|
||||
var websocket = null;
|
||||
|
||||
function startup() {
|
||||
if (decodeURIComponent('{{{loginpicture}}}') == 'true') { Q('loginPicture').src = "loginlogo.png"; }
|
||||
|
@ -361,13 +368,12 @@
|
|||
// Display the right server message
|
||||
var i;
|
||||
var messageid = parseInt('{{{messageid}}}');
|
||||
var okmessages = ['', "If valid, reset mail sent.", "Email sent.", "Email verification required, check your mailbox and click the confirmation link.", "SMS sent.", "Notification sent, {0}."];
|
||||
var okmessages = ['', "If valid, reset mail sent.", "Email sent.", "Email verification required, check your mailbox and click the confirmation link.", "SMS sent.", "Sending notification..."];
|
||||
var failmessages = ["Unable to create account.", "Account limit reached.", "Existing account with this email address.", "Invalid account creation token.", "Username already exists.", "Password rejected, use a different one.", "Invalid email.", "Account not found.", "Invalid token, try again.", "Unable to sent email.", "Account locked.", "Access denied.", "Login failed, check username and password.", "Password change requested.", "IP address blocked, try again later.", "Server under maintenance.", "Unable to send device notification."];
|
||||
if (messageid > 0) {
|
||||
var msg = '';
|
||||
if ((messageid < 100) && (messageid < okmessages.length)) { msg = okmessages[messageid]; }
|
||||
else if ((messageid >= 100) && ((messageid - 100) < failmessages.length)) { msg = failmessages[messageid - 100]; }
|
||||
if (messageid == 5) { msg = format(msg, passhint); }
|
||||
if (msg != '') {
|
||||
if (messageid >= 100) { msg = ('<span class="msg error"><b style=color:#8C001A>' + msg + '<b></span><br /><br />'); } else { msg = ('<span class="msg success"><b>' + msg + '</b></span><br /><br />'); }
|
||||
for (i = 1; i < 9; i++) { QH('message' + i, msg); }
|
||||
|
@ -403,16 +409,12 @@
|
|||
Q('createformargs').value = xurlargs;
|
||||
Q('resetformargs').value = xurlargs;
|
||||
Q('tokenformargs').value = xurlargs;
|
||||
Q('pushtokenformargs').value = xurlargs;
|
||||
Q('resettokenformargs').value = xurlargs;
|
||||
Q('resetpasswordformargs').value = xurlargs;
|
||||
Q('checkemailformargs').value = xurlargs;
|
||||
}
|
||||
|
||||
//var webPageFullScreen = getstore('webPageFullScreen', true);
|
||||
//if (webPageFullScreen == 'false') { webPageFullScreen = false; }
|
||||
//if (webPageFullScreen == 'true') { webPageFullScreen = true; }
|
||||
//toggleFullScreen();
|
||||
|
||||
if ((features & 32) == 0) {
|
||||
// Guard against other site's top frames (web bugs).
|
||||
var loc = null;
|
||||
|
@ -442,16 +444,6 @@
|
|||
if (authStrategies.indexOf('saml') >= 0) { QV('auth-saml', true); }
|
||||
}
|
||||
|
||||
// Display the welcome text
|
||||
/*
|
||||
if (welcomeText) {
|
||||
QH('welcomeText', welcomeText);
|
||||
} else {
|
||||
QH('welcomeText', addTextLink('MeshCentral', Q('welcomeText').innerHTML, 'http://www.meshcommander.com/meshcentral2'));
|
||||
}
|
||||
QV('welcomeText', true);
|
||||
*/
|
||||
|
||||
validateLogin();
|
||||
validateCreate();
|
||||
if (loginMode.length != 0) { go(parseInt(loginMode)); } else { go(1); }
|
||||
|
@ -487,38 +479,40 @@
|
|||
QV('2farow2', twofakey || emailkey || smskey || pushkey);
|
||||
}
|
||||
|
||||
/*
|
||||
if (loginMode == '5') {
|
||||
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
|
||||
if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) {
|
||||
if (typeof hardwareKeyChallenge.challenge == 'string') { hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; }
|
||||
|
||||
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', 'internal'] }
|
||||
);
|
||||
if (loginMode == '8') {
|
||||
// Perform websocket connection to server to wait for device authentication
|
||||
websocket = new WebSocket(passhint);
|
||||
websocket.onopen = function (e) { QS('waitpushpanelimage')['opacity'] = '1'; }
|
||||
websocket.onmessage = function (e) {
|
||||
if (typeof e.data != 'string') { this.close(); }
|
||||
var r = null;
|
||||
try { r = JSON.parse(e.data); } catch (ex) { }
|
||||
if (r.sent === true) {
|
||||
// Request was sent
|
||||
QH('message8', '<span class="msg success"><b>' + format("Request sent, {0}.", r.code) + '</b></span><br /><br />');
|
||||
} else if (r.sent === false) {
|
||||
// Request failed to send
|
||||
QH('message8', '<span class="msg error"><b style=color:#8C001A>' + "Failed to send request." + '<b></span><br /><br />');
|
||||
this.close();
|
||||
} else if (r.approved === true) {
|
||||
// Request approved
|
||||
this.close();
|
||||
QV('tokenInputRememberLabel2', false);
|
||||
QH('message8', '<span class="msg success"><b>' + "Request Accepted." + '</b></span><br /><br />');
|
||||
Q('pushtokenInput').value = r.token;
|
||||
Q('pushOkButton').click();
|
||||
} else {
|
||||
// Request rejected
|
||||
QH('message8', '<span class="msg error"><b style=color:#8C001A>' + "Access Rejected." + '<b></span><br /><br />');
|
||||
this.close();
|
||||
}
|
||||
|
||||
// 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); }
|
||||
);
|
||||
}
|
||||
websocket.onclose = function (e) { QS('waitpushpanelimage')['opacity'] = '0.3'; }
|
||||
websocket.onerror = function (e) {
|
||||
QH('message8', '<span class="msg error"><b style=color:#8C001A>' + "Connection Error" + '<b></span><br /><br />');
|
||||
QS('waitpushpanelimage')['opacity'] = '0.5';
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// Use a hardware security key
|
||||
|
|
122
webserver.js
122
webserver.js
|
@ -953,17 +953,59 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
}
|
||||
|
||||
if ((req.body.hwtoken == '**push**') && push2fa) {
|
||||
// Cause push notification to device
|
||||
const logincode = obj.common.zeroPad(getRandomSixDigitInteger(), 6);
|
||||
const code = Buffer.from(logincode).toString('base64');
|
||||
const authCookie = parent.encodeCookie({ a: 'checkAuth', c: code, u: user._id, n: user.otpdev });
|
||||
var payload = { notification: { title: "MeshCentral", body: user.name + " authentication" }, data: { url: '2fa://auth?code=' + code + '&c=' + authCookie } };
|
||||
const logincodeb64 = Buffer.from(obj.common.zeroPad(getRandomSixDigitInteger(), 6)).toString('base64');
|
||||
const sessioncode = obj.crypto.randomBytes(24).toString('base64');
|
||||
|
||||
// Create a browser cookie so the browser can connect using websocket and wait for device accept/reject.
|
||||
const browserCookie = parent.encodeCookie({ a: 'waitAuth', c: logincodeb64, u: user._id, n: user.otpdev, s: sessioncode, d: domain.id });
|
||||
|
||||
// Get the HTTPS port
|
||||
var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port if specified
|
||||
if (obj.args.agentport != null) { httpsPort = obj.args.agentport; } // If an agent only port is enabled, use that.
|
||||
if (obj.args.agentaliasport != null) { httpsPort = obj.args.agentaliasport; } // If an agent alias port is specified, use that.
|
||||
|
||||
// Get the agent connection server name
|
||||
var serverName = obj.getWebServerName(domain);
|
||||
if (typeof obj.args.agentaliasdns == 'string') { serverName = obj.args.agentaliasdns; }
|
||||
|
||||
// Build the connection URL. If we are using a sub-domain or one with a DNS, we need to craft the URL correctly.
|
||||
var xdomain = (domain.dns == null) ? domain.id : '';
|
||||
if (xdomain != '') xdomain += '/';
|
||||
var url = 'wss://' + serverName + ':' + httpsPort + '/' + xdomain + '2fahold.ashx?c=' + browserCookie;
|
||||
|
||||
// Request that the login page wait for device auth
|
||||
req.session.messageid = 5; // "Sending notification..." message
|
||||
req.session.passhint = url;
|
||||
req.session.loginmode = '8';
|
||||
if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
|
||||
|
||||
/*
|
||||
// Perform push notification to device
|
||||
const deviceCookie = parent.encodeCookie({ a: 'checkAuth', c: logincodeb64, u: user._id, n: user.otpdev, s: sessioncode });
|
||||
var payload = { notification: { title: "MeshCentral", body: "Authentication - " + logincode }, data: { url: '2fa://auth?code=' + logincodeb64 + '&c=' + deviceCookie } };
|
||||
var options = { priority: 'High', timeToLive: 60 }; // TTL: 1 minute
|
||||
parent.firebase.sendToDevice(user.otpdev, payload, options, function (id, err, errdesc) {
|
||||
if (err == null) {
|
||||
// Create a browser cookie so the browser can connect using websocket and wait for device accept/reject.
|
||||
const browserCookie = parent.encodeCookie({ a: 'waitAuth', c: logincodeb64, u: user._id, n: user.otpdev, s: sessioncode, d: domain.id });
|
||||
|
||||
// Get the HTTPS port
|
||||
var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port if specified
|
||||
if (obj.args.agentport != null) { httpsPort = obj.args.agentport; } // If an agent only port is enabled, use that.
|
||||
if (obj.args.agentaliasport != null) { httpsPort = obj.args.agentaliasport; } // If an agent alias port is specified, use that.
|
||||
|
||||
// Get the agent connection server name
|
||||
var serverName = obj.getWebServerName(domain);
|
||||
if (typeof obj.args.agentaliasdns == 'string') { serverName = obj.args.agentaliasdns; }
|
||||
|
||||
// Build the connection URL. If we are using a sub-domain or one with a DNS, we need to craft the URL correctly.
|
||||
var xdomain = (domain.dns == null) ? domain.id : '';
|
||||
if (xdomain != '') xdomain += '/';
|
||||
var url = 'wss://' + serverName + ':' + httpsPort + '/' + xdomain + '2fahold.ashx?c=' + browserCookie;
|
||||
|
||||
// Request that the login page wait for device auth
|
||||
req.session.messageid = 5; // "Notification sent." message
|
||||
req.session.passhint = logincode;
|
||||
req.session.passhint = logincode + '|' + url;
|
||||
req.session.loginmode = '8';
|
||||
} else {
|
||||
// Indicate the push notification failed
|
||||
|
@ -972,6 +1014,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
}
|
||||
if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
|
||||
});
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1116,6 +1159,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
// Regenerate session when signing in to prevent fixation
|
||||
//req.session.regenerate(function () {
|
||||
// Store the user's primary key in the session store to be retrieved, or in this case the entire user object
|
||||
delete req.session.u2fchallenge;
|
||||
delete req.session.loginmode;
|
||||
delete req.session.tokenuserid;
|
||||
delete req.session.tokenusername;
|
||||
|
@ -1318,6 +1362,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
// Check everything is ok
|
||||
if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap') || (typeof req.body.rpassword1 != 'string') || (typeof req.body.rpassword2 != 'string') || (req.body.rpassword1 != req.body.rpassword2) || (typeof req.body.rpasswordhint != 'string') || (req.session == null) || (typeof req.session.resettokenusername != 'string') || (typeof req.session.resettokenpassword != 'string')) {
|
||||
parent.debug('web', 'handleResetPasswordRequest: checks failed');
|
||||
delete req.session.u2fchallenge;
|
||||
delete req.session.loginmode;
|
||||
delete req.session.tokenuserid;
|
||||
delete req.session.tokenusername;
|
||||
|
@ -1400,6 +1445,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
} else {
|
||||
// Failed, error out.
|
||||
parent.debug('web', 'handleResetPasswordRequest: failed authenticate()');
|
||||
delete req.session.u2fchallenge;
|
||||
delete req.session.loginmode;
|
||||
delete req.session.tokenuserid;
|
||||
delete req.session.tokenusername;
|
||||
|
@ -2757,6 +2803,17 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
}
|
||||
handleLoginRequest(req, res, true); break;
|
||||
}
|
||||
case 'pushlogin': {
|
||||
if (req.body.hwstate) {
|
||||
var cookie = obj.parent.decodeCookie(req.body.hwstate, obj.parent.loginCookieEncryptionKey, 1);
|
||||
if ((cookie != null) && (typeof cookie.u == 'string') && (cookie.d == domain.id) && (cookie.a == 'pushAuth')) {
|
||||
req.session = { userid: cookie.u, domainid: cookie.d } // Push authentication is a success, login the user
|
||||
handleRootRequestEx(req, res, domain);
|
||||
return;
|
||||
}
|
||||
}
|
||||
handleLoginRequest(req, res, true); break;
|
||||
}
|
||||
case 'changepassword': { handlePasswordChangeRequest(req, res, true); break; }
|
||||
case 'deleteaccount': { handleDeleteAccountRequest(req, res, true); break; }
|
||||
case 'createaccount': { handleCreateAccountRequest(req, res, true); break; }
|
||||
|
@ -4233,6 +4290,58 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
ws.on('close', function (req) { });
|
||||
}
|
||||
|
||||
// Handle the 2FA hold web socket
|
||||
// Accept an hold a web socket connection until the 2FA response is received.
|
||||
function handle2faHoldWebSocket(ws, req) {
|
||||
const domain = checkUserIpAddress(ws, req);
|
||||
if (domain == null) { return; }
|
||||
ws._socket.setKeepAlive(true, 240000); // Set TCP keep alive
|
||||
if (typeof req.query.c !== 'string') { ws.close(); return; }
|
||||
const cookie = parent.decodeCookie(req.query.c, null, 1);
|
||||
if ((cookie == null) || (cookie.d != domain.id)) { ws.close(); return; }
|
||||
var user = obj.users[cookie.u];
|
||||
if ((user == null) || (typeof user.otpdev != 'string')) { ws.close(); return; }
|
||||
|
||||
// 2FA event subscription
|
||||
obj.parent.AddEventDispatch(['2fadev-' + cookie.s], ws);
|
||||
ws.cookie = cookie;
|
||||
ws.HandleEvent = function (source, event, ids, id) {
|
||||
obj.parent.RemoveAllEventDispatch(this);
|
||||
if ((event.approved === true) && (event.userid == this.cookie.u)) {
|
||||
// Create a login cookie
|
||||
const loginCookie = obj.parent.encodeCookie({ a: 'pushAuth', u: event.userid, d: event.domain }, obj.parent.loginCookieEncryptionKey);
|
||||
try { ws.send(JSON.stringify({ approved: true, token: loginCookie })); } catch (ex) { }
|
||||
} else {
|
||||
// Reject the login
|
||||
try { ws.send(JSON.stringify({ approved: false })); } catch (ex) { }
|
||||
}
|
||||
}
|
||||
|
||||
// We do not accept any data on this connection.
|
||||
ws.on('message', function (data) { this.close(); });
|
||||
|
||||
// If error, do nothing.
|
||||
ws.on('error', function (err) { });
|
||||
|
||||
// If closed, unsubscribe
|
||||
ws.on('close', function (req) { obj.parent.RemoveAllEventDispatch(this); });
|
||||
|
||||
// Perform push notification to device
|
||||
try {
|
||||
const deviceCookie = parent.encodeCookie({ a: 'checkAuth', c: cookie.c, u: cookie.u, n: cookie.n, s: cookie.s });
|
||||
var code = Buffer.from(cookie.c, 'base64').toString();
|
||||
var payload = { notification: { title: (domain.title ? domain.title : 'MeshCentral'), body: "Authentication - " + code }, data: { url: '2fa://auth?code=' + cookie.c + '&c=' + deviceCookie } };
|
||||
var options = { priority: 'High', timeToLive: 60 }; // TTL: 1 minute
|
||||
parent.firebase.sendToDevice(user.otpdev, payload, options, function (id, err, errdesc) {
|
||||
if (err == null) {
|
||||
try { ws.send(JSON.stringify({ sent: true, code: code })); } catch (ex) { }
|
||||
} else {
|
||||
try { ws.send(JSON.stringify({ sent: false })); } catch (ex) { }
|
||||
}
|
||||
});
|
||||
} catch (ex) { console.log(ex); }
|
||||
}
|
||||
|
||||
// Get the total size of all files in a folder and all sub-folders. (TODO: try to make all async version)
|
||||
function readTotalFileSize(path) {
|
||||
var r = 0, dir;
|
||||
|
@ -5336,6 +5445,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
obj.app.post(url + 'oneclickrecovery.ashx', handleOneClickRecoveryFile);
|
||||
obj.app.get(url + 'userfiles/*', handleDownloadUserFiles);
|
||||
obj.app.ws(url + 'echo.ashx', handleEchoWebSocket);
|
||||
obj.app.ws(url + '2fahold.ashx', handle2faHoldWebSocket);
|
||||
obj.app.ws(url + 'apf.ashx', function (ws, req) { obj.parent.mpsserver.onWebSocketConnection(ws, req); })
|
||||
obj.app.get(url + 'webrelay.ashx', function (req, res) { res.send('Websocket connection expected'); });
|
||||
obj.app.get(url + 'health.ashx', function (req, res) { res.send('ok'); }); // TODO: Perform more server checking.
|
||||
|
|
Loading…
Reference in New Issue