mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-12-24 06:05:53 -05:00
Session and account improvements.
This commit is contained in:
parent
f1e9d83cc9
commit
a932a66044
1
db.js
1
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); }
|
||||
|
@ -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
|
||||
|
85
meshuser.js
85
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;
|
||||
|
@ -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": "*",
|
||||
|
BIN
public/images/key12.png
Normal file
BIN
public/images/key12.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 371 B |
BIN
public/images/padlock12.png
Normal file
BIN
public/images/padlock12.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 381 B |
@ -6213,8 +6213,12 @@
|
||||
if ((user.quota != null) && ((user.siteadmin & 8) != 0)) { msg += ", " + (user.quota / 1024) + " k"; }
|
||||
if (self) { msg += "</a>"; }
|
||||
var username = EscapeHtml(user.name), emailVerified = '';
|
||||
if (serverinfo.emailcheck == true) { emailVerified = ((user.emailVerified != true)?' <b style=color:red title="Email is not verified">🗴</b>':' <b style=color:green title="Email is verified">🗸</b>'); }
|
||||
if (serverinfo.emailcheck == true) { emailVerified = ((user.emailVerified != true) ? ' <b style=color:red title="Email is not verified">🗴</b>' : ' <b style=color:green title="Email is verified">🗸</b>'); }
|
||||
if (user.email != null) { username += ', <a onclick=doemail(event,\"' + user.email + '\")>' + user.email + '</a>' + emailVerified; }
|
||||
|
||||
if ((user.otpsecret > 0) || (user.otphkeys > 0)) { username += ' <img src="images/key12.png" height=12 width=11 title="2nd factor authentication enabled" style="margin-top:2px" />'; }
|
||||
if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { username += ' <img src="images/padlock12.png" height=12 width=8 title="Account is locked" style="margin-top:2px" />'; }
|
||||
|
||||
x += '<tr onmouseover=userMouseHover(this,1) onmouseout=userMouseHover(this,0)><td style=cursor:pointer onclick=gotoUser(\"' + encodeURIComponent(user._id) + '\")>';
|
||||
x += '<div class=bar style=height:24px;width:100%;font-size:medium>';
|
||||
x += '<div style=float:left;height:24px;width:24px;background-color:white><div class="' + icon + gray + '" style=width:16px;margin-top:4px;margin-left:2px;height:16px></div></div>';
|
||||
@ -6270,8 +6274,8 @@
|
||||
Q('p4name').focus();
|
||||
}
|
||||
|
||||
function showCreateNewAccountDialogValidate() {
|
||||
if ((Q('p4email').value.length > 0) && (validateEmail(Q('p4email').value)) == false) { QE('idx_dlgOkButton', false); return; }
|
||||
function showCreateNewAccountDialogValidate(x) {
|
||||
if ((x == null) && (Q('p4email').value.length > 0) && (validateEmail(Q('p4email').value)) == false) { QE('idx_dlgOkButton', false); return; }
|
||||
QE('idx_dlgOkButton', (!Q('p4name') || ((Q('p4name').value.length > 0) && (Q('p4name').value.indexOf(' ') == -1))) && Q('p4pass1').value.length > 0 && Q('p4pass1').value == Q('p4pass2').value && checkPasswordRequirements(Q('p4pass1').value, passRequirements));
|
||||
}
|
||||
|
||||
@ -6374,6 +6378,15 @@
|
||||
if (user.quota) x += addDeviceAttribute('Server Quota', EscapeHtml(parseInt(user.quota) / 1024) + ' k');
|
||||
x += addDeviceAttribute('Creation', new Date(user.creation * 1000).toLocaleString());
|
||||
if (user.login) x += addDeviceAttribute('Last Login', new Date(user.login * 1000).toLocaleString());
|
||||
var multiFactor = 0;
|
||||
if ((user.otpsecret > 0) || (user.otphkeys > 0) || (user.otpkeys > 0)) {
|
||||
multiFactor = 1;
|
||||
var factors = [];
|
||||
if (user.otpsecret > 0) { factors.push('Authentication App'); }
|
||||
if (user.otphkeys > 0) { factors.push('Security Key'); }
|
||||
if (user.otpkeys > 0) { factors.push('Backup Codes'); }
|
||||
x += addDeviceAttribute('Security', factors.join(', '));
|
||||
}
|
||||
|
||||
x += '</table></div><br />';
|
||||
|
||||
@ -6396,7 +6409,7 @@
|
||||
x = '<div style=float:right;font-size:x-small>';
|
||||
if (deletePossible) x += '<a style=cursor:pointer onclick=p30showDeleteUserDialog() title="Remove this user">Delete User</a>';
|
||||
x += '</div><div style=font-size:x-small>';
|
||||
if (userinfo.siteadmin == 0xFFFFFFFF) x += '<a style=cursor:pointer onclick=p30showUserChangePassDialog() title="Change the password for this user">Change Password</a>';
|
||||
if (userinfo.siteadmin == 0xFFFFFFFF) x += '<a style=cursor:pointer onclick=p30showUserChangePassDialog(' + multiFactor + ') title="Change the password for this user">Change Password</a>';
|
||||
x += '</div><br>'
|
||||
QH('p30html3', x);
|
||||
|
||||
@ -6440,18 +6453,23 @@
|
||||
}
|
||||
|
||||
// Display the user's password change dialog box
|
||||
function p30showUserChangePassDialog() {
|
||||
function p30showUserChangePassDialog(multiFactor) {
|
||||
if (xxdialogMode) return;
|
||||
var x = '';
|
||||
x += addHtmlValue('Password', '<input id=p4pass1 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
|
||||
x += addHtmlValue('Password', '<input id=p4pass2 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
|
||||
setDialogMode(2, "Change Password for " + EscapeHtml(currentUser.name), 3, p30showUserChangePassDialogEx, x);
|
||||
showCreateNewAccountDialogValidate();
|
||||
x += addHtmlValue('Password', '<input id=p4pass1 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate(1) onkeyup=showCreateNewAccountDialogValidate(1)></input>');
|
||||
x += addHtmlValue('Password', '<input id=p4pass2 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate(1) onkeyup=showCreateNewAccountDialogValidate(1)></input>');
|
||||
x += addHtmlValue('Password hint', '<input id=p4hint type=text style=width:230px maxlength=256></input>');
|
||||
if (multiFactor == 1) { x += '<input id=p4twoFactorRemove type=checkbox />Remove all 2nd factor authentication.'; }
|
||||
setDialogMode(2, "Change Password for " + EscapeHtml(currentUser.name), 3, p30showUserChangePassDialogEx, x, multiFactor);
|
||||
showCreateNewAccountDialogValidate(1);
|
||||
Q('p4pass1').focus();
|
||||
}
|
||||
|
||||
function p30showUserChangePassDialogEx() {
|
||||
if (Q('p4pass1').value == Q('p4pass2').value) { meshserver.send({ action: 'changeuserpass', user: currentUser.name, pass: Q('p4pass1').value }); } }
|
||||
function p30showUserChangePassDialogEx(b, tag) {
|
||||
var removeMultiFactor = false;
|
||||
if ((tag == 1) && (Q('p4twoFactorRemove').checked == true)) { removeMultiFactor = true; }
|
||||
if (Q('p4pass1').value == Q('p4pass2').value) { meshserver.send({ action: 'changeuserpass', user: currentUser.name, pass: Q('p4pass1').value, hint: Q('p4hint').value, removeMultiFactor: removeMultiFactor }); }
|
||||
}
|
||||
|
||||
function p30showDeleteUserDialog() {
|
||||
if (xxdialogMode) return;
|
||||
|
163
webserver.js
163
webserver.js
@ -178,24 +178,6 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
function EscapeHtml(x) { if (typeof x == "string") return x.replace(/&/g, '&').replace(/>/g, '>').replace(/</g, '<').replace(/"/g, '"').replace(/'/g, '''); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
|
||||
//function EscapeHtmlBreaks(x) { if (typeof x == "string") return x.replace(/&/g, '&').replace(/>/g, '>').replace(/</g, '<').replace(/"/g, '"').replace(/'/g, ''').replace(/\r/g, '<br />').replace(/\n/g, '').replace(/\t/g, ' '); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
|
||||
|
||||
// Session-persisted message middleware
|
||||
obj.app.use(function (req, res, next) {
|
||||
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;
|
||||
}
|
||||
res.locals.message = '';
|
||||
if (err != null) res.locals.message = '<p class="msg error">' + err + '</p>';
|
||||
if (msg != null) res.locals.message = '<p class="msg success">' + msg + '</p>';
|
||||
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 = '<b style=color:#8C001A>Invalid token, try again.</b>'; }
|
||||
if ((req.body.token != null) || (req.body.hwtoken != null)) { req.session.error = '<b style=color:#8C001A>Invalid token, try again.</b>'; }
|
||||
req.session.loginmode = '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 = '<b style=color:#8C001A>Account locked.</b>'; } else { req.session.error = '<b style=color:#8C001A>Login failed, check username and password.</b>'; }
|
||||
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 = '<b style=color:#8C001A>Unable to create account.</b>';
|
||||
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 = '<b style=color:#8C001A>Account limit reached.</b>';
|
||||
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 = '<b style=color:#8C001A>Existing account with this email address.</b>';
|
||||
req.session.error = '<b style=color:#8C001A>Unable to create account.</b>';
|
||||
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 = '<b style=color:#8C001A>Invalid account creation token.</b>';
|
||||
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 = '<b style=color:#8C001A>Username already exists.</b>';
|
||||
} 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 = '<b style=color:#8C001A>Existing account with this email address.</b>';
|
||||
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 = '<b style=color:#8C001A>Invalid account creation token.</b>';
|
||||
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 = '<b style=color:#8C001A>Username already exists.</b>';
|
||||
} 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 = '<b style=color:#8C001A>Account locked.</b>';
|
||||
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 = '<p class="msg error">' + err + '</p>';
|
||||
if (msg != null) message = '<p class="msg success">' + msg + '</p>';
|
||||
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 });
|
||||
}
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user