Improved http session handling, new account page
This commit is contained in:
parent
82801f4069
commit
e3ed9bd3c2
|
@ -79,7 +79,7 @@ function CreateMeshCentralServer(config, args) {
|
||||||
try { require('./pass').hash('test', function () { }); } catch (e) { console.log('Old version of node, must upgrade.'); return; } // TODO: Not sure if this test works or not.
|
try { require('./pass').hash('test', function () { }); } catch (e) { console.log('Old version of node, must upgrade.'); return; } // TODO: Not sure if this test works or not.
|
||||||
|
|
||||||
// Check for invalid arguments
|
// Check for invalid arguments
|
||||||
var validArguments = ['_', 'notls', 'user', 'port', 'aliasport', 'mpsport', 'mpsaliasport', 'redirport', 'cert', 'mpscert', 'deletedomain', 'deletedefaultdomain', 'showall', 'showusers', 'shownodes', 'showmeshes', 'showevents', 'showpower', 'clearpower', 'showiplocations', 'help', 'exactports', 'install', 'uninstall', 'start', 'stop', 'restart', 'debug', 'filespath', 'datapath', 'noagentupdate', 'launch', 'noserverbackup', 'mongodb', 'mongodbcol', 'wanonly', 'lanonly', 'nousers', 'mpsdebug', 'mpspass', 'ciralocalfqdn', 'dbexport', 'dbimport', 'selfupdate', 'tlsoffload', 'userallowedip', 'fastcert', 'swarmport', 'swarmdebug', 'logintoken', 'logintokenkey', 'logintokengen', 'logintokengen', 'mailtokengen', 'admin', 'unadmin'];
|
var validArguments = ['_', 'notls', 'user', 'port', 'aliasport', 'mpsport', 'mpsaliasport', 'redirport', 'cert', 'mpscert', 'deletedomain', 'deletedefaultdomain', 'showall', 'showusers', 'shownodes', 'showmeshes', 'showevents', 'showpower', 'clearpower', 'showiplocations', 'help', 'exactports', 'install', 'uninstall', 'start', 'stop', 'restart', 'debug', 'filespath', 'datapath', 'noagentupdate', 'launch', 'noserverbackup', 'mongodb', 'mongodbcol', 'wanonly', 'lanonly', 'nousers', 'mpsdebug', 'mpspass', 'ciralocalfqdn', 'dbexport', 'dbimport', 'selfupdate', 'tlsoffload', 'userallowedip', 'fastcert', 'swarmport', 'swarmdebug', 'logintoken', 'logintokenkey', 'logintokengen', 'logintokengen', 'mailtokengen', 'admin', 'unadmin', 'sessionkey', 'sessiontime'];
|
||||||
for (var arg in obj.args) { obj.args[arg.toLocaleLowerCase()] = obj.args[arg]; if (validArguments.indexOf(arg.toLocaleLowerCase()) == -1) { console.log('Invalid argument "' + arg + '", use --help.'); return; } }
|
for (var arg in obj.args) { obj.args[arg.toLocaleLowerCase()] = obj.args[arg]; if (validArguments.indexOf(arg.toLocaleLowerCase()) == -1) { console.log('Invalid argument "' + arg + '", use --help.'); return; } }
|
||||||
if (obj.args.mongodb == true) { console.log('Must specify: --mongodb [connectionstring] \r\nSee https://docs.mongodb.com/manual/reference/connection-string/ for MongoDB connection string.'); return; }
|
if (obj.args.mongodb == true) { console.log('Must specify: --mongodb [connectionstring] \r\nSee https://docs.mongodb.com/manual/reference/connection-string/ for MongoDB connection string.'); return; }
|
||||||
for (var i in obj.config.settings) { obj.args[i] = obj.config.settings[i]; } // Place all settings into arguments, arguments have already been placed into settings so arguments take precedence.
|
for (var i in obj.config.settings) { obj.args[i] = obj.config.settings[i]; } // Place all settings into arguments, arguments have already been placed into settings so arguments take precedence.
|
||||||
|
@ -419,13 +419,12 @@ function CreateMeshCentralServer(config, args) {
|
||||||
// If the server is set to "nousers", allow only loopback unless IP filter is set
|
// If the server is set to "nousers", allow only loopback unless IP filter is set
|
||||||
if ((obj.args.nousers == true) && (obj.args.userallowedip == null)) { obj.args.userallowedip = "::1,127.0.0.1"; }
|
if ((obj.args.nousers == true) && (obj.args.userallowedip == null)) { obj.args.userallowedip = "::1,127.0.0.1"; }
|
||||||
|
|
||||||
if (obj.args.secret) {
|
// Set the session length to 60 minutes if not set and set a random key if needed
|
||||||
// This secret is used to encrypt HTTP session information, if specified, user it.
|
if ((obj.args.sessiontime == null) || (typeof obj.args.sessiontime != 'number') || (obj.args.sessiontime < 1)) { obj.args.sessiontime = 60; }
|
||||||
obj.webserver = require('./webserver.js').CreateWebServer(obj, obj.db, obj.args, obj.args.secret, obj.certificates);
|
if (!obj.args.sessionkey) { obj.args.sessionkey = buf.toString('hex').toUpperCase(); }
|
||||||
} else {
|
|
||||||
// If the secret is not specified, generate a random number.
|
// Start eh web server and if needed, the redirection web server.
|
||||||
obj.webserver = require('./webserver.js').CreateWebServer(obj, obj.db, obj.args, buf.toString('hex').toUpperCase(), obj.certificates);
|
obj.webserver = require('./webserver.js').CreateWebServer(obj, obj.db, obj.args, obj.certificates);
|
||||||
}
|
|
||||||
if (obj.redirserver != null) { obj.redirserver.hookMainWebServer(obj.certificates); }
|
if (obj.redirserver != null) { obj.redirserver.hookMainWebServer(obj.certificates); }
|
||||||
|
|
||||||
// Setup the Intel AMT event handler
|
// Setup the Intel AMT event handler
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "meshcentral",
|
"name": "meshcentral",
|
||||||
"version": "0.1.9-k",
|
"version": "0.1.9-m",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"Remote Management",
|
"Remote Management",
|
||||||
"Intel AMT",
|
"Intel AMT",
|
||||||
|
@ -30,6 +30,7 @@
|
||||||
"body-parser": "^1.18.2",
|
"body-parser": "^1.18.2",
|
||||||
"compression": "^1.7.1",
|
"compression": "^1.7.1",
|
||||||
"connect-redis": "^3.3.3",
|
"connect-redis": "^3.3.3",
|
||||||
|
"cookie-session": "^2.0.0-beta.3",
|
||||||
"express": "^4.16.2",
|
"express": "^4.16.2",
|
||||||
"express-handlebars": "^3.0.0",
|
"express-handlebars": "^3.0.0",
|
||||||
"express-session": "^1.15.6",
|
"express-session": "^1.15.6",
|
||||||
|
@ -43,7 +44,7 @@
|
||||||
"xmldom": "^0.1.27",
|
"xmldom": "^0.1.27",
|
||||||
"yauzl": "^2.9.1"
|
"yauzl": "^2.9.1"
|
||||||
},
|
},
|
||||||
"devDependencies": { },
|
"devDependencies": {},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Ylianst/MeshCentral.git"
|
"url": "https://github.com/Ylianst/MeshCentral.git"
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
"_MongoDbCol": "meshcentral",
|
"_MongoDbCol": "meshcentral",
|
||||||
"_WANonly": true,
|
"_WANonly": true,
|
||||||
"_LANonly": true,
|
"_LANonly": true,
|
||||||
|
"_SessionTime": 30,
|
||||||
|
"_SessionKey": "MyReallySecretPassword",
|
||||||
"_Port": 443,
|
"_Port": 443,
|
||||||
"_RedirPort": 80,
|
"_RedirPort": 80,
|
||||||
"_AllowLoginToken": true,
|
"_AllowLoginToken": true,
|
||||||
|
|
|
@ -72,27 +72,27 @@
|
||||||
</div>
|
</div>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td align=right width=100>Username:</td>
|
<td id="nuUser" align=right width=100>Username:</td>
|
||||||
<td><input id=ausername type=text name=username onchange=validateCreate(1) maxlength=64 onkeydown=haltReturn(event) onkeyup=validateCreate(1,event) /></td>
|
<td><input id=ausername type=text name=username onchange=validateCreate(1) maxlength=64 onkeydown=haltReturn(event) onkeyup=validateCreate(1,event) /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align=right width=100>Email:</td>
|
<td id="nuEmail" align=right width=100>Email:</td>
|
||||||
<td><input id=aemail type=text name=email onchange=validateCreate(2) maxlength=256 onkeydown=haltReturn(event) onkeyup=validateCreate(2,event) /></td>
|
<td><input id=aemail type=text name=email onchange=validateCreate(2) maxlength=256 onkeydown=haltReturn(event) onkeyup=validateCreate(2,event) /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align=right>Password:</td>
|
<td id="nuPass1" align=right>Password:</td>
|
||||||
<td><input id=apassword1 type=password name=password1 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(3) onkeyup=validateCreate(3,event) /></td>
|
<td><input id=apassword1 type=password name=password1 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(3) onkeyup=validateCreate(3,event) /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align=right>Password:</td>
|
<td id="nuPass2" align=right>Password:</td>
|
||||||
<td><input id=apassword2 type=password name=password2 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(4) onkeyup=validateCreate(4,event) /></td>
|
<td><input id=apassword2 type=password name=password2 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(4) onkeyup=validateCreate(4,event) /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align=right>Password Hint:</td>
|
<td id="nuHint" align=right>Password Hint:</td>
|
||||||
<td><input id=apasswordhint type=text name=apasswordhint autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(5) onkeyup=validateCreate(5,event) /></td>
|
<td><input id=apasswordhint type=text name=apasswordhint autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(5) onkeyup=validateCreate(5,event) /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id=newAccountPass title="Enter the account creation token">
|
<tr id=newAccountPass title="Enter the account creation token">
|
||||||
<td align=right>Creation Token:</td>
|
<td id="nuToken" align=right>Creation Token:</td>
|
||||||
<td><input id=anewaccountpass type=password name=anewaccountpass autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(6) onkeyup=validateCreate(6,event) /></td>
|
<td><input id=anewaccountpass type=password name=anewaccountpass autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(6) onkeyup=validateCreate(6,event) /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -224,8 +224,20 @@
|
||||||
|
|
||||||
function validateCreate(box,e) {
|
function validateCreate(box,e) {
|
||||||
setDialogMode(0);
|
setDialogMode(0);
|
||||||
var ok = ((Q('ausername').value.length > 0) && (Q('ausername').value.indexOf(' ') == -1) && (validateEmail(Q('aemail').value) == true) && (Q('apassword1').value.length > 0) && (Q('apassword2').value == Q('apassword1').value));
|
var userok = (Q('ausername').value.length > 0) && (Q('ausername').value.indexOf(' ') == -1);
|
||||||
if ((newAccountPass == 1) && (Q('anewaccountpass').value.length == 0)) { ok = false; }
|
var emailok = (validateEmail(Q('aemail').value) == true);
|
||||||
|
var pass1ok = (Q('apassword1').value.length > 0);
|
||||||
|
var pass2ok = (Q('apassword2').value.length > 0) && (Q('apassword2').value == Q('apassword1').value);
|
||||||
|
var newAccOk = (newAccountPass == 0) || (Q('anewaccountpass').value.length > 0);
|
||||||
|
var ok = (userok && emailok && pass1ok && pass2ok && newAccOk);
|
||||||
|
|
||||||
|
// Color the fields
|
||||||
|
QS('nuUser').color = userok?'black':'#7b241c';
|
||||||
|
QS('nuEmail').color = emailok?'black':'#7b241c';
|
||||||
|
QS('nuPass1').color = pass1ok?'black':'#7b241c';
|
||||||
|
QS('nuPass2').color = pass2ok?'black':'#7b241c';
|
||||||
|
QS('nuToken').color = newAccOk?'black':'#7b241c';
|
||||||
|
|
||||||
QE('createButton', ok);
|
QE('createButton', ok);
|
||||||
if (Q('apassword1').value == '') {
|
if (Q('apassword1').value == '') {
|
||||||
QH('passWarning', '');
|
QH('passWarning', '');
|
||||||
|
|
27
webserver.js
27
webserver.js
|
@ -36,7 +36,7 @@ if (!String.prototype.startsWith) { String.prototype.startsWith = function (sear
|
||||||
if (!String.prototype.endsWith) { String.prototype.endsWith = function (searchString, position) { var subjectString = this.toString(); if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { position = subjectString.length; } position -= searchString.length; var lastIndex = subjectString.lastIndexOf(searchString, position); return lastIndex !== -1 && lastIndex === position; }; }
|
if (!String.prototype.endsWith) { String.prototype.endsWith = function (searchString, position) { var subjectString = this.toString(); if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { position = subjectString.length; } position -= searchString.length; var lastIndex = subjectString.lastIndexOf(searchString, position); return lastIndex !== -1 && lastIndex === position; }; }
|
||||||
|
|
||||||
// Construct a HTTP web server object
|
// Construct a HTTP web server object
|
||||||
module.exports.CreateWebServer = function (parent, db, args, secret, certificates) {
|
module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||||
var obj = {};
|
var obj = {};
|
||||||
|
|
||||||
// Modules
|
// Modules
|
||||||
|
@ -46,7 +46,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
|
||||||
obj.path = require('path');
|
obj.path = require('path');
|
||||||
obj.constants = require('constants');
|
obj.constants = require('constants');
|
||||||
obj.bodyParser = require('body-parser');
|
obj.bodyParser = require('body-parser');
|
||||||
obj.session = require('express-session');
|
obj.session = require('cookie-session');
|
||||||
obj.exphbs = require('express-handlebars');
|
obj.exphbs = require('express-handlebars');
|
||||||
obj.crypto = require('crypto');
|
obj.crypto = require('crypto');
|
||||||
obj.common = require('./common.js');
|
obj.common = require('./common.js');
|
||||||
|
@ -154,9 +154,11 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
|
||||||
obj.app.set('view engine', 'handlebars');
|
obj.app.set('view engine', 'handlebars');
|
||||||
obj.app.use(obj.bodyParser.urlencoded({ extended: false }));
|
obj.app.use(obj.bodyParser.urlencoded({ extended: false }));
|
||||||
obj.app.use(obj.session({
|
obj.app.use(obj.session({
|
||||||
resave: false, // don't save session if unmodified
|
name: 'xid', // Recommanded security practice to not use the default cookie name
|
||||||
saveUninitialized: false, // don't create session until something stored
|
httpOnly: true,
|
||||||
secret: secret // If multiple instances of this server are behind a load-balancer, this secret must be the same for all instances
|
keys: [ obj.args.sessionkey ], // If multiple instances of this server are behind a load-balancer, this secret must be the same for all instances
|
||||||
|
secure: (obj.args.notls != true), // Use this cookie only over TLS
|
||||||
|
maxAge: (obj.args.sessiontime * 60 * 1000) // 24 hours
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Session-persisted message middleware
|
// Session-persisted message middleware
|
||||||
|
@ -290,9 +292,8 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
|
||||||
var user = obj.users[req.session.userid]
|
var user = obj.users[req.session.userid]
|
||||||
obj.parent.DispatchEvent(['*'], obj, { etype: 'user', username: user.name, action: 'logout', msg: 'Account logout', domain: domain.id })
|
obj.parent.DispatchEvent(['*'], obj, { etype: 'user', username: user.name, action: 'logout', msg: 'Account logout', domain: domain.id })
|
||||||
}
|
}
|
||||||
req.session.destroy(function () {
|
req.session = null;
|
||||||
res.redirect(domain.url);
|
res.redirect(domain.url);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleLoginRequest(req, res) {
|
function handleLoginRequest(req, res) {
|
||||||
|
@ -306,8 +307,9 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
|
||||||
user.login = Date.now();
|
user.login = Date.now();
|
||||||
obj.db.SetUser(user);
|
obj.db.SetUser(user);
|
||||||
|
|
||||||
|
|
||||||
// Regenerate session when signing in to prevent fixation
|
// Regenerate session when signing in to prevent fixation
|
||||||
req.session.regenerate(function () {
|
//req.session.regenerate(function () {
|
||||||
// Store the user's primary key in the session store to be retrieved, or in this case the entire user object
|
// Store the user's primary key in the session store to be retrieved, or in this case the entire user object
|
||||||
// req.session.success = 'Authenticated as ' + user.name + 'click to <a href="/logout">logout</a>. You may now access <a href="/restricted">/restricted</a>.';
|
// req.session.success = 'Authenticated as ' + user.name + 'click to <a href="/logout">logout</a>. You may now access <a href="/restricted">/restricted</a>.';
|
||||||
delete req.session.loginmode;
|
delete req.session.loginmode;
|
||||||
|
@ -334,7 +336,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
|
||||||
} else {
|
} else {
|
||||||
res.redirect(domain.url);
|
res.redirect(domain.url);
|
||||||
}
|
}
|
||||||
});
|
//});
|
||||||
|
|
||||||
obj.parent.DispatchEvent(['*'], obj, { etype: 'user', username: user.name, action: 'login', msg: 'Account login', domain: domain.id })
|
obj.parent.DispatchEvent(['*'], obj, { etype: 'user', username: user.name, action: 'login', msg: 'Account login', domain: domain.id })
|
||||||
} else {
|
} else {
|
||||||
|
@ -569,7 +571,8 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
|
||||||
// Remove the user
|
// Remove the user
|
||||||
obj.db.Remove(user._id);
|
obj.db.Remove(user._id);
|
||||||
delete obj.users[user._id];
|
delete obj.users[user._id];
|
||||||
req.session.destroy(function () { res.redirect(domain.url); });
|
req.session = null;
|
||||||
|
res.redirect(domain.url);
|
||||||
obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'accountremove', msg: 'Account removed', domain: domain.id })
|
obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'accountremove', msg: 'Account removed', domain: domain.id })
|
||||||
} else {
|
} else {
|
||||||
res.redirect(domain.url);
|
res.redirect(domain.url);
|
||||||
|
@ -679,7 +682,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
|
||||||
|
|
||||||
// If a user is logged in, serve the default app, otherwise server the login app.
|
// If a user is logged in, serve the default app, otherwise server the login app.
|
||||||
if (req.session && req.session.userid) {
|
if (req.session && req.session.userid) {
|
||||||
if (req.session.domainid != domain.id) { req.session.destroy(function () { res.redirect(domain.url); }); return; } // Check is the session is for the correct domain
|
if (req.session.domainid != domain.id) { req.session = null; res.redirect(domain.url); return; } // Check is the session is for the correct domain
|
||||||
var viewmode = 1;
|
var viewmode = 1;
|
||||||
if (req.session.viewmode) {
|
if (req.session.viewmode) {
|
||||||
viewmode = req.session.viewmode;
|
viewmode = req.session.viewmode;
|
||||||
|
@ -751,7 +754,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
|
||||||
if (domain == null) return;
|
if (domain == null) return;
|
||||||
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' });
|
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' });
|
||||||
if (req.session && req.session.userid) {
|
if (req.session && req.session.userid) {
|
||||||
if (req.session.domainid != domain.id) { req.session.destroy(function () { res.redirect(domain.url); }); return; } // Check is the session is for the correct domain
|
if (req.session.domainid != domain.id) { req.session = null; res.redirect(domain.url); return; } // Check is the session is for the correct domain
|
||||||
var user = obj.users[req.session.userid];
|
var user = obj.users[req.session.userid];
|
||||||
res.render(obj.path.join(__dirname, isMobileBrowser(req) ? 'views/terms-mobile' : 'views/terms'), { title: domain.title, title2: domain.title2, logoutControl: 'Welcome ' + user.name + '. <a href=' + domain.url + 'logout?' + Math.random() + ' style=color:white>Logout</a>' });
|
res.render(obj.path.join(__dirname, isMobileBrowser(req) ? 'views/terms-mobile' : 'views/terms'), { title: domain.title, title2: domain.title2, logoutControl: 'Welcome ' + user.name + '. <a href=' + domain.url + 'logout?' + Math.random() + ' style=color:white>Logout</a>' });
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue