Added per-domain SMTP/SendGrid support.

Ylian Saint-Hilaire committed Feb 10, 2021 at 11:28 UTC d96bf4b4f51de0715af64941dd0c9266e6f6d9e4
5 files changed +127 -73
meshcentral-config-schema.json
+26
@@ -506,6 +506,32 @@
506 "required": [ "protocols" ]
507 },
508 "showPasswordLogin": { "type": "boolean", "default": true, "description": "When set to false, hides the username and password prompt on login screen." },
509 + "sendgrid": {
510 + "title" : "SendGrid.com Email server",
511 + "description": "Connects MeshCentral to the SendGrid email server, allows MeshCentral to send email messages for 2FA or user notification.",
512 + "type": "object",
513 + "properties": {
514 + "from": { "type": "string", "format": "email", "description": "Email address used in the messages from field." },
515 + "apikey": { "type": "string", "description": "The SendGrid API key." },
516 + "verifyemail": { "type": "boolean", "default": true, "description": "When set to false, the email format and DNS MX record are not checked." }
517 + },
518 + "required": [ "from", "apikey" ]
519 + },
520 + "smtp": {
521 + "title" : "SMTP email server",
522 + "description": "Connects MeshCentral to a SMTP email server, allows MeshCentral to send email messages for 2FA or user notification.",
523 + "type": "object",
524 + "properties": {
525 + "host": { "type": "string", "format": "hostname" },
526 + "port": { "type": "integer", "minimum": 1, "maximum": 65535 },
527 + "from": { "type": "string", "format": "email", "description": "Email address used in the messages from field." },
528 + "tls": { "type": "boolean" },
529 + "tlscertcheck": { "type": "boolean" },
530 + "tlsstrict": { "type": "boolean" },
531 + "verifyemail": { "type": "boolean", "default": true, "description": "When set to false, the email format and DNS MX record are not checked." }
532 + },
533 + "required": [ "host", "port", "from", "tls" ]
534 + },
535 "authStrategies": {
536 "type": "object",
537 "additionalProperties": false,
meshcentral.js
+25 -3
@@ -1525,7 +1525,7 @@ function CreateMeshCentralServer(config, args) {
1525 obj.swarmserver = require('./swarmserver.js').CreateSwarmServer(obj, obj.db, obj.args, obj.certificates);
1526 }
1527
1528 - // Setup email server
1528 + // Setup the main email server
1529 if (obj.config.sendgrid != null) {
1530 // Sendgrid server
1531 obj.mailserver = require('./meshmail.js').CreateMeshMail(obj);
@@ -1538,6 +1538,24 @@ function CreateMeshCentralServer(config, args) {
1538 if (obj.args.lanonly == true) { addServerWarning("SMTP server has limited use in LAN mode."); }
1539 }
1540
1541 + // Setup the email server for each domain
1542 + for (i in obj.config.domains) {
1543 + if (obj.config.domains[i].sendgrid != null) {
1544 + // Sendgrid server
1545 + obj.config.domains[i].mailserver = require('./meshmail.js').CreateMeshMail(obj, obj.config.domains[i]);
1546 + obj.config.domains[i].mailserver.verify();
1547 + if (obj.args.lanonly == true) { addServerWarning("SendGrid server has limited use in LAN mode."); }
1548 + } else if ((obj.config.domains[i].smtp != null) && (obj.config.domains[i].smtp.host != null) && (obj.config.domains[i].smtp.from != null)) {
1549 + // SMTP server
1550 + obj.config.domains[i].mailserver = require('./meshmail.js').CreateMeshMail(obj, obj.config.domains[i]);
1551 + obj.config.domains[i].mailserver.verify();
1552 + if (obj.args.lanonly == true) { addServerWarning("SMTP server has limited use in LAN mode."); }
1553 + } else {
1554 + // Setup the parent mail server for this domain
1555 + if (obj.mailserver != null) { obj.config.domains[i].mailserver = obj.mailserver; }
1556 + }
1557 + }
1558 +
1559 // Setup SMS gateway
1560 if (config.sms != null) {
1561 obj.smsserver = require('./meshsms.js').CreateMeshSMS(obj);
@@ -2994,10 +3012,14 @@ function mainStart() {
3012 var recordingIndex = false;
3013 var domainCount = 0;
3014 var wildleek = false;
3015 + var nodemailer = false;
3016 + var sendgrid = false;
3017 if (require('os').platform() == 'win32') { for (var i in config.domains) { domainCount++; if (config.domains[i].auth == 'sspi') { sspi = true; } else { allsspi = false; } } } else { allsspi = false; }
3018 if (domainCount == 0) { allsspi = false; }
3019 for (var i in config.domains) {
3020 if (i.startsWith('_')) continue;
3021 + if (config.domains[i].smtp != null) { nodemailer = true; }
3022 + if (config.domains[i].sendgrid != null) { sendgrid = true; }
3023 if (config.domains[i].yubikey != null) { yubikey = true; }
3024 if (config.domains[i].auth == 'ldap') { ldap = true; }
3025 if (config.domains[i].mstsc === true) { mstsc = true; }
@@ -3030,8 +3052,8 @@ function mainStart() {
3052 if (config.settings.plugins != null) { modules.push('semver'); } // Required for version compat testing and update checks
3053 if ((config.settings.plugins != null) && (config.settings.plugins.proxy != null)) { modules.push('https-proxy-agent'); } // Required for HTTP/HTTPS proxy support
3054 else if (config.settings.xmongodb != null) { modules.push('mongojs'); } // Add MongoJS, old driver.
3033 - if (config.smtp != null) { modules.push('nodemailer'); } // Add SMTP support
3034 - if (config.sendgrid != null) { modules.push('@sendgrid/mail'); } // Add SendGrid support
3055 + if (nodemailer || (config.smtp != null)) { modules.push('nodemailer'); } // Add SMTP support
3056 + if (sendgrid || (config.sendgrid != null)) { modules.push('@sendgrid/mail'); } // Add SendGrid support
3057 if (args.translate) { modules.push('jsdom'); modules.push('esprima'); modules.push('minify-js'); modules.push('html-minifier'); } // Translation support
3058
3059 // If running NodeJS < 8, install "util.promisify"
meshmail.js
+35 -29
@@ -17,32 +17,39 @@
17 // TODO: Add NTML support with "nodemailer-ntlm-auth" https://github.com/nodemailer/nodemailer-ntlm-auth
18
19 // Construct a MeshAgent object, called upon connection
20 -module.exports.CreateMeshMail = function (parent) {
20 +module.exports.CreateMeshMail = function (parent, domain) {
21 var obj = {};
22 obj.pendingMails = [];
23 obj.parent = parent;
24 obj.retry = 0;
25 obj.sendingMail = false;
26 obj.mailCookieEncryptionKey = null;
27 + obj.verifyemail = false;
28 + obj.domain = domain;
29 //obj.mailTemplates = {};
30 const constants = (obj.parent.crypto.constants ? obj.parent.crypto.constants : require('constants')); // require('constants') is deprecated in Node 11.10, use require('crypto').constants instead.
31
32 function EscapeHtml(x) { if (typeof x == "string") return x.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;'); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
33 //function EscapeHtmlBreaks(x) { if (typeof x == "string") return x.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;').replace(/\r/g, '<br />').replace(/\n/g, '').replace(/\t/g, '&nbsp;&nbsp;'); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
34
33 - if (parent.config.sendgrid != null) {
35 + // Setup where we read our configuration from
36 + if (obj.domain == null) { obj.config = parent.config; } else { obj.config = domain; }
37 +
38 + if (obj.config.sendgrid != null) {
39 // Setup SendGrid mail server
40 obj.sendGridServer = require('@sendgrid/mail');
36 - obj.sendGridServer.setApiKey(parent.config.sendgrid.apikey);
37 - } else if (parent.config.smtp != null) {
41 + obj.sendGridServer.setApiKey(obj.config.sendgrid.apikey);
42 + if (obj.config.sendgrid.verifyemail == true) { obj.verifyemail = true; }
43 + } else if (obj.config.smtp != null) {
44 // Setup SMTP mail server
45 const nodemailer = require('nodemailer');
40 - var options = { host: parent.config.smtp.host, secure: (parent.config.smtp.tls == true), tls: {} };
41 - //var options = { host: parent.config.smtp.host, secure: (parent.config.smtp.tls == true), tls: { secureProtocol: 'SSLv23_method', ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false } };
42 - if (parent.config.smtp.port != null) { options.port = parent.config.smtp.port; }
43 - if (parent.config.smtp.tlscertcheck === false) { options.tls.rejectUnauthorized = false; }
44 - if (parent.config.smtp.tlsstrict === true) { options.tls.secureProtocol = 'SSLv23_method'; options.tls.ciphers = 'RSA+AES:!aNULL:!MD5:!DSS'; options.tls.secureOptions = constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE; }
45 - if ((parent.config.smtp.user != null) && (parent.config.smtp.pass != null)) { options.auth = { user: parent.config.smtp.user, pass: parent.config.smtp.pass }; }
46 + var options = { host: obj.config.smtp.host, secure: (obj.config.smtp.tls == true), tls: {} };
47 + //var options = { host: obj.config.smtp.host, secure: (obj.config.smtp.tls == true), tls: { secureProtocol: 'SSLv23_method', ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false } };
48 + if (obj.config.smtp.port != null) { options.port = obj.config.smtp.port; }
49 + if (obj.config.smtp.tlscertcheck === false) { options.tls.rejectUnauthorized = false; }
50 + if (obj.config.smtp.tlsstrict === true) { options.tls.secureProtocol = 'SSLv23_method'; options.tls.ciphers = 'RSA+AES:!aNULL:!MD5:!DSS'; options.tls.secureOptions = constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE; }
51 + if ((obj.config.smtp.user != null) && (obj.config.smtp.pass != null)) { options.auth = { user: obj.config.smtp.user, pass: obj.config.smtp.pass }; }
52 + if (obj.config.smtp.verifyemail == true) { obj.verifyemail = true; }
53 obj.smtpServer = nodemailer.createTransport(options);
54 }
55
@@ -149,10 +156,10 @@ module.exports.CreateMeshMail = function (parent) {
156
157 // Send a generic email
158 obj.sendMail = function (to, subject, text, html) {
152 - if (parent.config.sendgrid != null) {
153 - obj.pendingMails.push({ to: to, from: parent.config.sendgrid.from, subject: subject, text: text, html: html });
154 - } else if (parent.config.smtp != null) {
155 - obj.pendingMails.push({ to: to, from: parent.config.smtp.from, subject: subject, text: text, html: html });
159 + if (obj.config.sendgrid != null) {
160 + obj.pendingMails.push({ to: to, from: obj.config.sendgrid.from, subject: subject, text: text, html: html });
161 + } else if (obj.config.smtp != null) {
162 + obj.pendingMails.push({ to: to, from: obj.config.smtp.from, subject: subject, text: text, html: html });
163 }
164 sendNextMail();
165 };
@@ -180,8 +187,8 @@ module.exports.CreateMeshMail = function (parent) {
187
188 // Get from field
189 var from = null;
183 - if (parent.config.sendgrid && (typeof parent.config.sendgrid.from == 'string')) { from = parent.config.sendgrid.from; }
184 - else if (parent.config.smtp && (typeof parent.config.smtp.from == 'string')) { from = parent.config.smtp.from; }
190 + if (obj.config.sendgrid && (typeof obj.config.sendgrid.from == 'string')) { from = obj.config.sendgrid.from; }
191 + else if (obj.config.smtp && (typeof obj.config.smtp.from == 'string')) { from = obj.config.smtp.from; }
192
193 // Send the email
194 obj.pendingMails.push({ to: email, from: from, subject: mailReplacements(template.htmlSubject, domain, options), text: mailReplacements(template.txt, domain, options), html: mailReplacements(template.html, domain, options) });
@@ -213,8 +220,8 @@ module.exports.CreateMeshMail = function (parent) {
220
221 // Get from field
222 var from = null;
216 - if (parent.config.sendgrid && (typeof parent.config.sendgrid.from == 'string')) { from = parent.config.sendgrid.from; }
217 - else if (parent.config.smtp && (typeof parent.config.smtp.from == 'string')) { from = parent.config.smtp.from; }
223 + if (obj.config.sendgrid && (typeof obj.config.sendgrid.from == 'string')) { from = obj.config.sendgrid.from; }
224 + else if (obj.config.smtp && (typeof obj.config.smtp.from == 'string')) { from = obj.config.smtp.from; }
225
226 // Send the email
227 obj.pendingMails.push({ to: email, from: from, subject: mailReplacements(template.htmlSubject, domain, options), text: mailReplacements(template.txt, domain, options), html: mailReplacements(template.html, domain, options) });
@@ -247,8 +254,8 @@ module.exports.CreateMeshMail = function (parent) {
254
255 // Get from field
256 var from = null;
250 - if (parent.config.sendgrid && (typeof parent.config.sendgrid.from == 'string')) { from = parent.config.sendgrid.from; }
251 - else if (parent.config.smtp && (typeof parent.config.smtp.from == 'string')) { from = parent.config.smtp.from; }
257 + if (obj.config.sendgrid && (typeof obj.config.sendgrid.from == 'string')) { from = obj.config.sendgrid.from; }
258 + else if (obj.config.smtp && (typeof obj.config.smtp.from == 'string')) { from = obj.config.smtp.from; }
259
260 // Send the email
261 obj.pendingMails.push({ to: email, from: from, subject: mailReplacements(template.htmlSubject, domain, options), text: mailReplacements(template.txt, domain, options), html: mailReplacements(template.html, domain, options) });
@@ -281,8 +288,8 @@ module.exports.CreateMeshMail = function (parent) {
288
289 // Get from field
290 var from = null;
284 - if (parent.config.sendgrid && (typeof parent.config.sendgrid.from == 'string')) { from = parent.config.sendgrid.from; }
285 - else if (parent.config.smtp && (typeof parent.config.smtp.from == 'string')) { from = parent.config.smtp.from; }
291 + if (obj.config.sendgrid && (typeof obj.config.sendgrid.from == 'string')) { from = obj.config.sendgrid.from; }
292 + else if (obj.config.smtp && (typeof obj.config.smtp.from == 'string')) { from = obj.config.smtp.from; }
293
294 // Send the email
295 obj.pendingMails.push({ to: email, from: from, subject: mailReplacements(template.htmlSubject, domain, options), text: mailReplacements(template.txt, domain, options), html: mailReplacements(template.html, domain, options) });
@@ -319,8 +326,8 @@ module.exports.CreateMeshMail = function (parent) {
326
327 // Get from field
328 var from = null;
322 - if (parent.config.sendgrid && (typeof parent.config.sendgrid.from == 'string')) { from = parent.config.sendgrid.from; }
323 - else if (parent.config.smtp && (typeof parent.config.smtp.from == 'string')) { from = parent.config.smtp.from; }
329 + if (obj.config.sendgrid && (typeof obj.config.sendgrid.from == 'string')) { from = obj.config.sendgrid.from; }
330 + else if (obj.config.smtp && (typeof obj.config.smtp.from == 'string')) { from = obj.config.smtp.from; }
331
332 // Send the email
333 obj.pendingMails.push({ to: email, from: from, subject: mailReplacements(template.htmlSubject, domain, options), text: mailReplacements(template.txt, domain, options), html: mailReplacements(template.html, domain, options) });
@@ -399,13 +406,13 @@ module.exports.CreateMeshMail = function (parent) {
406 if (obj.smtpServer == null) return;
407 obj.smtpServer.verify(function (err, info) {
408 if (err == null) {
402 - console.log('SMTP mail server ' + parent.config.smtp.host + ' working as expected.');
409 + console.log('SMTP mail server ' + obj.config.smtp.host + ' working as expected.');
410 } else {
411 // Remove all non-object types from error to avoid a JSON stringify error.
412 var err2 = {};
413 for (var i in err) { if (typeof (err[i]) != 'object') { err2[i] = err[i]; } }
407 - parent.debug('email', 'SMTP mail server ' + parent.config.smtp.host + ' failed: ' + JSON.stringify(err2));
408 - console.log('SMTP mail server ' + parent.config.smtp.host + ' failed: ' + JSON.stringify(err2));
414 + parent.debug('email', 'SMTP mail server ' + obj.config.smtp.host + ' failed: ' + JSON.stringify(err2));
415 + console.log('SMTP mail server ' + obj.config.smtp.host + ' failed: ' + JSON.stringify(err2));
416 }
417 });
418 };
@@ -430,8 +437,7 @@ module.exports.CreateMeshMail = function (parent) {
437 // Check the email domain DNS MX record.
438 obj.approvedEmailDomains = {};
439 obj.checkEmail = function (email, func) {
433 - if ((parent.config.smtp) && (parent.config.smtp.verifyemail === false)) { func(true); return; }
434 - if ((parent.config.sendgrid) && (parent.config.sendgrid.verifyemail === false)) { func(true); return; }
440 + if (obj.verifyemail == false) { func(true); return; }
441 var emailSplit = email.split('@');
442 if (emailSplit.length != 2) { func(false); return; }
443 if (obj.approvedEmailDomains[emailSplit[1]] === true) { func(true); return; }
meshuser.js
+15 -15
@@ -446,7 +446,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
446 var httpport = ((args.aliasport != null) ? args.aliasport : args.port);
447
448 // Build server information object
449 - var serverinfo = { domain: domain.id, name: domain.dns ? domain.dns : parent.certificates.CommonName, mpsname: parent.certificates.AmtMpsName, mpsport: mpsport, mpspass: args.mpspass, port: httpport, emailcheck: ((parent.parent.mailserver != null) && (domain.auth != 'sspi') && (domain.auth != 'ldap') && (args.lanonly != true) && (parent.certificates.CommonName != null) && (parent.certificates.CommonName.indexOf('.') != -1) && (user._id.split('/')[2].startsWith('~') == false)), domainauth: (domain.auth == 'sspi'), serverTime: Date.now() };
449 + var serverinfo = { domain: domain.id, name: domain.dns ? domain.dns : parent.certificates.CommonName, mpsname: parent.certificates.AmtMpsName, mpsport: mpsport, mpspass: args.mpspass, port: httpport, emailcheck: ((domain.mailserver != null) && (domain.auth != 'sspi') && (domain.auth != 'ldap') && (args.lanonly != true) && (parent.certificates.CommonName != null) && (parent.certificates.CommonName.indexOf('.') != -1) && (user._id.split('/')[2].startsWith('~') == false)), domainauth: (domain.auth == 'sspi'), serverTime: Date.now() };
450 serverinfo.languages = parent.renderLanguages;
451 serverinfo.tlshash = Buffer.from(parent.webCertificateFullHashs[domain.id], 'binary').toString('hex').toUpperCase(); // SHA384 of server HTTPS certificate
452 serverinfo.agentCertHash = parent.agentCertificateHashBase64;
@@ -978,13 +978,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
978 break;
979 }
980 case 'email': {
981 - if (parent.parent.mailserver == null) {
981 + if (domain.mailserver == null) {
982 r = "No email service enabled.";
983 } else {
984 if (cmdargs['_'].length != 3) {
985 r = "Usage: email \"user@sample.com\" \"Subject\" \"Message\".";
986 } else {
987 - parent.parent.mailserver.sendMail(cmdargs['_'][0], cmdargs['_'][1], cmdargs['_'][2]);
987 + domain.mailserver.sendMail(cmdargs['_'][0], cmdargs['_'][1], cmdargs['_'][2]);
988 r = "Done.";
989 }
990 }
@@ -1626,7 +1626,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
1626 if (parent.parent.authlog) { parent.parent.authLog('https', 'User ' + user.name + ' changed email from ' + oldemail + ' to ' + user.email); }
1627
1628 // Send the verification email
1629 - if (parent.parent.mailserver != null) { parent.parent.mailserver.sendAccountCheckMail(domain, user.name, user._id, user.email, parent.getLanguageCodes(req)); }
1629 + if (domain.mailserver != null) { domain.mailserver.sendAccountCheckMail(domain, user.name, user._id, user.email, parent.getLanguageCodes(req)); }
1630 }
1631 });
1632 }
@@ -1644,9 +1644,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
1644 // Always lowercase the email address
1645 command.email = command.email.toLowerCase();
1646
1647 - if ((parent.parent.mailserver != null) && (obj.user.email.toLowerCase() == command.email)) {
1647 + if ((domain.mailserver != null) && (obj.user.email.toLowerCase() == command.email)) {
1648 // Send the verification email
1649 - parent.parent.mailserver.sendAccountCheckMail(domain, user.name, user._id, user.email, parent.getLanguageCodes(req));
1649 + domain.mailserver.sendAccountCheckMail(domain, user.name, user._id, user.email, parent.getLanguageCodes(req));
1650 }
1651 break;
1652 }
@@ -2039,8 +2039,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2039 parent.parent.DispatchEvent(targets, obj, event);
2040
2041 // Perform email invitation
2042 - if ((command.emailInvitation == true) && (command.emailVerified == true) && command.email && parent.parent.mailserver) {
2043 - parent.parent.mailserver.sendAccountInviteMail(newuserdomain, (user.realname ? user.realname : user.name), newusername, command.email.toLowerCase(), command.pass, parent.getLanguageCodes(req));
2042 + if ((command.emailInvitation == true) && (command.emailVerified == true) && command.email && domain.mailserver) {
2043 + domain.mailserver.sendAccountInviteMail(newuserdomain, (user.realname ? user.realname : user.name), newusername, command.email.toLowerCase(), command.pass, parent.getLanguageCodes(req));
2044 }
2045
2046 // Log in the auth log
@@ -2237,7 +2237,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2237 }
2238
2239 // In some situations, we need a verified email address to create a device group.
2240 - if ((err == null) && (parent.parent.mailserver != null) && (ugrpdomain.auth != 'sspi') && (ugrpdomain.auth != 'ldap') && (user.emailVerified !== true) && (user.siteadmin != SITERIGHT_ADMIN)) { err = "Email verification required"; } // User must verify it's email first.
2240 + if ((err == null) && (domain.mailserver != null) && (ugrpdomain.auth != 'sspi') && (ugrpdomain.auth != 'ldap') && (user.emailVerified !== true) && (user.siteadmin != SITERIGHT_ADMIN)) { err = "Email verification required"; } // User must verify it's email first.
2241 } catch (ex) { err = "Validation exception: " + ex; }
2242
2243 // Handle any errors
@@ -2861,7 +2861,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2861 if ((user.siteadmin != SITERIGHT_ADMIN) && ((user.siteadmin & 64) != 0)) { err = 'Permission denied'; }
2862
2863 // In some situations, we need a verified email address to create a device group.
2864 - else if ((parent.parent.mailserver != null) && (domain.auth != 'sspi') && (domain.auth != 'ldap') && (user.emailVerified !== true) && (user.siteadmin != SITERIGHT_ADMIN)) { err = 'Email verification required'; } // User must verify it's email first.
2864 + else if ((domain.mailserver != null) && (domain.auth != 'sspi') && (domain.auth != 'ldap') && (user.emailVerified !== true) && (user.siteadmin != SITERIGHT_ADMIN)) { err = 'Email verification required'; } // User must verify it's email first.
2865
2866 // Create mesh
2867 else if (common.validateString(command.meshname, 1, 128) == false) { err = 'Invalid group name'; } // Meshname is between 1 and 64 characters
@@ -4166,7 +4166,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
4166 }
4167
4168 try {
4169 - if ((parent.parent.mailserver == null) || (args.lanonly == true)) { err = 'Unsupported feature'; } // This operation requires the email server
4169 + if ((domain.mailserver == null) || (args.lanonly == true)) { err = 'Unsupported feature'; } // This operation requires the email server
4170 else if ((parent.parent.certificates.CommonName == null) || (parent.parent.certificates.CommonName.indexOf('.') == -1)) { err = 'Unsupported feature'; } // Server name must be configured
4171 else if (common.validateString(command.meshid, 1, 1024) == false) { err = 'Invalid group identifier'; } // Check meshid
4172 else {
@@ -4190,7 +4190,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
4190 }
4191
4192 // Perform email invitation
4193 - parent.parent.mailserver.sendAgentInviteMail(domain, (user.realname ? user.realname : user.name), command.email.toLowerCase(), command.meshid, command.name, command.os, command.msg, command.flags, command.expire, parent.getLanguageCodes(req), req.query.key);
4193 + domain.mailserver.sendAgentInviteMail(domain, (user.realname ? user.realname : user.name), command.email.toLowerCase(), command.meshid, command.name, command.os, command.msg, command.flags, command.expire, parent.getLanguageCodes(req), req.query.key);
4194
4195 // Send a response if needed
4196 if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'inviteAgent', responseid: command.responseid, result: 'ok' })); } catch (ex) { } }
@@ -4632,7 +4632,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
4632 }
4633 case 'emailuser': { // Send a email message to a user
4634 var errMsg = null, emailuser = null;
4635 - if (parent.parent.mailserver == null) { errMsg = 'Email server not enabled'; }
4635 + if (domain.mailserver == null) { errMsg = 'Email server not enabled'; }
4636 else if ((user.siteadmin & 2) == 0) { errMsg = 'No user management rights'; }
4637 else if (common.validateString(command.userid, 1, 2048) == false) { errMsg = 'Invalid userid'; }
4638 else if (common.validateString(command.subject, 1, 1000) == false) { errMsg = 'Invalid subject message'; }
@@ -4645,7 +4645,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
4645 }
4646
4647 if (errMsg != null) { displayNotificationMessage(errMsg); break; }
4648 - parent.parent.mailserver.sendMail(emailuser.email, command.subject, command.msg);
4648 + domain.mailserver.sendMail(emailuser.email, command.subject, command.msg);
4649 displayNotificationMessage("Email sent.", null, null, null, 14);
4650 break;
4651 }
@@ -5526,7 +5526,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
5526
5527 // Return the number of 2nd factor for this account
5528 function count2factoraAuths() {
5529 - var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (parent.parent.mailserver != null));
5529 + var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null));
5530 var sms2fa = ((parent.parent.smsserver != null) && ((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)));
5531 var authFactorCount = 0;
5532 if (typeof user.otpsecret == 'string') { authFactorCount++; } // Authenticator time factor
webserver.js
+26 -26
@@ -706,7 +706,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
706 var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null));
707
708 // Check if a 2nd factor is present
709 - return ((parent.config.settings.no2factorauth !== true) && (sms2fa || (user.otpsecret != null) || ((user.email != null) && (user.emailVerified == true) && (parent.mailserver != null) && (user.otpekey != null)) || ((user.otphkeys != null) && (user.otphkeys.length > 0))));
709 + return ((parent.config.settings.no2factorauth !== true) && (sms2fa || (user.otpsecret != null) || ((user.email != null) && (user.emailVerified == true) && (domain.mailserver != null) && (user.otpekey != null)) || ((user.otphkeys != null) && (user.otphkeys.length > 0))));
710 }
711
712 // Check the 2-step auth token
@@ -716,7 +716,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
716 if (twoStepLoginSupported == false) { parent.debug('web', 'checkUserOneTimePassword: not supported.'); func(true); return; };
717
718 // Check if we can use OTP tokens with email
719 - var otpemail = (parent.mailserver != null);
719 + var otpemail = (domain.mailserver != null);
720 if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.email2factor == false)) { otpemail = false; }
721 var otpsms = (parent.smsserver != null);
722 if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.sms2factor == false)) { otpsms = false; }
@@ -919,7 +919,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
919 return;
920 }
921
922 - var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (parent.mailserver != null) && (user.email != null) && (user.emailVerified == true) && (user.otpekey != null));
922 + var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.email != null) && (user.emailVerified == true) && (user.otpekey != null));
923 var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null));
924
925 // Check if this user has 2-step login active
@@ -928,7 +928,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
928 user.otpekey = { k: obj.common.zeroPad(getRandomEightDigitInteger(), 8), d: Date.now() };
929 obj.db.SetUser(user);
930 parent.debug('web', 'Sending 2FA email to: ' + user.email);
931 - parent.mailserver.sendAccountLoginMail(domain, user.email, user.otpekey.k, obj.getLanguageCodes(req), req.query.key);
931 + domain.mailserver.sendAccountLoginMail(domain, user.email, user.otpekey.k, obj.getLanguageCodes(req), req.query.key);
932 req.session.messageid = 2; // "Email sent" message
933 req.session.loginmode = '4';
934 if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
@@ -967,7 +967,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
967 // Wait and redirect the user
968 setTimeout(function () {
969 req.session.loginmode = '4';
970 - req.session.tokenemail = ((user.email != null) && (user.emailVerified == true) && (parent.mailserver != null) && (user.otpekey != null));
970 + req.session.tokenemail = ((user.email != null) && (user.emailVerified == true) && (domain.mailserver != null) && (user.otpekey != null));
971 req.session.tokensms = ((user.phone != null) && (parent.smsserver != null));
972 req.session.tokenuserid = userid;
973 req.session.tokenusername = xusername;
@@ -984,7 +984,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
984 }
985
986 // Check if email address needs to be confirmed
987 - var emailcheck = ((obj.parent.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true) && (domain.auth != 'sspi') && (domain.auth != 'ldap'))
987 + var emailcheck = ((domain.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true) && (domain.auth != 'sspi') && (domain.auth != 'ldap'))
988 if (emailcheck && (user.emailVerified !== true)) {
989 parent.debug('web', 'Redirecting using ' + user.name + ' to email check login page');
990 req.session.messageid = 3; // "Email verification required" message
@@ -1005,7 +1005,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1005 }
1006
1007 // Check if email address needs to be confirmed
1008 - var emailcheck = ((obj.parent.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true) && (domain.auth != 'sspi') && (domain.auth != 'ldap'))
1008 + var emailcheck = ((domain.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true) && (domain.auth != 'sspi') && (domain.auth != 'ldap'))
1009 if (emailcheck && (user.emailVerified !== true)) {
1010 parent.debug('web', 'Redirecting using ' + user.name + ' to email check login page');
1011 req.session.messageid = 3; // "Email verification required" message
@@ -1261,7 +1261,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1261 obj.db.SetUser(user);
1262
1263 // Send the verification email
1264 - if ((obj.parent.mailserver != null) && (domain.auth != 'sspi') && (domain.auth != 'ldap') && (obj.common.validateEmail(user.email, 1, 256) == true)) { obj.parent.mailserver.sendAccountCheckMail(domain, user.name, user._id, user.email, obj.getLanguageCodes(req), req.query.key); }
1264 + if ((domain.mailserver != null) && (domain.auth != 'sspi') && (domain.auth != 'ldap') && (obj.common.validateEmail(user.email, 1, 256) == true)) { domain.mailserver.sendAccountCheckMail(domain, user.name, user._id, user.email, obj.getLanguageCodes(req), req.query.key); }
1265 }, 0);
1266 var event = { etype: 'user', userid: user._id, username: user.name, account: obj.CloneSafeUser(user), action: 'accountcreate', msg: 'Account created, email is ' + req.body.email, domain: domain.id };
1267 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.
@@ -1458,8 +1458,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1458 } else {
1459 // Send email to perform recovery.
1460 delete req.session.tokenemail;
1461 - if (obj.parent.mailserver != null) {
1462 - obj.parent.mailserver.sendAccountResetMail(domain, user.name, user._id, user.email, obj.getLanguageCodes(req), req.query.key);
1461 + if (domain.mailserver != null) {
1462 + domain.mailserver.sendAccountResetMail(domain, user.name, user._id, user.email, obj.getLanguageCodes(req), req.query.key);
1463 if (i == 0) {
1464 parent.debug('web', 'handleResetAccountRequest: Hold on, reset mail sent.');
1465 req.session.loginmode = '1';
@@ -1478,8 +1478,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1478 });
1479 } else {
1480 // No second factor, send email to perform recovery.
1481 - if (obj.parent.mailserver != null) {
1482 - obj.parent.mailserver.sendAccountResetMail(domain, user.name, user._id, user.email, obj.getLanguageCodes(req), req.query.key);
1481 + if (domain.mailserver != null) {
1482 + domain.mailserver.sendAccountResetMail(domain, user.name, user._id, user.email, obj.getLanguageCodes(req), req.query.key);
1483 if (i == 0) {
1484 parent.debug('web', 'handleResetAccountRequest: Hold on, reset mail sent.');
1485 req.session.loginmode = '1';
@@ -1505,7 +1505,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1505 function handleCheckAccountEmailRequest(req, res, direct) {
1506 const domain = checkUserIpAddress(req, res);
1507 if (domain == null) { return; }
1508 - if ((obj.parent.mailserver == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap') || (typeof req.session.cuserid != 'string') || (obj.users[req.session.cuserid] == null) || (!obj.common.validateEmail(req.body.email, 1, 256))) { parent.debug('web', 'handleCheckAccountEmailRequest: failed checks.'); res.sendStatus(404); return; }
1508 + if ((domain.mailserver == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap') || (typeof req.session.cuserid != 'string') || (obj.users[req.session.cuserid] == null) || (!obj.common.validateEmail(req.body.email, 1, 256))) { parent.debug('web', 'handleCheckAccountEmailRequest: failed checks.'); res.sendStatus(404); return; }
1509 if ((domain.loginkey != null) && (domain.loginkey.indexOf(req.query.key) == -1)) { res.sendStatus(404); return; } // Check 3FA URL key
1510
1511 // Always lowercase the email address
@@ -1563,7 +1563,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1563 }
1564
1565 // Send the verification email
1566 - obj.parent.mailserver.sendAccountCheckMail(domain, user.name, user._id, user.email, obj.getLanguageCodes(req), req.query.key);
1566 + domain.mailserver.sendAccountCheckMail(domain, user.name, user._id, user.email, obj.getLanguageCodes(req), req.query.key);
1567
1568 // Send the response
1569 req.session.messageid = 2; // Email sent.
@@ -1579,11 +1579,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1579 function handleCheckMailRequest(req, res) {
1580 const domain = checkUserIpAddress(req, res);
1581 if (domain == null) { return; }
1582 - if ((domain.auth == 'sspi') || (domain.auth == 'ldap') || (obj.parent.mailserver == null)) { parent.debug('web', 'handleCheckMailRequest: failed checks.'); res.sendStatus(404); return; }
1582 + if ((domain.auth == 'sspi') || (domain.auth == 'ldap') || (domain.mailserver == null)) { parent.debug('web', 'handleCheckMailRequest: failed checks.'); res.sendStatus(404); return; }
1583 if ((domain.loginkey != null) && (domain.loginkey.indexOf(req.query.key) == -1)) { res.sendStatus(404); return; } // Check 3FA URL key
1584
1585 if (req.query.c != null) {
1586 - var cookie = obj.parent.decodeCookie(req.query.c, obj.parent.mailserver.mailCookieEncryptionKey, 30);
1586 + var cookie = obj.parent.decodeCookie(req.query.c, domain.mailserver.mailCookieEncryptionKey, 30);
1587 if ((cookie != null) && (cookie.u != null) && (cookie.u.startsWith('user/')) && (cookie.e != null)) {
1588 var idsplit = cookie.u.split('/');
1589 if ((idsplit.length != 3) || (idsplit[1] != domain.id)) {
@@ -2427,7 +2427,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
2427 if (domain.userQuota == -1) { features += 0x00000008; } // No server files mode
2428 if (obj.args.mpstlsoffload) { features += 0x00000010; } // No mutual-auth CIRA
2429 if ((parent.config.settings.allowframing != null) || (domain.allowframing != null)) { features += 0x00000020; } // Allow site within iframe
2430 - if ((obj.parent.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true)) { features += 0x00000040; } // Email invites
2430 + if ((domain.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true)) { features += 0x00000040; } // Email invites
2431 if (obj.args.webrtc == true) { features += 0x00000080; } // Enable WebRTC (Default false for now)
2432 // 0x00000100 --> This feature flag is free for future use.
2433 if (obj.args.allowhighqualitydesktop !== false) { features += 0x00000200; } // Enable AllowHighQualityDesktop (Default true)
@@ -2453,7 +2453,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
2453 if (domain.amtacmactivation) { features += 0x00100000; } // Intel AMT ACM activation/upgrade is possible
2454 if (domain.usernameisemail) { features += 0x00200000; } // Username is email address
2455 if (parent.mqttbroker != null) { features += 0x00400000; } // This server supports MQTT channels
2456 - if (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (parent.mailserver != null)) { features += 0x00800000; } // using email for 2FA is allowed
2456 + if (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null)) { features += 0x00800000; } // using email for 2FA is allowed
2457 if (domain.agentinvitecodes == true) { features += 0x01000000; } // Support for agent invite codes
2458 if (parent.smsserver != null) { features += 0x02000000; } // SMS messaging is supported
2459 if ((parent.smsserver != null) && ((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false))) { features += 0x04000000; } // SMS 2FA is allowed
@@ -2601,7 +2601,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
2601 delete req.session.messageid;
2602 delete req.session.passhint;
2603 }
2604 - var emailcheck = ((obj.parent.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true) && (domain.auth != 'sspi') && (domain.auth != 'ldap'))
2604 + var emailcheck = ((domain.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true) && (domain.auth != 'sspi') && (domain.auth != 'ldap'))
2605
2606 // Check if we are allowed to create new users using the login screen
2607 var newAccountsAllowed = true;
@@ -2613,7 +2613,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
2613 if (hardwareKeyChallenge) { hwstate = obj.parent.encodeCookie({ u: req.session.tokenusername, p: req.session.tokenpassword, c: req.session.u2fchallenge }, obj.parent.loginCookieEncryptionKey) }
2614
2615 // Check if we can use OTP tokens with email. We can't use email for 2FA password recovery (loginmode 5).
2616 - var otpemail = (loginmode != 5) && (parent.mailserver != null) && (req.session != null) && ((req.session.tokenemail == true) || (typeof req.session.tokenemail == 'string'));
2616 + var otpemail = (loginmode != 5) && (domain.mailserver != null) && (req.session != null) && ((req.session.tokenemail == true) || (typeof req.session.tokenemail == 'string'));
2617 if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.email2factor == false)) { otpemail = false; }
2618 var otpsms = (parent.smsserver != null) && (req.session != null) && (req.session.tokensms == true);
2619 if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.sms2factor == false)) { otpsms = false; }
@@ -5669,7 +5669,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
5669 if (domain == null) { parent.debug('web', 'WSERROR: Got no domain, user auth required.'); return; }
5670 }
5671
5672 - var emailcheck = ((obj.parent.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true) && (domain.auth != 'sspi') && (domain.auth != 'ldap'))
5672 + var emailcheck = ((domain.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true) && (domain.auth != 'sspi') && (domain.auth != 'ldap'))
5673
5674 // A web socket session can be authenticated in many ways (Default user, session, user/pass and cookie). Check authentication here.
5675 if ((req.query.user != null) && (req.query.pass != null)) {
@@ -5685,7 +5685,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
5685 // Check if a 2nd factor is needed
5686 if (checkUserOneTimePasswordRequired(domain, user, req) == true) {
5687 // Figure out if email 2FA is allowed
5688 - var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (parent.mailserver != null) && (user.otpekey != null));
5688 + var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.otpekey != null));
5689 var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null));
5690 if ((typeof req.query.token != 'string') || (req.query.token == '**email**') || (req.query.token == '**sms**')) {
5691 if ((req.query.token == '**email**') && (email2fa == true)) {
@@ -5693,7 +5693,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
5693 user.otpekey = { k: obj.common.zeroPad(getRandomEightDigitInteger(), 8), d: Date.now() };
5694 obj.db.SetUser(user);
5695 parent.debug('web', 'Sending 2FA email to: ' + user.email);
5696 - parent.mailserver.sendAccountLoginMail(domain, user.email, user.otpekey.k, obj.getLanguageCodes(req), req.query.key);
5696 + domain.mailserver.sendAccountLoginMail(domain, user.email, user.otpekey.k, obj.getLanguageCodes(req), req.query.key);
5697 // Ask for a login token & confirm email was sent
5698 try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, email2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
5699 } else if ((req.query.token == '**sms**') && (sms2fa == true)) {
@@ -5731,7 +5731,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
5731 // Check email verification
5732 if (emailcheck && (user.email != null) && (user.emailVerified !== true)) {
5733 parent.debug('web', 'Invalid login, asking for email validation');
5734 - var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (parent.mailserver != null) && (user.otpekey != null));
5734 + var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.otpekey != null));
5735 var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null));
5736 try { ws.send(JSON.stringify({ action: 'close', cause: 'emailvalidation', msg: 'emailvalidationrequired', email2fa: email2fa, sms2fa: sms2fa, email2fasent: true })); ws.close(); } catch (e) { }
5737 } else {
@@ -5788,7 +5788,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
5788 if (typeof domain.twofactorcookiedurationdays == 'number') { twoFactorCookieDays = domain.twofactorcookiedurationdays; }
5789
5790 // Figure out if email 2FA is allowed
5791 - var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (parent.mailserver != null) && (user.otpekey != null));
5791 + var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.otpekey != null));
5792 var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null));
5793 if (s.length != 3) {
5794 try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
@@ -5800,7 +5800,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
5800 user.otpekey = { k: obj.common.zeroPad(getRandomEightDigitInteger(), 8), d: Date.now() };
5801 obj.db.SetUser(user);
5802 parent.debug('web', 'Sending 2FA email to: ' + user.email);
5803 - parent.mailserver.sendAccountLoginMail(domain, user.email, user.otpekey.k, obj.getLanguageCodes(req), req.query.key);
5803 + domain.mailserver.sendAccountLoginMail(domain, user.email, user.otpekey.k, obj.getLanguageCodes(req), req.query.key);
5804 // Ask for a login token & confirm email was sent
5805 try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, email2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
5806 } else if ((s[2] == '**sms**') && (sms2fa == true)) {