Added support for browsers that don't support cookies.

This commit is contained in:
Ylian Saint-Hilaire 2019-08-26 12:20:24 -07:00
parent fbf20e1674
commit 25fc193a8b
10 changed files with 163 additions and 94 deletions

View File

@ -872,7 +872,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
// Always lowercase the email address // Always lowercase the email address
command.email = command.email.toLowerCase(); command.email = command.email.toLowerCase();
if (parent.users[req.session.userid].email != command.email) { if (obj.user.email != command.email) {
// Check if this email is already validated on a different account // Check if this email is already validated on a different account
db.GetUserWithVerifiedEmail(domain.id, command.email, function (err, docs) { db.GetUserWithVerifiedEmail(domain.id, command.email, function (err, docs) {
if ((docs != null) && (docs.length > 0)) { if ((docs != null) && (docs.length > 0)) {
@ -914,7 +914,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
// Always lowercase the email address // Always lowercase the email address
command.email = command.email.toLowerCase(); command.email = command.email.toLowerCase();
if ((parent.parent.mailserver != null) && (parent.users[req.session.userid].email.toLowerCase() == command.email)) { if ((parent.parent.mailserver != null) && (obj.user.email.toLowerCase() == command.email)) {
// Send the verification email // Send the verification email
parent.parent.mailserver.sendAccountCheckMail(domain, user.name, user.email); parent.parent.mailserver.sendAccountCheckMail(domain, user.name, user.email);
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "meshcentral", "name": "meshcentral",
"version": "0.4.0-g", "version": "0.4.0-h",
"keywords": [ "keywords": [
"Remote Management", "Remote Management",
"Intel AMT", "Intel AMT",

File diff suppressed because one or more lines are too long

View File

@ -1188,7 +1188,7 @@
function account_showDeleteAccount() { function account_showDeleteAccount() {
if (xxdialogMode) return; if (xxdialogMode) return;
var x = "<form action='" + domainUrl + "deleteaccount' method=post><table style=margin-left:10px><tr>"; var x = "<form method=post><table style=margin-left:10px><input type=hidden name=action value=deleteaccount /><input type=hidden name=authcookie value=" + authCookie + " /><tr>";
x += "<td align=right>Password:</td><td><input id=apassword1 type=password name=apassword1 autocomplete=off onchange=account_validateDeleteAccount() onkeyup=account_validateDeleteAccount() /></td>"; x += "<td align=right>Password:</td><td><input id=apassword1 type=password name=apassword1 autocomplete=off onchange=account_validateDeleteAccount() onkeyup=account_validateDeleteAccount() /></td>";
x += "</tr><tr><td align=right>Password:</td><td><input id=apassword2 type=password name=apassword2 autocomplete=off onchange=account_validateDeleteAccount() onkeyup=account_validateDeleteAccount() /></td>"; x += "</tr><tr><td align=right>Password:</td><td><input id=apassword2 type=password name=apassword2 autocomplete=off onchange=account_validateDeleteAccount() onkeyup=account_validateDeleteAccount() /></td>";
x += '</tr></table><div style=padding:10px;margin-bottom:4px>'; x += '</tr></table><div style=padding:10px;margin-bottom:4px>';
@ -1200,20 +1200,33 @@
Q('apassword1').focus(); Q('apassword1').focus();
} }
function account_showChangePassword() { function account_showChangePassword() {
if (xxdialogMode) return; if (xxdialogMode) return false;
var x = "<form action='" + domainUrl + "changepassword' method=post><table style=margin-left:10px>"; var x = "<table style=margin-left:10px>";
x += "<tr><td align=right>Old Password:</td><td><input id=apassword0 type=password name=apassword0 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /> <b><span id=dxPassWarn></span></b></td></tr>"; x += "<tr><td align=right>Old&nbsp;password:</td><td><input id=apassword0 type=password name=apassword0 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /> <b></b></td></tr>";
x += "<tr><td align=right>New Password:</td><td><input id=apassword1 type=password name=apassword1 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /> <b><span id=dxPassWarn></span></b></td></tr>"; x += "<tr><td align=right>New&nbsp;password:</td><td><input id=apassword1 type=password name=apassword1 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /> <b><span id=dxPassWarn></span></b></td></tr>";
x += "<tr><td align=right>New Password:</td><td><input id=apassword2 type=password name=apassword2 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /></td></tr>"; x += "<tr><td align=right>New&nbsp;password:</td><td><input id=apassword2 type=password name=apassword2 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /></td></tr>";
if (features & 0x00010000) { x += "<tr><td align=right>Hint:</td><td><input id=apasswordhint name=apasswordhint maxlength=250 type=text autocomplete=off /></td></tr>"; } if (features & 0x00010000) { x += "<tr><td align=right>Password hint:</td><td><input id=apasswordhint name=apasswordhint maxlength=250 type=text autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /></td></tr>"; }
x += '</table><div style=padding:10px;margin-bottom:4px>'; x += '</table>'
x += '<input id=account_dlgCancelButton type=button value=Cancel style=float:right;width:80px;margin-left:5px onclick=dialogclose(0)>'; if (passRequirements) {
x += '<input id=account_dlgOkButton type=submit value=OK style="float:right;width:80px" onclick=dialogclose(1)>'; var r = [], rc = 0;
x += '</div><br /></form>'; for (var i in passRequirements) { if ((i != 'reset') && (i != 'hint')) { r.push(i + ':' + passRequirements[i]); rc++; } }
setDialogMode(2, "Change Password", 0, null, x); if (rc > 0) { x += '<br /><span style=font-size:x-small>Requirements: ' + r.join(', ') + '.</span>'; }
account_validateNewPassword(); }
x += '<br />';
setDialogMode(2, "Change Password", 3, account_showChangePasswordEx, x);
Q('apassword0').focus(); Q('apassword0').focus();
account_validateNewPassword();
return false;
}
function account_showChangePasswordEx() {
if (Q('apassword1').value == Q('apassword2').value) {
var r = { action: 'changepassword', oldpass: Q('apassword0').value, newpass: Q('apassword1').value };
if (features & 0x00010000) { r.hint = Q('apasswordhint').value; }
meshserver.send(r);
}
} }
function account_createMesh() { function account_createMesh() {
@ -1256,15 +1269,16 @@
if (passRequirements == null || passRequirements == '') { if (passRequirements == null || passRequirements == '') {
// No password requirements, display password strength // No password requirements, display password strength
var passStrength = checkPasswordStrength(Q('apassword1').value); var passStrength = checkPasswordStrength(Q('apassword1').value);
if (passStrength >= 80) { r = '<span style=color:green>&#9679;<span>'; } else if (passStrength >= 60) { r = '<span style=color:blue>&#9679;<span>'; } else { r = '<span style=color:red>&#9679;<span>'; } if (passStrength >= 80) { r = '<span style=color:green>Strong<span>'; } else if (passStrength >= 60) { r = '<span style=color:blue>&#9679;<span>'; } else { r = '<span style=color:red>&#9679;<span>'; }
} else { } else {
// Password requirements provided, use that // Password requirements provided, use that
var passReq = checkPasswordRequirements(Q('apassword1').value, passRequirements); var passReq = checkPasswordRequirements(Q('apassword1').value, passRequirements);
if (passReq == false) { ok = false; r = '<span style=color:red>&#9679;<span>' } if (passReq == false) { ok = false; r = '<span style=color:red>Policy<span>' }
} }
} }
QH('dxPassWarn', r); QH('dxPassWarn', r);
QE('account_dlgOkButton', ok); //QE('account_dlgOkButton', ok);
QE('idx_dlgOkButton', ok);
} }
// Return a password strength score // Return a password strength score

View File

@ -6469,7 +6469,7 @@
function account_showDeleteAccount() { function account_showDeleteAccount() {
if (xxdialogMode) return false; if (xxdialogMode) return false;
var x = "To delete this account, type in the account password in both boxes below and hit ok.<br /><br />"; var x = "To delete this account, type in the account password in both boxes below and hit ok.<br /><br />";
x += "<form action='" + domainUrl + "deleteaccount' method=post><table style=margin-left:80px><tr>"; x += "<form method=post><input type=hidden name=action value=deleteaccount /><input type=hidden name=authcookie value=" + authCookie + " /><table style=margin-left:80px><tr>";
x += "<td align=right>Password:</td><td><input id=apassword1 type=password name=apassword1 autocomplete=off onchange=account_validateDeleteAccount() onkeyup=account_validateDeleteAccount() /></td>"; x += "<td align=right>Password:</td><td><input id=apassword1 type=password name=apassword1 autocomplete=off onchange=account_validateDeleteAccount() onkeyup=account_validateDeleteAccount() /></td>";
x += "</tr><tr><td align=right>Password:</td><td><input id=apassword2 type=password name=apassword2 autocomplete=off onchange=account_validateDeleteAccount() onkeyup=account_validateDeleteAccount() /></td>"; x += "</tr><tr><td align=right>Password:</td><td><input id=apassword2 type=password name=apassword2 autocomplete=off onchange=account_validateDeleteAccount() onkeyup=account_validateDeleteAccount() /></td>";
x += '</tr></table><br /><div style=padding:10px;margin-bottom:4px>'; x += '</tr></table><br /><div style=padding:10px;margin-bottom:4px>';
@ -6486,7 +6486,7 @@
if (xxdialogMode) return false; if (xxdialogMode) return false;
var x = "Change your account password by entering the old password and new password twice in the boxes below."; var x = "Change your account password by entering the old password and new password twice in the boxes below.";
if (features & 0x00010000) { " Password hint can be used but is not recommanded."; } if (features & 0x00010000) { " Password hint can be used but is not recommanded."; }
x += "<br /><br />";; x += "<br /><br />";
//x += "<form action='" + domainUrl + "changepassword' method=post>"; //x += "<form action='" + domainUrl + "changepassword' method=post>";
x += "<table style=margin-left:60px>"; x += "<table style=margin-left:60px>";
x += "<tr><td align=right>Old password:</td><td><input id=apassword0 type=password name=apassword0 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /> <b></b></td></tr>"; x += "<tr><td align=right>Old password:</td><td><input id=apassword0 type=password name=apassword0 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /> <b></b></td></tr>";

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -44,7 +44,8 @@
<tr> <tr>
<td align=center> <td align=center>
<div id=loginpanel style="background-color:#979797;border-radius:16px;width:260px;padding:16px;text-align:center;clear:both;display:none"> <div id=loginpanel style="background-color:#979797;border-radius:16px;width:260px;padding:16px;text-align:center;clear:both;display:none">
<form action="login" method=post> <form method=post>
<input type=hidden name=action value=login />
<div id=message1> <div id=message1>
{{{message}}} {{{message}}}
</div> </div>
@ -77,7 +78,8 @@
</div> </div>
<div id=createpanel style="display:none"> <div id=createpanel style="display:none">
<div style="background-color:#979797;border-radius:16px;width:260px;padding:16px;text-align:center;clear:both;position:relative"> <div style="background-color:#979797;border-radius:16px;width:260px;padding:16px;text-align:center;clear:both;position:relative">
<form action=createaccount method=post> <form method=post>
<input type=hidden name=action value=createaccount />
<div id=message2> <div id=message2>
{{{message}}} {{{message}}}
</div> </div>
@ -123,7 +125,8 @@
</div> </div>
</div> </div>
<div id=resetpanel style="background-color:#979797;border-radius:16px;width:260px;padding:16px;text-align:center;display:none;clear:both"> <div id=resetpanel style="background-color:#979797;border-radius:16px;width:260px;padding:16px;text-align:center;display:none;clear:both">
<form action=resetaccount method=post> <form method=post>
<input type=hidden name=action value=resetaccount />
<div id=message3> <div id=message3>
{{{message}}} {{{message}}}
</div> </div>
@ -147,7 +150,9 @@
</form> </form>
</div> </div>
<div id=tokenpanel style="background-color:#979797;border-radius:16px;width:260px;padding:16px;text-align:center;display:none;clear:both"> <div id=tokenpanel style="background-color:#979797;border-radius:16px;width:260px;padding:16px;text-align:center;display:none;clear:both">
<form action=tokenlogin method=post autocomplete=off> <form method=post autocomplete=off>
<input type=hidden name=action value=tokenlogin />
<input type=hidden name=hwstate value="{{{hwstate}}}" />
<div id=message4> <div id=message4>
{{{message}}} {{{message}}}
</div> </div>
@ -171,7 +176,8 @@
</div> </div>
<div id=resettokenpanel style="background-color:#979797;border-radius:16px;width:260px;padding:16px;text-align:center;display:none;clear:both"> <div id=resettokenpanel style="background-color:#979797;border-radius:16px;width:260px;padding:16px;text-align:center;display:none;clear:both">
<form action=resetaccount method=post autocomplete=off> <form method=post autocomplete=off>
<input type=hidden name=action value=resetaccount />
<div id=message5> <div id=message5>
{{{message}}} {{{message}}}
</div> </div>
@ -195,7 +201,8 @@
</div> </div>
<div id=resetpasswordpanel style="position:relative;background-color:#979797;border-radius:16px;width:300px;padding:16px;text-align:center;display:none"> <div id=resetpasswordpanel style="position:relative;background-color:#979797;border-radius:16px;width:300px;padding:16px;text-align:center;display:none">
<form action=resetpassword method=post> <form method=post>
<input type=hidden name=action value=resetpassword />
<div id=message6> <div id=message6>
{{{message}}} {{{message}}}
</div> </div>

View File

@ -41,7 +41,8 @@
</td> </td>
<td id="logincell"> <td id="logincell">
<div id=loginpanel style="display:none"> <div id=loginpanel style="display:none">
<form action="login" method=post> <form method=post>
<input type=hidden name=action value=login />
<div id=message1> <div id=message1>
{{{message}}} {{{message}}}
</div> </div>
@ -73,7 +74,8 @@
</form> </form>
</div> </div>
<div id=createpanel style="display:none;position:relative"> <div id=createpanel style="display:none;position:relative">
<form action=createaccount method=post> <form method=post>
<input type=hidden name=action value=createaccount />
<div id=message2> <div id=message2>
{{{message}}} {{{message}}}
</div> </div>
@ -118,7 +120,8 @@
</form> </form>
</div> </div>
<div id=resetpanel style="display:none"> <div id=resetpanel style="display:none">
<form action=resetaccount method=post> <form method=post>
<input type=hidden name=action value=resetaccount />
<div id=message3> <div id=message3>
{{{message}}} {{{message}}}
</div> </div>
@ -142,7 +145,9 @@
</form> </form>
</div> </div>
<div id=tokenpanel style="display:none"> <div id=tokenpanel style="display:none">
<form action=tokenlogin method=post autocomplete=off> <form method=post autocomplete=off>
<input type=hidden name=action value=tokenlogin />
<input type=hidden name=hwstate value="{{{hwstate}}}" />
<div id=message4> <div id=message4>
{{{message}}} {{{message}}}
</div> </div>
@ -165,7 +170,8 @@
</form> </form>
</div> </div>
<div id=resettokenpanel style="display:none"> <div id=resettokenpanel style="display:none">
<form action=resetaccount method=post> <form method=post>
<input type=hidden name=action value=resetaccount />
<div id=message5> <div id=message5>
{{{message}}} {{{message}}}
</div> </div>
@ -188,7 +194,8 @@
</form> </form>
</div> </div>
<div id=resetpasswordpanel style="display:none;position:relative"> <div id=resetpasswordpanel style="display:none;position:relative">
<form action=resetpassword method=post> <form method=post>
<input type=hidden name=action value=resetpassword />
<div id=message6> <div id=message6>
{{{message}}} {{{message}}}
</div> </div>

View File

@ -628,7 +628,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
func(''); func('');
} }
function handleLoginRequest(req, res) { function handleLoginRequest(req, res, direct) {
const domain = checkUserIpAddress(req, res); const domain = checkUserIpAddress(req, res);
if (domain == null) { parent.debug('web', 'handleLoginRequest: invalid domain'); res.sendStatus(404); return; } if (domain == null) { parent.debug('web', 'handleLoginRequest: invalid domain'); res.sendStatus(404); return; }
@ -661,12 +661,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
req.session.loginmode = '4'; req.session.loginmode = '4';
req.session.tokenusername = xusername; req.session.tokenusername = xusername;
req.session.tokenpassword = xpassword; req.session.tokenpassword = xpassword;
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
}, randomWaitTime); }, randomWaitTime);
} else { } else {
// Login succesful // Login succesful
parent.debug('web', 'handleLoginRequest: succesful 2FA login'); parent.debug('web', 'handleLoginRequest: succesful 2FA login');
completeLoginRequest(req, res, domain, user, userid, xusername, xpassword); completeLoginRequest(req, res, domain, user, userid, xusername, xpassword, direct);
} }
}); });
return; return;
@ -674,7 +674,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Login succesful // Login succesful
parent.debug('web', 'handleLoginRequest: succesful login'); parent.debug('web', 'handleLoginRequest: succesful login');
completeLoginRequest(req, res, domain, user, userid, xusername, xpassword); completeLoginRequest(req, res, domain, user, userid, xusername, xpassword, direct);
} else { } else {
// Login failed, wait a random delay // Login failed, wait a random delay
setTimeout(function () { setTimeout(function () {
@ -694,13 +694,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} else { } else {
delete req.session.passhint; delete req.session.passhint;
} }
res.redirect(domain.url + getQueryPortion(req));
if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
}, 2000 + (obj.crypto.randomBytes(2).readUInt16BE(0) % 4095)); // Wait for 2 to ~6 seconds. }, 2000 + (obj.crypto.randomBytes(2).readUInt16BE(0) % 4095)); // Wait for 2 to ~6 seconds.
} }
}); });
} }
function completeLoginRequest(req, res, domain, user, userid, xusername, xpassword) { function completeLoginRequest(req, res, domain, user, userid, xusername, xpassword, direct) {
// Check if we need to change the password // Check if we need to change the password
if ((typeof user.passchange == 'number') && ((user.passchange == -1) || ((typeof domain.passwordrequirements == 'object') && (typeof domain.passwordrequirements.reset == 'number') && (user.passchange + (domain.passwordrequirements.reset * 86400) < Math.floor(Date.now() / 1000))))) { if ((typeof user.passchange == 'number') && ((user.passchange == -1) || ((typeof domain.passwordrequirements == 'object') && (typeof domain.passwordrequirements.reset == 'number') && (user.passchange + (domain.passwordrequirements.reset * 86400) < Math.floor(Date.now() / 1000))))) {
// Request a password change // Request a password change
@ -709,7 +710,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
req.session.error = '<b style=color:#8C001A>Password change requested.</b>'; req.session.error = '<b style=color:#8C001A>Password change requested.</b>';
req.session.resettokenusername = xusername; req.session.resettokenusername = xusername;
req.session.resettokenpassword = xpassword; req.session.resettokenpassword = xpassword;
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
return; return;
} }
@ -749,19 +750,19 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} }
console.log("CurrentNode: " + req.session.currentNode); console.log("CurrentNode: " + req.session.currentNode);
// This redirect happens after finding node is completed // This redirect happens after finding node is completed
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
}); });
*/ */
parent.debug('web', 'handleLoginRequest: login ok (1)'); parent.debug('web', 'handleLoginRequest: login ok (1)');
res.redirect(domain.url + getQueryPortion(req)); // Temporary if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); } // Temporary
} else { } else {
parent.debug('web', 'handleLoginRequest: login ok (2)'); parent.debug('web', 'handleLoginRequest: login ok (2)');
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
} }
//}); //});
} }
function handleCreateAccountRequest(req, res) { function handleCreateAccountRequest(req, res, direct) {
const domain = checkUserIpAddress(req, res); const domain = checkUserIpAddress(req, res);
if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) { if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) {
parent.debug('web', 'handleCreateAccountRequest: failed checks.'); parent.debug('web', 'handleCreateAccountRequest: failed checks.');
@ -795,7 +796,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
parent.debug('web', 'handleCreateAccountRequest: unable to create account (1)'); parent.debug('web', 'handleCreateAccountRequest: unable to create account (1)');
req.session.loginmode = '2'; req.session.loginmode = '2';
req.session.error = '<b style=color:#8C001A>Unable to create account.</b>'; req.session.error = '<b style=color:#8C001A>Unable to create account.</b>';
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
return; return;
} }
var emailok = false, emaildomain = req.body.email.substring(i + 1).toLowerCase(); var emailok = false, emaildomain = req.body.email.substring(i + 1).toLowerCase();
@ -804,7 +805,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
parent.debug('web', 'handleCreateAccountRequest: unable to create account (2)'); parent.debug('web', 'handleCreateAccountRequest: unable to create account (2)');
req.session.loginmode = '2'; req.session.loginmode = '2';
req.session.error = '<b style=color:#8C001A>Unable to create account.</b>'; req.session.error = '<b style=color:#8C001A>Unable to create account.</b>';
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
return; return;
} }
} }
@ -815,14 +816,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
parent.debug('web', 'handleCreateAccountRequest: account limit reached'); parent.debug('web', 'handleCreateAccountRequest: account limit reached');
req.session.loginmode = '2'; req.session.loginmode = '2';
req.session.error = '<b style=color:#8C001A>Account limit reached.</b>'; req.session.error = '<b style=color:#8C001A>Account limit reached.</b>';
console.log('max', req.session); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
res.redirect(domain.url + getQueryPortion(req));
} else { } else {
if (!obj.common.validateUsername(req.body.username, 1, 64) || !obj.common.validateEmail(req.body.email, 1, 256) || !obj.common.validateString(req.body.password1, 1, 256) || !obj.common.validateString(req.body.password2, 1, 256) || (req.body.password1 != req.body.password2) || req.body.username == '~' || !obj.common.checkPasswordRequirements(req.body.password1, domain.passwordrequirements)) { if (!obj.common.validateUsername(req.body.username, 1, 64) || !obj.common.validateEmail(req.body.email, 1, 256) || !obj.common.validateString(req.body.password1, 1, 256) || !obj.common.validateString(req.body.password2, 1, 256) || (req.body.password1 != req.body.password2) || req.body.username == '~' || !obj.common.checkPasswordRequirements(req.body.password1, domain.passwordrequirements)) {
parent.debug('web', 'handleCreateAccountRequest: unable to create account (3)'); parent.debug('web', 'handleCreateAccountRequest: unable to create account (3)');
req.session.loginmode = '2'; req.session.loginmode = '2';
req.session.error = '<b style=color:#8C001A>Unable to create account.</b>'; req.session.error = '<b style=color:#8C001A>Unable to create account.</b>';
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
} else { } else {
// Check if this email was already verified // Check if this email was already verified
obj.db.GetUserWithVerifiedEmail(domain.id, req.body.email, function (err, docs) { obj.db.GetUserWithVerifiedEmail(domain.id, req.body.email, function (err, docs) {
@ -830,14 +830,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
parent.debug('web', 'handleCreateAccountRequest: Existing account with this email address'); parent.debug('web', 'handleCreateAccountRequest: Existing account with this email address');
req.session.loginmode = '2'; req.session.loginmode = '2';
req.session.error = '<b style=color:#8C001A>Existing account with this email address.</b>'; req.session.error = '<b style=color:#8C001A>Existing account with this email address.</b>';
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
} else { } else {
// Check if there is domain.newAccountToken, check if supplied token is valid // Check if there is domain.newAccountToken, check if supplied token is valid
if ((domain.newaccountspass != null) && (domain.newaccountspass != '') && (req.body.anewaccountpass != domain.newaccountspass)) { if ((domain.newaccountspass != null) && (domain.newaccountspass != '') && (req.body.anewaccountpass != domain.newaccountspass)) {
parent.debug('web', 'handleCreateAccountRequest: Invalid account creation token'); parent.debug('web', 'handleCreateAccountRequest: Invalid account creation token');
req.session.loginmode = '2'; req.session.loginmode = '2';
req.session.error = '<b style=color:#8C001A>Invalid account creation token.</b>'; req.session.error = '<b style=color:#8C001A>Invalid account creation token.</b>';
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
return; return;
} }
// Check if user exists // Check if user exists
@ -868,7 +868,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (obj.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to create the user. Another event will come. if (obj.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to create the user. Another event will come.
obj.parent.DispatchEvent(['*', 'server-users'], obj, event); obj.parent.DispatchEvent(['*', 'server-users'], obj, event);
} }
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
} }
}); });
} }
@ -877,7 +877,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} }
// Called to process an account password reset // Called to process an account password reset
function handleResetPasswordRequest(req, res) { function handleResetPasswordRequest(req, res, direct) {
const domain = checkUserIpAddress(req, res); const domain = checkUserIpAddress(req, res);
// Check everything is ok // Check everything is ok
@ -892,7 +892,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
delete req.session.success; delete req.session.success;
delete req.session.error; delete req.session.error;
delete req.session.passhint; delete req.session.passhint;
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
return; return;
} }
@ -907,7 +907,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
parent.debug('web', 'handleResetPasswordRequest: password rejected, use a different one (1)'); parent.debug('web', 'handleResetPasswordRequest: password rejected, use a different one (1)');
req.session.loginmode = '6'; req.session.loginmode = '6';
req.session.error = '<b style=color:#8C001A>Password rejected, use a different one.</b>'; req.session.error = '<b style=color:#8C001A>Password rejected, use a different one.</b>';
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
return; return;
} }
@ -918,7 +918,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
parent.debug('web', 'handleResetPasswordRequest: password rejected, use a different one (2)'); parent.debug('web', 'handleResetPasswordRequest: password rejected, use a different one (2)');
req.session.loginmode = '6'; req.session.loginmode = '6';
req.session.error = '<b style=color:#8C001A>Password rejected, use a different one.</b>'; req.session.error = '<b style=color:#8C001A>Password rejected, use a different one.</b>';
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
} else { } else {
// Update the password, use a different salt. // Update the password, use a different salt.
require('./pass').hash(req.body.rpassword1, function (err, salt, hash, tag) { require('./pass').hash(req.body.rpassword1, function (err, salt, hash, tag) {
@ -937,7 +937,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
parent.debug('web', 'handleResetPasswordRequest: success'); parent.debug('web', 'handleResetPasswordRequest: success');
req.session.userid = userid; req.session.userid = userid;
req.session.domainid = domain.id; req.session.domainid = domain.id;
completeLoginRequest(req, res, domain, obj.users[userid], userid, req.session.tokenusername, req.session.tokenpassword); completeLoginRequest(req, res, domain, obj.users[userid], userid, req.session.tokenusername, req.session.tokenpassword, direct);
}, 0); }, 0);
} }
}, 0); }, 0);
@ -953,14 +953,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
delete req.session.success; delete req.session.success;
delete req.session.error; delete req.session.error;
delete req.session.passhint; delete req.session.passhint;
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
return; return;
} }
}); });
} }
// Called to process an account reset request // Called to process an account reset request
function handleResetAccountRequest(req, res) { function handleResetAccountRequest(req, res, direct) {
const domain = checkUserIpAddress(req, res); const domain = checkUserIpAddress(req, res);
if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap') || (obj.args.lanonly == true) || (obj.parent.certificates.CommonName == null) || (obj.parent.certificates.CommonName.indexOf('.') == -1)) { if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap') || (obj.args.lanonly == true) || (obj.parent.certificates.CommonName == null) || (obj.parent.certificates.CommonName.indexOf('.') == -1)) {
parent.debug('web', 'handleResetAccountRequest: check failed'); parent.debug('web', 'handleResetAccountRequest: check failed');
@ -980,14 +980,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
parent.debug('web', 'handleResetAccountRequest: Invalid email'); parent.debug('web', 'handleResetAccountRequest: Invalid email');
req.session.loginmode = '3'; req.session.loginmode = '3';
req.session.error = '<b style=color:#8C001A>Invalid email.</b>'; req.session.error = '<b style=color:#8C001A>Invalid email.</b>';
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
} else { } else {
obj.db.GetUserWithVerifiedEmail(domain.id, email, function (err, docs) { obj.db.GetUserWithVerifiedEmail(domain.id, email, function (err, docs) {
if ((err != null) || (docs.length == 0)) { if ((err != null) || (docs.length == 0)) {
parent.debug('web', 'handleResetAccountRequest: Account not found'); parent.debug('web', 'handleResetAccountRequest: Account not found');
req.session.loginmode = '3'; req.session.loginmode = '3';
req.session.error = '<b style=color:#8C001A>Account not found.</b>'; req.session.error = '<b style=color:#8C001A>Account not found.</b>';
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
} else { } else {
// If many accounts have the same validated e-mail, we are going to use the first one for display, but sent a reset email for all accounts. // If many accounts have the same validated e-mail, we are going to use the first one for display, but sent a reset email for all accounts.
var responseSent = false; var responseSent = false;
@ -1003,7 +1003,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if ((req.body.token != null) || (req.body.hwtoken != null)) { req.session.error = '<b style=color:#8C001A>Invalid token, try again.</b>'; } if ((req.body.token != null) || (req.body.hwtoken != null)) { req.session.error = '<b style=color:#8C001A>Invalid token, try again.</b>'; }
req.session.loginmode = '5'; req.session.loginmode = '5';
req.session.tokenemail = email; req.session.tokenemail = email;
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
} }
} else { } else {
// Send email to perform recovery. // Send email to perform recovery.
@ -1014,14 +1014,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
parent.debug('web', 'handleResetAccountRequest: Hold on, reset mail sent.'); parent.debug('web', 'handleResetAccountRequest: Hold on, reset mail sent.');
req.session.loginmode = '1'; req.session.loginmode = '1';
req.session.error = '<b style=color:darkgreen>Hold on, reset mail sent.</b>'; req.session.error = '<b style=color:darkgreen>Hold on, reset mail sent.</b>';
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
} }
} else { } else {
if (i == 0) { if (i == 0) {
parent.debug('web', 'handleResetAccountRequest: Unable to sent email.'); parent.debug('web', 'handleResetAccountRequest: Unable to sent email.');
req.session.loginmode = '3'; req.session.loginmode = '3';
req.session.error = '<b style=color:#8C001A>Unable to sent email.</b>'; req.session.error = '<b style=color:#8C001A>Unable to sent email.</b>';
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
} }
} }
} }
@ -1034,14 +1034,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
parent.debug('web', 'handleResetAccountRequest: Hold on, reset mail sent.'); parent.debug('web', 'handleResetAccountRequest: Hold on, reset mail sent.');
req.session.loginmode = '1'; req.session.loginmode = '1';
req.session.error = '<b style=color:darkgreen>Hold on, reset mail sent.</b>'; req.session.error = '<b style=color:darkgreen>Hold on, reset mail sent.</b>';
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
} }
} else { } else {
if (i == 0) { if (i == 0) {
parent.debug('web', 'handleResetAccountRequest: Unable to sent email.'); parent.debug('web', 'handleResetAccountRequest: Unable to sent email.');
req.session.loginmode = '3'; req.session.loginmode = '3';
req.session.error = '<b style=color:#8C001A>Unable to sent email.</b>'; req.session.error = '<b style=color:#8C001A>Unable to sent email.</b>';
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
} }
} }
} }
@ -1186,7 +1186,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} }
} }
function handleDeleteAccountRequest(req, res) { function handleDeleteAccountRequest(req, res, direct) {
parent.debug('web', 'handleDeleteAccountRequest()');
const domain = checkUserIpAddress(req, res); const domain = checkUserIpAddress(req, res);
if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) { if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) {
parent.debug('web', 'handleDeleteAccountRequest: failed checks.'); parent.debug('web', 'handleDeleteAccountRequest: failed checks.');
@ -1194,9 +1195,21 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
return; return;
} }
// Check if the user is logged and we have all required parameters var user = null;
if (!req.session || !req.session.userid || !req.body.apassword1 || (req.body.apassword1 != req.body.apassword2) || (req.session.domainid != domain.id)) { res.redirect(domain.url + getQueryPortion(req)); return; } if (req.body.authcookie) {
var user = obj.users[req.session.userid]; // If a authentication cookie is provided, decode it here
var loginCookie = obj.parent.decodeCookie(req.body.authcookie, obj.parent.loginCookieEncryptionKey, 60); // 60 minute timeout
if ((loginCookie != null) && (domain.id == loginCookie.domainid)) { user = obj.users[loginCookie.userid]; }
} else {
// Check if the user is logged and we have all required parameters
if (!req.session || !req.session.userid || !req.body.apassword1 || (req.body.apassword1 != req.body.apassword2) || (req.session.domainid != domain.id)) {
parent.debug('web', 'handleDeleteAccountRequest: required parameters not present.');
if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
return;
} else {
user = obj.users[req.session.userid];
}
}
if (!user) { parent.debug('web', 'handleDeleteAccountRequest: user not found.'); res.sendStatus(404); return; } if (!user) { parent.debug('web', 'handleDeleteAccountRequest: user not found.'); res.sendStatus(404); return; }
// Check if the password is correct // Check if the password is correct
@ -1225,12 +1238,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.db.Remove(user._id); obj.db.Remove(user._id);
delete obj.users[user._id]; delete obj.users[user._id];
req.session = null; req.session = null;
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', userid: user._id, username: user.name, action: 'accountremove', msg: 'Account removed', domain: domain.id }); obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', userid: user._id, username: user.name, action: 'accountremove', msg: 'Account removed', domain: domain.id });
parent.debug('web', 'handleDeleteAccountRequest: removed user.'); parent.debug('web', 'handleDeleteAccountRequest: removed user.');
} else { } else {
parent.debug('web', 'handleDeleteAccountRequest: auth failed.'); parent.debug('web', 'handleDeleteAccountRequest: auth failed.');
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
} }
}); });
} }
@ -1264,7 +1277,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} }
// Handle password changes // Handle password changes
function handlePasswordChangeRequest(req, res) { function handlePasswordChangeRequest(req, res, direct) {
const domain = checkUserIpAddress(req, res); const domain = checkUserIpAddress(req, res);
if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) { if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) {
parent.debug('web', 'handlePasswordChangeRequest: failed checks (1).'); parent.debug('web', 'handlePasswordChangeRequest: failed checks (1).');
@ -1275,14 +1288,16 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Check if the user is logged and we have all required parameters // Check if the user is logged and we have all required parameters
if (!req.session || !req.session.userid || !req.body.apassword0 || !req.body.apassword1 || (req.body.apassword1 != req.body.apassword2) || (req.session.domainid != domain.id)) { if (!req.session || !req.session.userid || !req.body.apassword0 || !req.body.apassword1 || (req.body.apassword1 != req.body.apassword2) || (req.session.domainid != domain.id)) {
parent.debug('web', 'handlePasswordChangeRequest: failed checks (2).'); parent.debug('web', 'handlePasswordChangeRequest: failed checks (2).');
res.redirect(domain.url + getQueryPortion(req)); return; if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
return;
} }
// Get the current user // Get the current user
var user = obj.users[req.session.userid]; var user = obj.users[req.session.userid];
if (!user) { if (!user) {
parent.debug('web', 'handlePasswordChangeRequest: user not found.'); parent.debug('web', 'handlePasswordChangeRequest: user not found.');
res.redirect(domain.url + getQueryPortion(req)); return; if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
return;
} }
// Check old password // Check old password
@ -1298,7 +1313,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
delete user.passtype; delete user.passtype;
obj.db.SetUser(user); obj.db.SetUser(user);
req.session.viewmode = 2; req.session.viewmode = 2;
res.redirect(domain.url + getQueryPortion(req)); if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', userid: user._id, username: user.name, action: 'passchange', msg: 'Account password changed: ' + user.name, domain: domain.id }); obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', userid: user._id, username: user.name, action: 'passchange', msg: 'Account password changed: ' + user.name, domain: domain.id });
}, 0); }, 0);
} }
@ -1306,7 +1321,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} }
// Indicates that any request to "/" should render "default" or "login" depending on login state // Indicates that any request to "/" should render "default" or "login" depending on login state
function handleRootRequest(req, res) { function handleRootRequest(req, res, direct) {
const domain = checkUserIpAddress(req, res); const domain = checkUserIpAddress(req, res);
if (domain == null) { parent.debug('web', 'handleRootRequest: invalid domain.'); res.sendStatus(404); return; } if (domain == null) { parent.debug('web', 'handleRootRequest: invalid domain.'); res.sendStatus(404); return; }
if (!obj.args) { parent.debug('web', 'handleRootRequest: no obj.args.'); res.sendStatus(500); return; } if (!obj.args) { parent.debug('web', 'handleRootRequest: no obj.args.'); res.sendStatus(500); return; }
@ -1319,7 +1334,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
res.end('Authentication Required...'); res.end('Authentication Required...');
} else { } else {
parent.debug('web', 'handleRootRequest: SSPI auth ok.'); parent.debug('web', 'handleRootRequest: SSPI auth ok.');
handleRootRequestEx(req, res, domain); handleRootRequestEx(req, res, domain, direct);
} }
}); });
} else if (req.query.user && req.query.pass) { } else if (req.query.user && req.query.pass) {
@ -1329,22 +1344,22 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
req.session.userid = userid; req.session.userid = userid;
req.session.domainid = domain.id; req.session.domainid = domain.id;
req.session.currentNode = ''; req.session.currentNode = '';
handleRootRequestEx(req, res, domain); handleRootRequestEx(req, res, domain, direct);
}); });
} else { } else {
// Login using a different system // Login using a different system
handleRootRequestEx(req, res, domain); handleRootRequestEx(req, res, domain, direct);
} }
} }
function handleRootRequestEx(req, res, domain) { function handleRootRequestEx(req, res, domain, direct) {
var nologout = false, user = null, features = 0; var nologout = false, user = null, features = 0;
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' }); res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' });
// Check if we have an incomplete domain name in the path // Check if we have an incomplete domain name in the path
if ((domain.id != '') && (domain.dns == null) && (req.url.split('/').length == 2)) { if ((domain.id != '') && (domain.dns == null) && (req.url.split('/').length == 2)) {
parent.debug('web', 'handleRootRequestEx: incomplete domain name in the path.'); parent.debug('web', 'handleRootRequestEx: incomplete domain name in the path.');
res.redirect(domain.url + getQueryPortion(req)); res.redirect(domain.url + getQueryPortion(req)); // BAD***
return; return;
} }
@ -1424,7 +1439,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (req.session.domainid != domain.id) { // Check is the session is for the correct domain if (req.session.domainid != domain.id) { // Check is the session is for the correct domain
parent.debug('web', 'handleRootRequestEx: incorrect domain.'); parent.debug('web', 'handleRootRequestEx: incorrect domain.');
req.session = null; req.session = null;
res.redirect(domain.url + getQueryPortion(req)); res.redirect(domain.url + getQueryPortion(req)); // BAD***
return; return;
} }
@ -1437,7 +1452,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
delete req.session.currentNode; delete req.session.currentNode;
delete req.session.passhint; delete req.session.passhint;
req.session.error = '<b style=color:#8C001A>Account locked.</b>'; req.session.error = '<b style=color:#8C001A>Account locked.</b>';
res.redirect(domain.url + getQueryPortion(req)); res.redirect(domain.url + getQueryPortion(req)); // BAD***
return; return;
} }
@ -1500,7 +1515,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Send back the login application // Send back the login application
// If this is a 2 factor auth request, look for a hardware key challenge. // If this is a 2 factor auth request, look for a hardware key challenge.
// Normal login 2 factor request // Normal login 2 factor request
if ((req.session.loginmode == '4') && (req.session.tokenusername)) { if (req.session && (req.session.loginmode == '4') && (req.session.tokenusername)) {
var user = obj.users['user/' + domain.id + '/' + req.session.tokenusername.toLowerCase()]; var user = obj.users['user/' + domain.id + '/' + req.session.tokenusername.toLowerCase()];
if (user != null) { if (user != null) {
parent.debug('web', 'handleRootRequestEx: sending 2FA challenge.'); parent.debug('web', 'handleRootRequestEx: sending 2FA challenge.');
@ -1509,12 +1524,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} }
} }
// Password recovery 2 factor request // Password recovery 2 factor request
if ((req.session.loginmode == '5') && (req.session.tokenemail)) { if (req.session && (req.session.loginmode == '5') && (req.session.tokenemail)) {
obj.db.GetUserWithVerifiedEmail(domain.id, req.session.tokenemail, function (err, docs) { obj.db.GetUserWithVerifiedEmail(domain.id, req.session.tokenemail, function (err, docs) {
if ((err != null) || (docs.length == 0)) { if ((err != null) || (docs.length == 0)) {
parent.debug('web', 'handleRootRequestEx: password recover 2FA fail.'); parent.debug('web', 'handleRootRequestEx: password recover 2FA fail.');
req.session = null; req.session = null;
res.redirect(domain.url + getQueryPortion(req)); res.redirect(domain.url + getQueryPortion(req)); // BAD***
} else { } else {
var user = obj.users[docs[0]._id]; var user = obj.users[docs[0]._id];
if (user != null) { if (user != null) {
@ -1523,7 +1538,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} else { } else {
parent.debug('web', 'handleRootRequestEx: password recover 2FA no user.'); parent.debug('web', 'handleRootRequestEx: password recover 2FA no user.');
req.session = null; req.session = null;
res.redirect(domain.url + getQueryPortion(req)); res.redirect(domain.url + getQueryPortion(req)); // BAD***
} }
} }
}); });
@ -1539,8 +1554,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if ((parent.config != null) && (parent.config.settings != null) && (parent.config.settings.allowframing == true)) { features += 32; } // Allow site within iframe if ((parent.config != null) && (parent.config.settings != null) && (parent.config.settings.allowframing == true)) { features += 32; } // Allow site within iframe
if (domain.usernameisemail) { features += 0x00200000; } // Username is email address if (domain.usernameisemail) { features += 0x00200000; } // Username is email address
var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified
var loginmode = req.session.loginmode; var loginmode = '';
delete req.session.loginmode; // Clear this state, if the user hits refresh, we want to go back to the login page. if (req.session) { loginmode = req.session.loginmode; delete req.session.loginmode; } // Clear this state, if the user hits refresh, we want to go back to the login page.
// Format an error message if needed // Format an error message if needed
var err = null, msg = null, passhint = null; var err = null, msg = null, passhint = null;
@ -1561,8 +1576,33 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
var newAccountsAllowed = true; var newAccountsAllowed = true;
if ((domain.newaccounts !== 1) && (domain.newaccounts !== true)) { for (var i in obj.users) { if (obj.users[i].domain == domain.id) { newAccountsAllowed = false; break; } } } if ((domain.newaccounts !== 1) && (domain.newaccounts !== true)) { for (var i in obj.users) { if (obj.users[i].domain == domain.id) { newAccountsAllowed = false; break; } } }
// Encrypt the hardware key challenge state if needed
var hwstate = null;
if (hardwareKeyChallenge) { hwstate = obj.parent.encodeCookie({ u: req.session.tokenusername, p: req.session.tokenpassword, c: req.session.u2fchallenge }, obj.parent.loginCookieEncryptionKey) }
// Render the login page // Render the login page
res.render(getRenderPage('login', req), { loginmode: loginmode, rootCertLink: getRootCertLink(), domainurl: domain.url, title: domain.title, title2: domain.title2, newAccount: newAccountsAllowed, 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 }); res.render(getRenderPage('login', req), { loginmode: loginmode, rootCertLink: getRootCertLink(), domainurl: domain.url, title: domain.title, title2: domain.title2, newAccount: newAccountsAllowed, 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, hwstate: hwstate });
}
// Handle a post request on the root
function handleRootPostRequest(req, res) {
parent.debug('web', 'handleRootPostRequest, action: ' + req.body.action);
switch (req.body.action) {
case 'login': { handleLoginRequest(req, res, true); break; }
case 'tokenlogin': {
if (req.body.hwstate) {
var cookie = obj.parent.decodeCookie(req.body.hwstate, obj.parent.loginCookieEncryptionKey, 10);
if (cookie != null) { req.session.tokenusername = cookie.u; req.session.tokenpassword = cookie.p; req.session.u2fchallenge = cookie.c; }
}
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; }
case 'resetpassword': { handleResetPasswordRequest(req, res, true); break; }
case 'resetaccount': { handleResetAccountRequest(req, res, true); break; }
default: { handleLoginRequest(req, res, true); break; }
}
} }
// Return true if it looks like we are using a real TLS certificate. // Return true if it looks like we are using a real TLS certificate.
@ -3131,6 +3171,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (parent.config.domains[i].dns != null) { continue; } // This is a subdomain with a DNS name, no added HTTP bindings needed. if (parent.config.domains[i].dns != null) { continue; } // This is a subdomain with a DNS name, no added HTTP bindings needed.
var url = parent.config.domains[i].url; var url = parent.config.domains[i].url;
obj.app.get(url, handleRootRequest); obj.app.get(url, handleRootRequest);
obj.app.post(url, handleRootPostRequest);
obj.app.get(url + 'backup.zip', handleBackupRequest); obj.app.get(url + 'backup.zip', handleBackupRequest);
obj.app.post(url + 'restoreserver.ashx', handleRestoreRequest); obj.app.post(url + 'restoreserver.ashx', handleRestoreRequest);
obj.app.get(url + 'terms', handleTermsRequest); obj.app.get(url + 'terms', handleTermsRequest);