mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-12-24 06:05:53 -05:00
Email based 2FA almost completed.
This commit is contained in:
parent
cee4e44c18
commit
59f1463a3f
16
meshmail.js
16
meshmail.js
@ -43,11 +43,13 @@ module.exports.CreateMeshMail = function (parent) {
|
||||
// Set default mail templates
|
||||
// You can override these by placing a file with the same name in "meshcentral-data/mail"
|
||||
// If the server hash many domains, just add the domainid to the file like this: 'account-check-customer1.html', 'mesh-invite-customer1.txt'.
|
||||
obj.mailTemplates['account-login.html'] = '<title>[[[SERVERNAME]]] - Account Login</title>\r\n<div style="font-family:Arial,Helvetica,sans-serif"><table style="background-color:#003366;color:lightgray;width:100%" cellpadding=8><tr><td><b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Account Login</b></td></tr></table><p>Your login token is: [[[TOKEN]]]</p><p>This token can only be used once and is valid for 5 minutes.</p></div>';
|
||||
obj.mailTemplates['account-invite.html'] = '<title>[[[SERVERNAME]]] - Account Invitation</title>\r\n<div style="font-family:Arial,Helvetica,sans-serif"><table style="background-color:#003366;color:lightgray;width:100%" cellpadding=8><tr><td><b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Account Invitation</b></td></tr></table><p>An account was created for you on server <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a>, you can access it now with:</p><p> Username: <b>[[[ACCOUNTNAME]]]</b><br /> Password: <b>[[[PASSWORD]]]</b></p>Best regards,<br>[[[USERNAME]]]<br></div>';
|
||||
obj.mailTemplates['account-check.html'] = '<title>[[[SERVERNAME]]] - Email Verification</title>\r\n<div style="font-family:Arial,Helvetica,sans-serif"><table style="background-color:#003366;color:lightgray;width:100%" cellpadding=8><tr><td><b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Verification</b></td></tr></table><p>Hi [[[USERNAME]]], <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> is requesting email verification, click on the following link to complete the process.</p><p style="margin-left:30px"><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]">Click here to verify your e-mail address.</a></p>If you did not initiate this request, please ignore this mail.</div>';
|
||||
obj.mailTemplates['account-reset.html'] = '<title>[[[SERVERNAME]]] - Account Reset</title>\r\n<div style="font-family:Arial,Helvetica,sans-serif"><table style="background-color:#003366;color:lightgray;width:100%" cellpadding=8><tr><td><b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Verification</b></td></tr></table><p>Hi [[[USERNAME]]], <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> is requesting an account password reset, click on the following link to complete the process.</p><p style="margin-left:30px"><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]">Click here to reset your account password.</a></p>If you did not initiate this request, please ignore this mail.</div>';
|
||||
obj.mailTemplates['mesh-invite.html'] = '<title>[[[SERVERNAME]]] - Invitation</title>\r\n<div style="font-family:Arial,Helvetica,sans-serif"><table style="background-color:#003366;color:lightgray;width:100%" cellpadding=8><tr><td><b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Agent Installation</b></td></tr></table>[[[AREA-NAME]]]<p>Hello [[[NAME]]],</p>[[[/AREA-NAME]]]<p>User [[[USERNAME]]] on server <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> is requesting you to install software to start a remote control session.</p>[[[AREA-MSG]]]<p>Message: <b>[[[MSG]]]</b></p>[[[/AREA-MSG]]][[[AREA-WINDOWS]]]<p style="margin-left:30px"><a href="[[[SERVERURL]]]/meshagents?id=3&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Click here to download the MeshAgent for Windows.</a></p>[[[/AREA-WINDOWS]]][[[AREA-OSX]]]<p style="margin-left:30px"><a href="[[[SERVERURL]]]/meshagents?id=16&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Click here to download the MeshAgent for Apple OSX.</a></p>[[[/AREA-OSX]]][[[AREA-LINUX]]]<p>For Linux, cut & paste the following in a terminal to install the agent:<br /><pre style="margin-left:30px">wget -q "[[[SERVERURL]]]/meshagents?script=1" --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] \'[[[MESHIDHEX]]]\'</pre></p>[[[/AREA-LINUX]]][[[AREA-LINK]]]<p>To install the software, <a href="[[[SERVERURL]]][[[LINKURL]]]">click here</a> and follow the instructions.</p>[[[/AREA-LINK]]]<p>If you did not initiate this request, please ignore this mail.</p>Best regards,<br>[[[USERNAME]]]<br></div>';
|
||||
|
||||
obj.mailTemplates['account-login.txt'] = '[[[SERVERNAME]]] - Account Login\r\nYour login token is: [[[TOKEN]]]\r\n\r\nThis token can only be used once and is valid for 5 minutes.';
|
||||
obj.mailTemplates['account-invite.txt'] = '[[[SERVERNAME]]] - Account Invitation\r\nAn account was created for you on server [[[SERVERNAME]]] ([[[SERVERURL]]]/), you can access it now with username \"[[[ACCOUNTNAME]]]\" and password \"[[[PASSWORD]]]\".\r\n\r\nBest regards,\r\n[[[USERNAME]]]';
|
||||
obj.mailTemplates['account-check.txt'] = '[[[SERVERNAME]]] - Email Verification\r\nHi [[[USERNAME]]], [[[SERVERNAME]]] ([[[SERVERURL]]]) is performing an e-mail verification. Nagivate to the following link to complete the process:\r\n\r\n[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]\r\n\r\nIf you did not initiate this request, please ignore this mail.\r\n';
|
||||
obj.mailTemplates['account-reset.txt'] = '[[[SERVERNAME]]] - Account Reset\r\nHi [[[USERNAME]]], [[[SERVERNAME]]] ([[[SERVERURL]]]) is requesting an account password reset. Nagivate to the following link to complete the process:\r\n\r\n[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]\r\n\r\nIf you did not initiate this request, please ignore this mail.';
|
||||
@ -133,6 +135,20 @@ module.exports.CreateMeshMail = function (parent) {
|
||||
sendNextMail();
|
||||
};
|
||||
|
||||
// Send account login mail / 2 factor token
|
||||
obj.sendAccountLoginMail = function (domain, email, token) {
|
||||
var template = getTemplateEx('account-login', domain);
|
||||
if ((template == null) || (template.htmlSubject == null) || (template.txtSubject == null) || (parent.certificates == null) || (parent.certificates.CommonName == null) || (parent.certificates.CommonName.indexOf('.') == -1)) return; // If the server name is not set, invitation not possible.
|
||||
|
||||
// Set all the options.
|
||||
var options = { email: email, servername: domain.title ? domain.title : 'MeshCentral', token: token };
|
||||
|
||||
// Send the email
|
||||
console.log(options);
|
||||
obj.pendingMails.push({ to: email, from: parent.config.smtp.from, subject: mailReplacements(template.htmlSubject, domain, options), text: mailReplacements(template.txt, domain, options), html: mailReplacements(template.html, domain, options) });
|
||||
sendNextMail();
|
||||
};
|
||||
|
||||
// Send account invitation mail
|
||||
obj.sendAccountInviteMail = function (domain, username, accountname, email, password) {
|
||||
var template = getTemplateEx('account-invite', domain);
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "meshcentral",
|
||||
"version": "0.5.0-e",
|
||||
"version": "0.5.0-f",
|
||||
"keywords": [
|
||||
"Remote Management",
|
||||
"Intel AMT",
|
||||
|
@ -507,10 +507,9 @@ module.exports.pluginHandler = function (parent) {
|
||||
obj.removePlugin = function (id, func) {
|
||||
parent.db.getPlugin(id, function (err, docs) {
|
||||
var plugin = docs[0];
|
||||
var rimraf = null;
|
||||
try { rimraf = require('rimraf'); } catch (ex) { }
|
||||
var rimraf = require('rimraf');
|
||||
let pluginPath = obj.parent.path.join(obj.pluginPath, plugin.shortName);
|
||||
if (rimraf) rimraf.sync(pluginPath);
|
||||
rimraf.sync(pluginPath);
|
||||
parent.db.deletePlugin(id, func);
|
||||
delete obj.plugins[plugin.shortName];
|
||||
});
|
||||
|
@ -164,7 +164,10 @@
|
||||
<tr>
|
||||
<td colspan=2>
|
||||
<div style=float:right><input id=tokenOkButton type=submit value="Login" disabled="disabled" /></div>
|
||||
<div style=float:right><input style="display:none;float:right" id=securityKeyButton type=button value="Use Security Key" onclick="useSecurityKey()" /></div>
|
||||
<div style=float:right>
|
||||
<input style="display:none;float:right" id=securityKeyButton type=button value="Use Security Key" onclick="useSecurityKey()" />
|
||||
<input style="display:none;float:right" id=emailKeyButton type=button value="Email" onclick="useEmailToken()" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@ -238,7 +241,7 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div id=dialog style="z-index:1000;background-color:#EEE;box-shadow:0px 0px 15px #666;font-family:Arial,Helvetica,sans-serif;border-radius:5px;position:fixed;top:180px;width:400px;display:none">
|
||||
<div id=dialog style="z-index:1000;background-color:#EEE;box-shadow:0px 0px 15px #666;font-family:Arial,Helvetica,sans-serif;border-radius:5px;position:fixed;top:180px;width:300px;display:none">
|
||||
<div style="width:100%;background-color:#003366;color:#FFF;border-radius:5px 5px 0 0">
|
||||
<div id=id_dialogclose style=float:right;padding:5px;cursor:pointer onclick=setDialogMode()><b>X</b></div>
|
||||
<div id=id_dialogtitle style=padding:5px></div>
|
||||
@ -271,10 +274,11 @@
|
||||
var hardwareKeyChallenge = decodeURIComponent('{{{hkey}}}');
|
||||
var publicKeyCredentialRequestOptions = null;
|
||||
var currentpanel = 0;
|
||||
var otpemail = ('{{{otpemail}}}' === 'true');
|
||||
|
||||
// Display the right server message
|
||||
var messageid = parseInt('{{{messageid}}}');
|
||||
var okmessages = ['', "Hold on, reset mail sent."];
|
||||
var okmessages = ['', "Hold on, reset mail sent.", "Email sent."];
|
||||
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."];
|
||||
if (messageid > 0) {
|
||||
var msg = '';
|
||||
@ -328,6 +332,7 @@
|
||||
if (loginMode == '4') {
|
||||
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
|
||||
QV('securityKeyButton', (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn'));
|
||||
QV('emailKeyButton', otpemail && (messageid != 2)); // TODO
|
||||
}
|
||||
|
||||
if (loginMode == '5') {
|
||||
@ -364,6 +369,7 @@
|
||||
|
||||
// Use a hardware security key
|
||||
function useSecurityKey() {
|
||||
if (xxdialogMode) return;
|
||||
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; }
|
||||
|
||||
@ -393,6 +399,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
function useEmailToken() {
|
||||
if (xxdialogMode) return;
|
||||
if (otpemail != true) return;
|
||||
setDialogMode(1, "Secure Login", 3, useEmailKeyEx, "Send token to registed email address?");
|
||||
}
|
||||
|
||||
function useEmailKeyEx() {
|
||||
Q('hwtokenInput').value = '**email**';
|
||||
QE('tokenOkButton', true);
|
||||
Q('tokenOkButton').click();
|
||||
}
|
||||
|
||||
function showPassHint() {
|
||||
if (passRequirements.hint === true) { messagebox("Password Hint", passhint); }
|
||||
}
|
||||
@ -633,7 +651,7 @@
|
||||
if (((b & 8) || x) && f) f(x, t);
|
||||
}
|
||||
|
||||
function center() { QS('dialog').left = ((((getDocWidth() - 400) / 2)) + 'px'); }
|
||||
function center() { QS('dialog').left = ((((getDocWidth() - 300) / 2)) + 'px'); }
|
||||
function messagebox(t, m) { QH('id_dialogMessage', m); setDialogMode(1, t, 1); }
|
||||
function statusbox(t, m) { QH('id_dialogMessage', m); setDialogMode(1, t); }
|
||||
function getDocWidth() { if (window.innerWidth) return window.innerWidth; if (document.documentElement && document.documentElement.clientWidth && document.documentElement.clientWidth != 0) return document.documentElement.clientWidth; return document.getElementsByTagName('body')[0].clientWidth; }
|
||||
|
@ -160,7 +160,10 @@
|
||||
<tr>
|
||||
<td colspan=2>
|
||||
<div style=float:right><input id=tokenOkButton type=submit value="Login" disabled="disabled" /></div>
|
||||
<div style=float:right><input style="display:none;float:right" id=securityKeyButton type=button value="Use Security Key" onclick="useSecurityKey()" /></div>
|
||||
<div style=float:right>
|
||||
<input style="display:none;float:right" id=securityKeyButton type=button value="Use Security Key" onclick="useSecurityKey()" />
|
||||
<input style="display:none;float:right" id=emailKeyButton type=button value="Email" onclick="useEmailToken()" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@ -270,10 +273,11 @@
|
||||
var webPageFullScreen = true;
|
||||
var nightMode = (getstore('_nightMode', '0') == '1');
|
||||
var publicKeyCredentialRequestOptions = null;
|
||||
var otpemail = ('{{{otpemail}}}' === 'true');
|
||||
|
||||
// Display the right server message
|
||||
var messageid = parseInt('{{{messageid}}}');
|
||||
var okmessages = ['', "Hold on, reset mail sent."];
|
||||
var okmessages = ['', "Hold on, reset mail sent.", "Email sent."];
|
||||
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."];
|
||||
if (messageid > 0) {
|
||||
var msg = '';
|
||||
@ -349,6 +353,7 @@
|
||||
if (loginMode == '4') {
|
||||
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
|
||||
QV('securityKeyButton', (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn'));
|
||||
QV('emailKeyButton', otpemail && (messageid != 2));
|
||||
}
|
||||
|
||||
if (loginMode == '5') {
|
||||
@ -417,6 +422,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
function useEmailToken() {
|
||||
if (otpemail != true) return;
|
||||
setDialogMode(1, "Secure Login", 3, useEmailKeyEx, "Send token to registed email address?");
|
||||
}
|
||||
|
||||
function useEmailKeyEx() {
|
||||
Q('hwtokenInput').value = '**email**';
|
||||
QE('tokenOkButton', true);
|
||||
Q('tokenOkButton').click();
|
||||
}
|
||||
|
||||
function showPassHint(e) {
|
||||
messagebox("Password Hint", passhint);
|
||||
haltEvent(e);
|
||||
|
69
webserver.js
69
webserver.js
@ -541,7 +541,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
}
|
||||
|
||||
// Check if a 2nd factor is present
|
||||
return ((parent.config.settings.no2factorauth !== true) && ((user.otpsecret != null) || ((user.otphkeys != null) && (user.otphkeys.length > 0))));
|
||||
return ((parent.config.settings.no2factorauth !== true) && ((user.otpsecret != null) || (user.otpekey != null) || ((user.otphkeys != null) && (user.otphkeys.length > 0))));
|
||||
}
|
||||
|
||||
// Check the 2-step auth token
|
||||
@ -550,6 +550,22 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
const twoStepLoginSupported = ((domain.auth != 'sspi') && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.nousers !== true) && (parent.config.settings.no2factorauth !== true));
|
||||
if (twoStepLoginSupported == false) { parent.debug('web', 'checkUserOneTimePassword: not supported.'); func(true); return; };
|
||||
|
||||
// Check if we can use OTP tokens with email
|
||||
var otpemail = (parent.mailserver != null);
|
||||
if ((typeof domain.passwordrequirements == 'object') && (typeof domain.passwordrequirements.email2factor == false)) { otpemail = false; }
|
||||
|
||||
// Check email key
|
||||
if ((otpemail) && (user.otpekey != null) && (user.otpekey.d != null) && (user.otpekey.k === token)) {
|
||||
var deltaTime = (Date.now() - user.otpekey.d);
|
||||
if ((deltaTime > 0) && (deltaTime < 300000)) { // Allow 5 minutes to use the email token (10000 * 60 * 5).
|
||||
user.otpekey = {};
|
||||
obj.db.SetUser(user);
|
||||
parent.debug('web', 'checkUserOneTimePassword: success (email).');
|
||||
func(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check hardware key
|
||||
if (user.otphkeys && (user.otphkeys.length > 0) && (typeof (hwtoken) == 'string') && (hwtoken.length > 0)) {
|
||||
var authResponse = null;
|
||||
@ -595,10 +611,10 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
// Update the hardware key counter and accept the 2nd factor
|
||||
webAuthnKey.counter = webauthnResponse.counter;
|
||||
obj.db.SetUser(user);
|
||||
parent.debug('web', 'checkUserOneTimePassword: success.');
|
||||
parent.debug('web', 'checkUserOneTimePassword: success (hardware).');
|
||||
func(true);
|
||||
} else {
|
||||
parent.debug('web', 'checkUserOneTimePassword: fail.');
|
||||
parent.debug('web', 'checkUserOneTimePassword: fail (hardware).');
|
||||
func(false);
|
||||
}
|
||||
return;
|
||||
@ -610,12 +626,21 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
// Check Google Authenticator
|
||||
const otplib = require('otplib')
|
||||
otplib.authenticator.options = { window: 2 }; // Set +/- 1 minute window
|
||||
if (user.otpsecret && (typeof (token) == 'string') && (token.length == 6) && (otplib.authenticator.check(token, user.otpsecret) == true)) { func(true); return; };
|
||||
if (user.otpsecret && (typeof (token) == 'string') && (token.length == 6) && (otplib.authenticator.check(token, user.otpsecret) == true)) {
|
||||
parent.debug('web', 'checkUserOneTimePassword: success (authenticator).');
|
||||
func(true);
|
||||
return;
|
||||
};
|
||||
|
||||
// Check written down keys
|
||||
if ((user.otpkeys != null) && (user.otpkeys.keys != null) && (typeof (token) == 'string') && (token.length == 8)) {
|
||||
var tokenNumber = parseInt(token);
|
||||
for (var i = 0; i < user.otpkeys.keys.length; i++) { if ((tokenNumber === user.otpkeys.keys[i].p) && (user.otpkeys.keys[i].u === true)) { user.otpkeys.keys[i].u = false; func(true); return; } }
|
||||
for (var i = 0; i < user.otpkeys.keys.length; i++) {
|
||||
if ((tokenNumber === user.otpkeys.keys[i].p) && (user.otpkeys.keys[i].u === true)) {
|
||||
parent.debug('web', 'checkUserOneTimePassword: success (one-time).');
|
||||
user.otpkeys.keys[i].u = false; func(true); return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check OTP hardware key
|
||||
@ -631,7 +656,15 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
var yubikeyotp = require('yubikeyotp');
|
||||
var request = { otp: token, id: domain.yubikey.id, key: domain.yubikey.secret, timestamp: true }
|
||||
if (domain.yubikey.proxy) { request.requestParams = { proxy: domain.yubikey.proxy }; }
|
||||
yubikeyotp.verifyOTP(request, function (err, results) { func((results != null) && (results.status == 'OK')); });
|
||||
yubikeyotp.verifyOTP(request, function (err, results) {
|
||||
if ((results != null) && (results.status == 'OK')) {
|
||||
parent.debug('web', 'checkUserOneTimePassword: success (Yubikey).');
|
||||
func(true);
|
||||
} else {
|
||||
parent.debug('web', 'checkUserOneTimePassword: fail (Yubikey).');
|
||||
func(false);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -687,6 +720,17 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
|
||||
// Check if this user has 2-step login active
|
||||
if ((req.session.loginmode != '6') && checkUserOneTimePasswordRequired(domain, user, req)) {
|
||||
if ((req.body.hwtoken == '**email**') && (user.email != null) && (user.emailVerified == true) && (parent.mailserver != null) && (user.otpekey != null)) {
|
||||
user.otpekey = { k: obj.common.zeroPad(getRandomEightDigitInteger(), 8), d: Date.now() };
|
||||
obj.db.SetUser(user);
|
||||
parent.debug('web', 'Sending 2FA email to: ' + user.email);
|
||||
parent.mailserver.sendAccountLoginMail(domain, user.email, user.otpekey.k);
|
||||
req.session.messageid = 2; // "Email sent" message
|
||||
req.session.loginmode = '4';
|
||||
if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
|
||||
return;
|
||||
}
|
||||
|
||||
checkUserOneTimePassword(req, domain, user, req.body.token, req.body.hwtoken, function (result) {
|
||||
if (result == false) {
|
||||
var randomWaitTime = 0;
|
||||
@ -706,6 +750,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
// Wait and redirect the user
|
||||
setTimeout(function () {
|
||||
req.session.loginmode = '4';
|
||||
req.session.tokenemail = ((user.email != null) && (user.emailVerified == true) && (parent.mailserver != null) && (user.otpekey != null));
|
||||
req.session.tokenusername = xusername;
|
||||
req.session.tokenpassword = xpassword;
|
||||
if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
|
||||
@ -793,6 +838,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
//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.loginmode;
|
||||
delete req.session.tokenemail;
|
||||
delete req.session.tokenusername;
|
||||
delete req.session.tokenpassword;
|
||||
delete req.session.tokenemail;
|
||||
@ -1008,6 +1054,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
// Failed, error out.
|
||||
parent.debug('web', 'handleResetPasswordRequest: failed authenticate()');
|
||||
delete req.session.loginmode;
|
||||
delete req.session.tokenemail;
|
||||
delete req.session.tokenusername;
|
||||
delete req.session.tokenpassword;
|
||||
delete req.session.resettokenusername;
|
||||
@ -1672,8 +1719,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
var hwstate = null;
|
||||
if (hardwareKeyChallenge) { hwstate = obj.parent.encodeCookie({ u: req.session.tokenusername, p: req.session.tokenpassword, c: req.session.u2fchallenge }, obj.parent.loginCookieEncryptionKey) }
|
||||
|
||||
// Check if we can use OTP tokens with email
|
||||
var otpemail = (parent.mailserver != null) && (req.session.tokenemail);
|
||||
if ((typeof domain.passwordrequirements == 'object') && (typeof domain.passwordrequirements.email2factor == false)) { otpemail = false; }
|
||||
|
||||
// Render the login page
|
||||
render(req, res, getRenderPage('login', req), getRenderArgs({ loginmode: loginmode, rootCertLink: getRootCertLink(), 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), messageid: msgid, passhint: passhint, welcometext: domain.welcometext ? encodeURIComponent(domain.welcometext).split('\'').join('\\\'') : null, hwstate: hwstate }, domain));
|
||||
render(req, res, getRenderPage('login', req), getRenderArgs({ loginmode: loginmode, rootCertLink: getRootCertLink(), 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), messageid: msgid, passhint: passhint, welcometext: domain.welcometext ? encodeURIComponent(domain.welcometext).split('\'').join('\\\'') : null, hwstate: hwstate, otpemail: otpemail }, domain));
|
||||
}
|
||||
|
||||
// Handle a post request on the root
|
||||
@ -4223,6 +4274,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
delete user2.domain;
|
||||
delete user2.subscriptions;
|
||||
delete user2.passtype;
|
||||
if ((typeof user2.otpkeys == 'object') && (user2.otpkeys != null)) { user2.otpekey = 1; } // Indicates that email 2FA is enabled.
|
||||
if ((typeof user2.otpsecret == 'string') && (user2.otpsecret != null)) { user2.otpsecret = 1; } // Indicates a time secret is present.
|
||||
if ((typeof user2.otpkeys == 'object') && (user2.otpkeys != null)) { user2.otpkeys = 0; if (user.otpkeys != null) { for (var i = 0; i < user.otpkeys.keys.length; i++) { if (user.otpkeys.keys[i].u == true) { user2.otpkeys = 1; } } } } // Indicates the number of one time backup codes that are active.
|
||||
if ((typeof user2.otphkeys == 'object') && (user2.otphkeys != null)) { user2.otphkeys = user2.otphkeys.length; } // Indicates the number of hardware keys setup
|
||||
@ -4510,6 +4562,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
function getRandomPassword() { return Buffer.from(obj.crypto.randomBytes(9), 'binary').toString('base64').split('/').join('@'); }
|
||||
function getRandomLowerCase(len) { var r = '', random = obj.crypto.randomBytes(len); for (var i = 0; i < len; i++) { r += String.fromCharCode(97 + (random[i] % 26)); } return r; }
|
||||
|
||||
// Generate a 8 digit integer with even random probability for each value.
|
||||
function getRandomEightDigitInteger() { var bigInt; do { bigInt = parent.crypto.randomBytes(4).readUInt32BE(0); } while (bigInt >= 4200000000); return bigInt % 100000000; }
|
||||
|
||||
// Clean a IPv6 address that encodes a IPv4 address
|
||||
function cleanRemoteAddr(addr) { if (typeof addr != 'string') { return null; } if (addr.indexOf('::ffff:') == 0) { return addr.substring(7); } else { return addr; } }
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user