diff --git a/db.js b/db.js
index ab715d57..6f476a0e 100644
--- a/db.js
+++ b/db.js
@@ -175,6 +175,7 @@ module.exports.CreateDB = function (parent) {
obj.getPowerTimeline = function (nodeid, func) { if (obj.databaseType == 1) { obj.file.find({ type: 'power', node: { $in: ['*', nodeid] } }).sort({ time: 1 }).exec(func); } else { obj.file.find({ type: 'power', node: { $in: ['*', nodeid] } }).sort({ time: 1 }, func); } };
obj.getLocalAmtNodes = function (func) { obj.file.find({ type: 'node', host: { $exists: true, $ne: null }, intelamt: { $exists: true } }, func); };
obj.getAmtUuidNode = function (meshid, uuid, func) { obj.file.find({ type: 'node', meshid: meshid, 'intelamt.uuid': uuid }, func); };
+ obj.isMaxType = function (max, type, func) { if (max == null) { func(false); } else { obj.file.count({ type: type }, function (err, count) { func((err != null) || (count > max)); }); } }
// Read a configuration file from the database
obj.getConfigFile = function (path, func) { obj.Get('cfile/' + path, func); }
diff --git a/meshcentral.js b/meshcentral.js
index 0da4828b..d063cef6 100644
--- a/meshcentral.js
+++ b/meshcentral.js
@@ -1520,7 +1520,7 @@ function mainStart(args) {
if (require('os').platform() == 'win32') { for (var i in config.domains) { if (config.domains[i].auth == 'sspi') { sspi = true; } } }
// Build the list of required modules
- var modules = ['ws', 'nedb', 'https', 'yauzl', 'xmldom', 'express', 'archiver', 'multiparty', 'node-forge', 'express-ws', 'compression', 'body-parser', 'connect-redis', 'express-session', 'express-handlebars'];
+ var modules = ['ws', 'nedb', 'https', 'yauzl', 'xmldom', 'express', 'archiver', 'multiparty', 'node-forge', 'express-ws', 'compression', 'body-parser', 'connect-redis', 'express-handlebars'];
if (require('os').platform() == 'win32') { modules.push('node-windows'); if (sspi == true) { modules.push('node-sspi'); } } // Add Windows modules
if (config.letsencrypt != null) { modules.push('greenlock'); modules.push('le-store-certbot'); modules.push('le-challenge-fs'); modules.push('le-acme-core'); } // Add Greenlock Modules
if (config.settings.mongodb != null) { modules.push('mongojs'); } // Add MongoDB
diff --git a/meshuser.js b/meshuser.js
index 10d8fbcc..51f40f98 100644
--- a/meshuser.js
+++ b/meshuser.js
@@ -640,19 +640,35 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if ((command.email != null) && (obj.common.validateEmail(command.email, 1, 256) == false)) break; // Check if this is a valid email address
var newusername = command.username, newuserid = 'user/' + domain.id + '/' + command.username.toLowerCase();
if (newusername == '~') break; // This is a reserved user name
- if (!obj.parent.users[newuserid]) {
- var newuser = { type: 'user', _id: newuserid, name: newusername, creation: Math.floor(Date.now() / 1000), domain: domain.id };
- if (command.email != null) { newuser.email = command.email; } // Email
- obj.parent.users[newuserid] = newuser;
- // Create a user, generate a salt and hash the password
- require('./pass').hash(command.pass, function (err, salt, hash) {
- if (err) throw err;
- newuser.salt = salt;
- newuser.hash = hash;
- obj.db.SetUser(newuser);
- obj.parent.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: newusername, account: obj.parent.CloneSafeUser(newuser), action: 'accountcreate', msg: 'Account created, email is ' + command.email, domain: domain.id });
- });
- }
+ if (obj.parent.users[newuserid]) break; // Account already exists
+
+ // Check if we exceed the maximum number of user accounts
+ obj.db.isMaxType(domain.maxaccounts, 'user', function (maxExceed) {
+ if (maxExceed) {
+ // Account count exceed, do notification
+
+ // Create the notification message
+ var notification = { "action": "msg", "type": "notify", "value": "Account limit reached.", "userid": user._id, "username": user.name };
+
+ // Get the list of sessions for this user
+ var sessions = obj.parent.wssessions[user._id];
+ if (sessions != null) { for (i in sessions) { try { sessions[i].send(JSON.stringify(notification)); } catch (ex) { } } }
+ // TODO: Notify all sessions on other peers.
+ } else {
+ // Check if this is an existing user
+ var newuser = { type: 'user', _id: newuserid, name: newusername, creation: Math.floor(Date.now() / 1000), domain: domain.id };
+ if (command.email != null) { newuser.email = command.email; } // Email
+ obj.parent.users[newuserid] = newuser;
+ // Create a user, generate a salt and hash the password
+ require('./pass').hash(command.pass, function (err, salt, hash) {
+ if (err) throw err;
+ newuser.salt = salt;
+ newuser.hash = hash;
+ obj.db.SetUser(newuser);
+ obj.parent.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: newusername, account: obj.parent.CloneSafeUser(newuser), action: 'accountcreate', msg: 'Account created, email is ' + command.email, domain: domain.id });
+ });
+ }
+ });
break;
}
case 'edituser':
@@ -684,11 +700,28 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if (user.siteadmin != 0xFFFFFFFF) break;
if (obj.common.validateString(command.user, 1, 256) == false) break;
if (obj.common.validateString(command.pass, 1, 256) == false) break;
+ if (obj.common.validateString(command.hint, 0, 256) == false) break;
+ if (typeof command.removeMultiFactor != 'boolean') break;
if (obj.common.checkPasswordRequirements(command.pass, domain.passwordrequirements) == false) break; // Password does not meet requirements
+
var chguserid = 'user/' + domain.id + '/' + command.user.toLowerCase(), chguser = obj.parent.users[chguserid];
if (chguser && chguser.salt) {
// Compute the password hash & save it
- require('./pass').hash(command.pass, chguser.salt, function (err, hash) { if (!err) { chguser.hash = hash; obj.db.SetUser(chguser); } });
+ require('./pass').hash(command.pass, chguser.salt, function (err, hash) {
+ if (!err) {
+ var annonceChange = false;
+ chguser.hash = hash;
+ chguser.passhint = command.hint;
+ if (command.removeMultiFactor == true) {
+ if (chguser.otpsecret) { delete chguser.otpsecret; annonceChange = true; }
+ if (chguser.otphkeys) { delete chguser.otphkeys; annonceChange = true; }
+ if (chguser.otpkeys) { delete chguser.otpkeys; annonceChange = true; }
+ }
+ obj.db.SetUser(chguser);
+
+ if (annonceChange == true) { obj.parent.parent.DispatchEvent(['*', 'server-users', user._id, chguser._id], obj, { etype: 'user', username: user.name, account: obj.parent.CloneSafeUser(chguser), action: 'accountchange', msg: 'Removed 2nd factor auth.', domain: domain.id }); }
+ }
+ });
}
break;
}
@@ -1447,8 +1480,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
obj.parent.db.SetUser(user);
ws.send(JSON.stringify({ action: 'otpauth-setup', success: true })); // Report success
- // Notify change TODO: Should be done on all sessions/servers for this user.
- try { ws.send(JSON.stringify({ action: 'userinfo', userinfo: obj.parent.CloneSafeUser(user) })); } catch (ex) { }
+ // Notify change
+ obj.parent.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: user.name, account: obj.parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added authentication application.', domain: domain.id });
} else {
ws.send(JSON.stringify({ action: 'otpauth-setup', success: false })); // Report fail
}
@@ -1464,10 +1497,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if (user.otpsecret) {
delete user.otpsecret;
obj.parent.db.SetUser(user);
+ ws.send(JSON.stringify({ action: 'otpauth-clear', success: true })); // Report success
// Notify change
- try { ws.send(JSON.stringify({ action: 'userinfo', userinfo: obj.parent.CloneSafeUser(user) })); } catch (ex) { }
- ws.send(JSON.stringify({ action: 'otpauth-clear', success: true })); // Report success
+ obj.parent.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: user.name, account: obj.parent.CloneSafeUser(user), action: 'accountchange', msg: 'Removed authentication application.', domain: domain.id });
} else {
ws.send(JSON.stringify({ action: 'otpauth-clear', success: false })); // Report fail
}
@@ -1501,8 +1534,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
ws.send(JSON.stringify({ action: 'otpauth-getpasswords', passwords: user.otpkeys ? user.otpkeys.keys : null }));
}
- // Notify change TODO: Should be done on all sessions/servers for this user.
- try { ws.send(JSON.stringify({ action: 'userinfo', userinfo: obj.parent.CloneSafeUser(user) })); } catch (ex) { }
+ // Notify change
+ obj.parent.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: user.name, account: obj.parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added security key.', domain: domain.id });
break;
}
case 'otp-hkey-get':
@@ -1532,8 +1565,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
obj.parent.db.SetUser(user);
}
- // Notify change TODO: Should be done on all sessions/servers for this user.
- try { ws.send(JSON.stringify({ action: 'userinfo', userinfo: obj.parent.CloneSafeUser(user) })); } catch (ex) { }
+ // Notify change
+ obj.parent.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: user.name, account: obj.parent.CloneSafeUser(user), action: 'accountchange', msg: 'Removed security key.', domain: domain.id });
break;
}
case 'otp-hkey-yubikey-add':
@@ -1568,7 +1601,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
ws.send(JSON.stringify({ action: 'otp-hkey-yubikey-add', result: true, name: command.name, index: keyIndex }));
// Notify change TODO: Should be done on all sessions/servers for this user.
- try { ws.send(JSON.stringify({ action: 'userinfo', userinfo: obj.parent.CloneSafeUser(user) })); } catch (ex) { }
+ obj.parent.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: user.name, account: obj.parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added security key.', domain: domain.id });
} else {
ws.send(JSON.stringify({ action: 'otp-hkey-yubikey-add', result: false, name: command.name }));
}
@@ -1612,10 +1645,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if (user.otphkeys == null) { user.otphkeys = []; }
user.otphkeys.push({ name: command.name, type: 1, publicKey: registrationStatus.publicKey, keyHandle: registrationStatus.keyHandle, certificate: registrationStatus.certificate, keyIndex: keyIndex });
obj.parent.db.SetUser(user);
-
- // Notify change TODO: Should be done on all sessions/servers for this user.
- try { ws.send(JSON.stringify({ action: 'userinfo', userinfo: obj.parent.CloneSafeUser(user) })); } catch (ex) { }
delete obj.hardwareKeyRegistrationRequest;
+
+ // Notify change
+ obj.parent.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: user.name, account: obj.parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added security key.', domain: domain.id });
}, function (error) {
ws.send(JSON.stringify({ action: 'otp-hkey-setup-response', result: false, error: error, name: command.name, index: keyIndex }));
delete obj.hardwareKeyRegistrationRequest;
diff --git a/package.json b/package.json
index 312c6790..48b33ab3 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "meshcentral",
- "version": "0.2.7-t",
+ "version": "0.2.7-u",
"keywords": [
"Remote Management",
"Intel AMT",
@@ -34,7 +34,6 @@
"cookie-session": "^2.0.0-beta.3",
"express": "^4.16.4",
"express-handlebars": "^3.0.0",
- "express-session": "^1.15.6",
"express-ws": "^4.0.0",
"ipcheck": "^0.1.0",
"meshcentral": "*",
diff --git a/public/images/key12.png b/public/images/key12.png
new file mode 100644
index 00000000..cebca941
Binary files /dev/null and b/public/images/key12.png differ
diff --git a/public/images/padlock12.png b/public/images/padlock12.png
new file mode 100644
index 00000000..670ccbb2
Binary files /dev/null and b/public/images/padlock12.png differ
diff --git a/views/default.handlebars b/views/default.handlebars
index bc6a8ecd..6705a5a1 100644
--- a/views/default.handlebars
+++ b/views/default.handlebars
@@ -6213,8 +6213,12 @@
if ((user.quota != null) && ((user.siteadmin & 8) != 0)) { msg += ", " + (user.quota / 1024) + " k"; }
if (self) { msg += ""; }
var username = EscapeHtml(user.name), emailVerified = '';
- if (serverinfo.emailcheck == true) { emailVerified = ((user.emailVerified != true)?' 🗴':' 🗸'); }
+ if (serverinfo.emailcheck == true) { emailVerified = ((user.emailVerified != true) ? ' 🗴' : ' 🗸'); }
if (user.email != null) { username += ', ' + user.email + '' + emailVerified; }
+
+ if ((user.otpsecret > 0) || (user.otphkeys > 0)) { username += ' '; }
+ if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { username += '
'; }
+
x += '
' + err + '
'; - if (msg != null) res.locals.message = '' + msg + '
'; - if (passhint != null) res.locals.passhint = EscapeHtml(passhint); - next(); - }); - // Fetch all users from the database, keep this in memory obj.db.GetAllType('user', function (err, docs) { var domainUserCount = {}, i = 0; @@ -377,7 +359,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if ((domain.yubikey != null) && (domain.yubikey.id != null) && (domain.yubikey.secret != null) && (user.otphkeys != null) && (user.otphkeys.length > 0) && (typeof (token) == 'string') && (token.length == 44)) { var keyId = token.substring(0, 12); - // Find a matching OPT key + // Find a matching OTP key var match = false; for (var i = 0; i < user.otphkeys.length; i++) { if ((user.otphkeys[i].type === 2) && (user.otphkeys[i].keyid === keyId)) { match = true; } } @@ -441,10 +423,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { checkUserOneTimePassword(req, domain, user, req.body.token, req.body.hwtoken, function (result) { if (result == false) { // 2-step auth is required, but the token is not present or not valid. - if (user.otpsecret != null) { req.session.error = 'Invalid token, try again.'; } + if ((req.body.token != null) || (req.body.hwtoken != null)) { req.session.error = 'Invalid token, try again.'; } req.session.loginmode = '4'; req.session.tokenusername = xusername; req.session.tokenpassword = xpassword; + req.session.tokenRetry = true; res.redirect(domain.url); } else { // Login succesful @@ -457,12 +440,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // Login succesful completeLoginRequest(req, res, domain, user, userid); } else { + //console.log('passhint', passhint); delete req.session.loginmode; if (err == 'locked') { req.session.error = 'Account locked.'; } else { req.session.error = 'Login failed, check username and password.'; } if ((passhint != null) && (passhint.length > 0)) { req.session.passhint = passhint; } else { - if (req.session.passhint) { delete req.session.passhint; } + delete req.session.passhint; } res.redirect(domain.url); } @@ -481,10 +465,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { delete req.session.loginmode; delete req.session.tokenusername; delete req.session.tokenpassword; + delete req.session.success; + delete req.session.error; + delete req.session.passhint; req.session.userid = userid; req.session.domainid = domain.id; req.session.currentNode = ''; - if (req.session.passhint) { delete req.session.passhint; } if (req.body.viewmode) { req.session.viewmode = req.body.viewmode; } if (req.body.host) { // TODO: This is a terrible search!!! FIX THIS. @@ -515,56 +501,67 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if ((domain == null) || (domain.auth == 'sspi')) return; if ((domain.newaccounts === 0) || (domain.newaccounts === false)) { res.sendStatus(401); return; } - 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)) { - req.session.loginmode = 2; - req.session.error = 'Unable to create account.'; - res.redirect(domain.url); - } else { - // Check if this email was already verified - obj.db.GetUserWithVerifiedEmail(domain.id, req.body.email, function (err, docs) { - if (docs.length > 0) { + + // Check if we exceed the maximum number of user accounts + obj.db.isMaxType(domain.maxaccounts, 'user', function (maxExceed) { + if (maxExceed) { + req.session.loginmode = 2; + req.session.error = 'Account limit reached.'; + console.log('max', req.session); + res.redirect(domain.url); + } 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)) { req.session.loginmode = 2; - req.session.error = 'Existing account with this email address.'; + req.session.error = 'Unable to create account.'; res.redirect(domain.url); } else { - // Check if there is domain.newAccountToken, check if supplied token is valid - if ((domain.newaccountspass != null) && (domain.newaccountspass != '') && (req.body.anewaccountpass != domain.newaccountspass)) { - req.session.loginmode = 2; - req.session.error = 'Invalid account creation token.'; - res.redirect(domain.url); - return; - } - // Check if user exists - if (obj.users['user/' + domain.id + '/' + req.body.username.toLowerCase()]) { - req.session.loginmode = 2; - req.session.error = 'Username already exists.'; - } else { - var hint = req.body.apasswordhint; - if (hint.length > 250) hint = hint.substring(0, 250); - var user = { type: 'user', _id: 'user/' + domain.id + '/' + req.body.username.toLowerCase(), name: req.body.username, email: req.body.email, creation: Math.floor(Date.now() / 1000), login: Math.floor(Date.now() / 1000), domain: domain.id, passhint: hint }; - var usercount = 0; - for (var i in obj.users) { if (obj.users[i].domain == domain.id) { usercount++; } } - if (usercount == 0) { user.siteadmin = 0xFFFFFFFF; if (domain.newaccounts === 2) { domain.newaccounts = 0; } } // If this is the first user, give the account site admin. - obj.users[user._id] = user; - req.session.userid = user._id; - req.session.domainid = domain.id; - // Create a user, generate a salt and hash the password - require('./pass').hash(req.body.password1, function (err, salt, hash) { - if (err) throw err; - user.salt = salt; - user.hash = hash; - obj.db.SetUser(user); + // Check if this email was already verified + obj.db.GetUserWithVerifiedEmail(domain.id, req.body.email, function (err, docs) { + if (docs.length > 0) { + req.session.loginmode = 2; + req.session.error = 'Existing account with this email address.'; + res.redirect(domain.url); + } else { + // Check if there is domain.newAccountToken, check if supplied token is valid + if ((domain.newaccountspass != null) && (domain.newaccountspass != '') && (req.body.anewaccountpass != domain.newaccountspass)) { + req.session.loginmode = 2; + req.session.error = 'Invalid account creation token.'; + res.redirect(domain.url); + return; + } + // Check if user exists + if (obj.users['user/' + domain.id + '/' + req.body.username.toLowerCase()]) { + req.session.loginmode = 2; + req.session.error = 'Username already exists.'; + } else { + var hint = req.body.apasswordhint; + if (hint.length > 250) hint = hint.substring(0, 250); + var user = { type: 'user', _id: 'user/' + domain.id + '/' + req.body.username.toLowerCase(), name: req.body.username, email: req.body.email, creation: Math.floor(Date.now() / 1000), login: Math.floor(Date.now() / 1000), domain: domain.id, passhint: hint }; + var usercount = 0; + for (var i in obj.users) { if (obj.users[i].domain == domain.id) { usercount++; } } + if (usercount == 0) { user.siteadmin = 0xFFFFFFFF; if (domain.newaccounts === 2) { domain.newaccounts = 0; } } // If this is the first user, give the account site admin. + obj.users[user._id] = user; + req.session.userid = user._id; + req.session.domainid = domain.id; + // Create a user, generate a salt and hash the password + require('./pass').hash(req.body.password1, function (err, salt, hash) { + if (err) throw err; + user.salt = salt; + user.hash = hash; + obj.db.SetUser(user); - // Send the verification email - if ((obj.parent.mailserver != null) && (domain.auth != 'sspi') && (obj.common.validateEmail(user.email, 1, 256) == true)) { obj.parent.mailserver.sendAccountCheckMail(domain, user.name, user.email); } + // Send the verification email + if ((obj.parent.mailserver != null) && (domain.auth != 'sspi') && (obj.common.validateEmail(user.email, 1, 256) == true)) { obj.parent.mailserver.sendAccountCheckMail(domain, user.name, user.email); } - }); - obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, account: obj.CloneSafeUser(user), action: 'accountcreate', msg: 'Account created, email is ' + req.body.email, domain: domain.id }); - } - res.redirect(domain.url); + }); + obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, account: obj.CloneSafeUser(user), action: 'accountcreate', msg: 'Account created, email is ' + req.body.email, domain: domain.id }); + } + res.redirect(domain.url); + } + }); } - }); - } + } + }); } // Called to process an account reset request @@ -853,6 +850,19 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (req.session && req.session.userid && obj.users[req.session.userid]) { var user = obj.users[req.session.userid]; if (req.session.domainid != domain.id) { req.session = null; res.redirect(domain.url); return; } // Check is the session is for the correct domain + + // Check if this is a locked account + if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { + // Locked account + delete req.session.userid; + delete req.session.domainid; + delete req.session.currentNode; + delete req.session.passhint; + req.session.error = 'Account locked.'; + res.redirect(domain.url); + return; + } + var viewmode = 1; if (req.session.viewmode) { viewmode = req.session.viewmode; @@ -928,17 +938,32 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { var 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 + var err = null, msg = null, passhint = null; + if (req.session != null) { + err = req.session.error; + msg = req.session.success; + passhint = req.session.passhint; + delete req.session.error; + delete req.session.success; + delete req.session.passhint; + } + var message = ''; + if (err != null) message = '' + err + '
'; + if (msg != null) message = '' + msg + '
'; + if (passhint != null) passhint = EscapeHtml(passhint); + if (obj.args.minify && !req.query.nominify) { // Try to server the minified version if we can. try { - res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile-min' : 'login-min'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: obj.parent.mailserver != null, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: hardwareKeyChallenge }); + res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile-min' : 'login-min'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: obj.parent.mailserver != null, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: hardwareKeyChallenge, message: message, passhint: passhint }); } catch (ex) { // In case of an exception, serve the non-minified version. - res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile' : 'login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: obj.parent.mailserver != null, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: hardwareKeyChallenge }); + res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile' : 'login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: obj.parent.mailserver != null, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: hardwareKeyChallenge, message: message, passhint: passhint }); } } else { // Serve non-minified version of web pages. - res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile' : 'login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: obj.parent.mailserver != null, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: hardwareKeyChallenge }); + res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile' : 'login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: obj.parent.mailserver != null, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: hardwareKeyChallenge, message: message, passhint: passhint }); } /*