From f98d937923421d2577e68df10b7bf22ec5ad9bce Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Wed, 12 Jun 2019 10:23:26 -0700 Subject: [PATCH] Added invitation link email, server fixes and improvements. --- meshcentral.js | 3 ++ meshmail.js | 15 +++++-- meshuser.js | 2 +- views/default.handlebars | 9 +++- views/login-mobile.handlebars | 17 ++++++++ views/login.handlebars | 17 ++++++++ webserver.js | 81 ++++++++++++++++++----------------- 7 files changed, 98 insertions(+), 46 deletions(-) diff --git a/meshcentral.js b/meshcentral.js index 8a552eef..75cf0e73 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -234,6 +234,9 @@ function CreateMeshCentralServer(config, args) { //var wincmd = require('node-windows'); //wincmd.list(function (svc) { console.log(svc); }, true); + // Check top level configuration for any unreconized values + if (config) { for (var i in config) { if ((typeof i == 'string') && (i.length > 0) && (i[0] != '_') && (['settings', 'domains', 'configfiles', 'smtp', 'letsencrypt', 'peers'].indexOf(i) == -1)) { console.log('WARNING: unrecognized configuration option \"' + i + '\".'); } } } + if (typeof obj.args.userallowedip == 'string') { if (obj.args.userallowedip == '') { obj.args.userallowedip = null; } else { obj.args.userallowedip = obj.args.userallowedip.split(','); } } if (typeof obj.args.userblockedip == 'string') { if (obj.args.userblockedip == '') { obj.args.userblockedip = null; } else { obj.args.userblockedip = obj.args.userblockedip.split(','); } } if (typeof obj.args.agentallowedip == 'string') { if (obj.args.agentallowedip == '') { obj.args.agentallowedip = null; } else { obj.args.agentallowedip = obj.args.agentallowedip.split(','); } } diff --git a/meshmail.js b/meshmail.js index d54ff90a..29369f26 100644 --- a/meshmail.js +++ b/meshmail.js @@ -39,10 +39,10 @@ module.exports.CreateMeshMail = function (parent) { // If the server hash many domains, just add the domainid to the file like this: 'account-check-customer1.html', 'mesh-invite-customer1.txt'. obj.mailTemplates['account-check.html'] = '[[[SERVERNAME]]] - Email Verification\r\n
[[[SERVERNAME]]] - Verification

Hi [[[USERNAME]]], [[[SERVERNAME]]] is requesting email verification, click on the following link to complete the process.

Click here to verify your e-mail address.

If you did not initiate this request, please ignore this mail.
'; obj.mailTemplates['account-reset.html'] = '[[[SERVERNAME]]] - Account Reset\r\n
[[[SERVERNAME]]] - Verification

Hi [[[USERNAME]]], [[[SERVERNAME]]] is requesting an account password reset, click on the following link to complete the process.

Click here to reset your account password.

If you did not initiate this request, please ignore this mail.
'; - obj.mailTemplates['mesh-invite.html'] = '[[[SERVERNAME]]] - Invitation\r\n
[[[SERVERNAME]]] - Agent Installation
[[[AREA-NAME]]]

Hello [[[NAME]]],

[[[/AREA-NAME]]]

User [[[USERNAME]]] on server [[[SERVERNAME]]] is requesting you to download the following software to start the remote control session.

[[[AREA-MSG]]]

Message: [[[MSG]]]

[[[/AREA-MSG]]][[[AREA-WINDOWS]]]

Click here to download the MeshAgent for Windows.

[[[/AREA-WINDOWS]]][[[AREA-OSX]]]

Click here to download the MeshAgent for Apple OSX.

[[[/AREA-OSX]]][[[AREA-LINUX]]]

For Linux, cut & paste the following in a terminal to install the agent:

wget -q [[[SERVERURL]]]/meshagents?script=1 --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] [[[MESHIDHEX]]]

[[[/AREA-LINUX]]]

If you did not initiate this request, please ignore this mail.

Best regards,
[[[USERNAME]]]
'; + obj.mailTemplates['mesh-invite.html'] = '[[[SERVERNAME]]] - Invitation\r\n
[[[SERVERNAME]]] - Agent Installation
[[[AREA-NAME]]]

Hello [[[NAME]]],

[[[/AREA-NAME]]]

User [[[USERNAME]]] on server [[[SERVERNAME]]] is requesting you to install software to start a remote control session.

[[[AREA-MSG]]]

Message: [[[MSG]]]

[[[/AREA-MSG]]][[[AREA-WINDOWS]]]

Click here to download the MeshAgent for Windows.

[[[/AREA-WINDOWS]]][[[AREA-OSX]]]

Click here to download the MeshAgent for Apple OSX.

[[[/AREA-OSX]]][[[AREA-LINUX]]]

For Linux, cut & paste the following in a terminal to install the agent:

wget -q [[[SERVERURL]]]/meshagents?script=1 --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] [[[MESHIDHEX]]]

[[[/AREA-LINUX]]][[[AREA-LINK]]]

To install the software, click here and follow the instructions.

[[[/AREA-LINK]]]

If you did not initiate this request, please ignore this mail.

Best regards,
[[[USERNAME]]]
'; obj.mailTemplates['account-check.txt'] = '[[[SERVERNAME]]] - Email Verification\r\nHi [[[USERNAME]]], [[[SERVERNAME]]] ([[[SERVERURL]]]) is performing an e-mail verification. Nagivate to the following link to complete the process:\r\n\r\n[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]\r\n\r\nIf you did not initiate this request, please ignore this mail.\r\n'; obj.mailTemplates['account-reset.txt'] = '[[[SERVERNAME]]] - Account Reset\r\nHi [[[USERNAME]]], [[[SERVERNAME]]] ([[[SERVERURL]]]) is requesting an account password reset. Nagivate to the following link to complete the process:\r\n\r\n[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]\r\n\r\nIf you did not initiate this request, please ignore this mail.'; - obj.mailTemplates['mesh-invite.txt'] = '[[[SERVERNAME]]] - Invitation\r\n[[[AREA-NAME]]]Hello [[[NAME]]],\r\n\r\n[[[/AREA-NAME]]]User [[[USERNAME]]] on server [[[SERVERNAME]]] ([[[SERVERURL]]]/) is requesting you to download the following software to start the remote control session.[[[AREA-MSG]]]\r\n\r\nMessage: [[[MSG]]]\r\n\r\n[[[/AREA-MSG]]][[[AREA-WINDOWS]]]For Windows, nagivate to the following link to complete the process:\r\n\r\n[[[SERVERURL]]]/meshagents?id=3&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]\r\n\r\n[[[/AREA-WINDOWS]]][[[AREA-OSX]]]For Apple OSX, nagivate to the following link to complete the process:\r\n\r\n[[[SERVERURL]]]/meshagents?id=16&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]\r\n\r\n[[[/AREA-OSX]]][[[AREA-LINUX]]]For Linux, cut & paste the following in a terminal to install the agent:\r\n\r\nwget -q [[[SERVERURL]]]/meshagents?script=1 --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] [[[MESHIDHEX]]]\r\n\r\n[[[/AREA-LINUX]]]If you did not initiate this request, please ignore this mail.\r\n\r\nBest regards,\r\n[[[USERNAME]]]'; + obj.mailTemplates['mesh-invite.txt'] = '[[[SERVERNAME]]] - Invitation\r\n[[[AREA-NAME]]]Hello [[[NAME]]],\r\n\r\n[[[/AREA-NAME]]]User [[[USERNAME]]] on server [[[SERVERNAME]]] ([[[SERVERURL]]]/) is requesting you install software to start the remote control session.[[[AREA-MSG]]]\r\n\r\nMessage: [[[MSG]]]\r\n\r\n[[[/AREA-MSG]]][[[AREA-WINDOWS]]]For Windows, nagivate to the following link to complete the process:\r\n\r\n[[[SERVERURL]]]/meshagents?id=3&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]\r\n\r\n[[[/AREA-WINDOWS]]][[[AREA-OSX]]]For Apple OSX, nagivate to the following link to complete the process:\r\n\r\n[[[SERVERURL]]]/meshagents?id=16&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]\r\n\r\n[[[/AREA-OSX]]][[[AREA-LINUX]]]For Linux, cut & paste the following in a terminal to install the agent:\r\n\r\nwget -q [[[SERVERURL]]]/meshagents?script=1 --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] [[[MESHIDHEX]]]\r\n\r\n[[[/AREA-LINUX]]][[[AREA-LINK]]]To install the software, navigate to [[[SERVERURL]]][[[LINKURL]]] and follow the instructions.\r\n\r\n[[[/AREA-LINK]]]If you did not initiate this request, please ignore this mail.\r\n\r\nBest regards,\r\n[[[USERNAME]]]'; // Load all of the mail templates if present if (obj.parent.fs.existsSync(obj.parent.path.join(obj.parent.datapath, 'mail-templates'))) { @@ -93,7 +93,7 @@ module.exports.CreateMeshMail = function (parent) { return str.substring(0, si) + str.substring(ei + end.length); } - // Remove the string between two markers + // Keep or remove the string between two markers function strZone(str, marker, keep) { marker = marker.toUpperCase(); if (keep) { return str.split('[[[AREA-' + marker + ']]]').join('').split('[[[/AREA-' + marker + ']]]').join(''); } @@ -153,7 +153,7 @@ module.exports.CreateMeshMail = function (parent) { }; // Send agent invite mail - obj.sendAgentInviteMail = function (domain, username, email, meshid, name, os, msg, flags) { + obj.sendAgentInviteMail = function (domain, username, email, meshid, name, os, msg, flags, expirehours) { var template = getTemplateEx('mesh-invite', domain); if ((template == null) || (template.htmlSubject == null) || (template.txtSubject == null) || (parent.certificates == null) || (parent.certificates.CommonName == null) || (parent.certificates.CommonName.indexOf('.') == -1)) return; // If the server name is not set, don't validate the email address. @@ -162,6 +162,8 @@ module.exports.CreateMeshMail = function (parent) { options.windows = ((os == 0) || (os == 1)) ? 1 : 0; options.linux = ((os == 0) || (os == 2)) ? 1 : 0; options.osx = ((os == 0) || (os == 3)) ? 1 : 0; + options.link = (os == 4) ? 1 : 0; + options.linkurl = createInviteLink(domain, meshid, flags, expirehours); // Send the email obj.pendingMails.push({ to: email, from: parent.config.smtp.from, subject: mailReplacements(template.htmlSubject, domain, options), text: mailReplacements(template.txt, domain, options), html: mailReplacements(template.html, domain, options) }); @@ -213,5 +215,10 @@ module.exports.CreateMeshMail = function (parent) { } }); + // Create a agent invitation link + function createInviteLink(domain, meshid, flags, expirehours) { + return '/agentinvite?c=' + parent.encodeCookie({ a: 4, mid: meshid, f: flags, expire: expirehours * 60 }, parent.invitationLinkEncryptionKey); + } + return obj; }; \ No newline at end of file diff --git a/meshuser.js b/meshuser.js index 493bbfff..415d7d43 100644 --- a/meshuser.js +++ b/meshuser.js @@ -2103,7 +2103,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use //if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 4) == 0)) return; // Perform email invitation - parent.parent.mailserver.sendAgentInviteMail(domain, user.name, command.email, command.meshid, command.name, command.os, command.msg, command.flags); + parent.parent.mailserver.sendAgentInviteMail(domain, user.name, command.email, command.meshid, command.name, command.os, command.msg, command.flags, command.expire); } break; } diff --git a/views/default.handlebars b/views/default.handlebars index 8aa0f27a..a84c4496 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -2710,7 +2710,10 @@ x += "'; @@ -2719,6 +2722,7 @@ x += addHtmlValue('Link Expiration', ''); x += ''; setDialogMode(2, "Invite", 3, performAgentInvite, x, meshid); + d2ChangedInviteType(); validateAgentInvite(); d2RequestInvitationLink(); } @@ -2729,6 +2733,7 @@ function d2ChangedInviteType() { QV('urlInviteDiv', Q('d2InviteType').value == 0); + QV('d2agentexpirediv', Q('agentInviteNameOs').value == 4); if (features & 64) { QV('emailInviteDiv', Q('d2InviteType').value == 1); } validateAgentInvite(); } @@ -2747,7 +2752,7 @@ function performAgentInvite(button, meshid) { if ((features & 64) && (Q('d2InviteType').value == 1)) { - meshserver.send({ action: 'inviteAgent', meshid: meshid, email: Q('agentInviteEmail').value, name: Q('agentInviteName').value, os: Q('agentInviteNameOs').value, flags: Q('agentInviteType').value, msg: Q('agentInviteMessage').value }); + meshserver.send({ action: 'inviteAgent', meshid: meshid, email: Q('agentInviteEmail').value, name: Q('agentInviteName').value, os: Q('agentInviteNameOs').value, flags: Q('agentInviteType').value, msg: Q('agentInviteMessage').value, expire: parseInt(Q('agentInviteExpire').value) }); } } diff --git a/views/login-mobile.handlebars b/views/login-mobile.handlebars index 96f0f010..39ec84d6 100644 --- a/views/login-mobile.handlebars +++ b/views/login-mobile.handlebars @@ -72,6 +72,7 @@ + @@ -141,6 +143,7 @@
Back to login + @@ -186,6 +190,7 @@
Back to login + @@ -216,6 +221,7 @@
Back to login + @@ -264,6 +270,17 @@ var hardwareKeyChallenge = decodeURIComponent('{{{hkey}}}'); var currentpanel = 0; + // If URL arguments are provided, add them to form posts + if (window.location.href.indexOf('?') > 0) { + var urlargs = window.location.href.substring(window.location.href.indexOf('?')); + Q('loginformargs').value = urlargs; + Q('createformargs').value = urlargs; + Q('resetformargs').value = urlargs; + Q('tokenformargs').value = urlargs; + Q('resettokenformargs').value = urlargs; + Q('resetpasswordformargs').value = urlargs; + } + function startup() { if ((features & 32) == 0) { // Guard against other site's top frames (web bugs). diff --git a/views/login.handlebars b/views/login.handlebars index 3e63cc33..af3eee92 100644 --- a/views/login.handlebars +++ b/views/login.handlebars @@ -69,6 +69,7 @@ + @@ -260,6 +266,17 @@ var webPageFullScreen = true; var nightMode = (getstore('_nightMode', '0') == '1'); + // If URL arguments are provided, add them to form posts + if (window.location.href.indexOf('?') > 0) { + var urlargs = window.location.href.substring(window.location.href.indexOf('?')); + Q('loginformargs').value = urlargs; + Q('createformargs').value = urlargs; + Q('resetformargs').value = urlargs; + Q('tokenformargs').value = urlargs; + Q('resettokenformargs').value = urlargs; + Q('resetpasswordformargs').value = urlargs; + } + //var webPageFullScreen = getstore('webPageFullScreen', true); //if (webPageFullScreen == 'false') { webPageFullScreen = false; } //if (webPageFullScreen == 'true') { webPageFullScreen = true; } diff --git a/webserver.js b/webserver.js index 797adb03..6c228bcf 100644 --- a/webserver.js +++ b/webserver.js @@ -647,7 +647,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { req.session.loginmode = '4'; req.session.tokenusername = xusername; req.session.tokenpassword = xpassword; - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); }, randomWaitTime); } else { // Login succesful @@ -672,7 +672,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { } else { delete req.session.passhint; } - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); }, 2000 + (obj.crypto.randomBytes(2).readUInt16BE(0) % 4095)); // Wait for 2 to ~6 seconds. } }); @@ -686,7 +686,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { req.session.error = 'Password change requested.'; req.session.resettokenusername = xusername; req.session.resettokenpassword = xpassword; - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); return; } @@ -726,12 +726,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { } console.log("CurrentNode: " + req.session.currentNode); // This redirect happens after finding node is completed - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); }); */ - res.redirect(domain.url); // Temporary + res.redirect(domain.url + getQueryPortion(req)); // Temporary } else { - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); } //}); } @@ -755,7 +755,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (i == -1) { req.session.loginmode = '2'; req.session.error = 'Unable to create account.'; - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); return; } var emailok = false, emaildomain = req.body.email.substring(i + 1).toLowerCase(); @@ -763,7 +763,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (emailok == false) { req.session.loginmode = '2'; req.session.error = 'Unable to create account.'; - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); return; } } @@ -774,25 +774,25 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { req.session.loginmode = '2'; req.session.error = 'Account limit reached.'; console.log('max', req.session); - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); } 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 = 'Unable to create account.'; - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); } else { // 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); + res.redirect(domain.url + getQueryPortion(req)); } 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); + res.redirect(domain.url + getQueryPortion(req)); return; } // Check if user exists @@ -822,7 +822,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { 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. obj.parent.DispatchEvent(['*', 'server-users'], obj, event); } - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); } }); } @@ -845,7 +845,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { delete req.session.success; delete req.session.error; delete req.session.passhint; - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); return; } @@ -859,7 +859,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (!obj.common.checkPasswordRequirements(req.body.rpassword1, domain.passwordrequirements)) { req.session.loginmode = '6'; req.session.error = 'Password rejected, use a different one.'; - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); return; } @@ -869,7 +869,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // This is the same password, request a password change again req.session.loginmode = '6'; req.session.error = 'Password rejected, use a different one.'; - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); } else { // Update the password, use a different salt. require('./pass').hash(req.body.rpassword1, function (err, salt, hash, tag) { @@ -902,7 +902,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { delete req.session.success; delete req.session.error; delete req.session.passhint; - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); return; } }); @@ -921,13 +921,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (!email || checkEmail(email) == false) { req.session.loginmode = '3'; req.session.error = 'Invalid email.'; - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); } else { obj.db.GetUserWithVerifiedEmail(domain.id, email, function (err, docs) { if ((err != null) || (docs.length == 0)) { req.session.loginmode = '3'; req.session.error = 'Account not found.'; - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); } else { // If many accounts have the same validated e-mail, we are going to use the first one for display, but sent a reset email for all accounts. var responseSent = false; @@ -942,7 +942,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if ((req.body.token != null) || (req.body.hwtoken != null)) { req.session.error = 'Invalid token, try again.'; } req.session.loginmode = '5'; req.session.tokenemail = email; - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); } } else { // Send email to perform recovery. @@ -952,13 +952,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (i == 0) { req.session.loginmode = '1'; req.session.error = 'Hold on, reset mail sent.'; - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); } } else { if (i == 0) { req.session.loginmode = '3'; req.session.error = 'Unable to sent email.'; - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); } } } @@ -970,13 +970,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (i == 0) { req.session.loginmode = '1'; req.session.error = 'Hold on, reset mail sent.'; - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); } } else { if (i == 0) { req.session.loginmode = '3'; req.session.error = 'Unable to sent email.'; - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); } } } @@ -1107,7 +1107,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) { res.sendStatus(404); return; } // Check if the user is logged and we have all required parameters - if (!req.session || !req.session.userid || !req.body.apassword1 || (req.body.apassword1 != req.body.apassword2) || (req.session.domainid != domain.id)) { res.redirect(domain.url); return; } + if (!req.session || !req.session.userid || !req.body.apassword1 || (req.body.apassword1 != req.body.apassword2) || (req.session.domainid != domain.id)) { res.redirect(domain.url + getQueryPortion(req)); return; } var user = obj.users[req.session.userid]; if (!user) return; @@ -1138,10 +1138,10 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { obj.db.Remove(user._id); delete obj.users[user._id]; req.session = null; - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'accountremove', msg: 'Account removed', domain: domain.id }); } else { - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); } }); } @@ -1178,11 +1178,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) { res.sendStatus(404); return; } // Check if the user is logged and we have all required parameters - if (!req.session || !req.session.userid || !req.body.apassword0 || !req.body.apassword1 || (req.body.apassword1 != req.body.apassword2) || (req.session.domainid != domain.id)) { res.redirect(domain.url); return; } + if (!req.session || !req.session.userid || !req.body.apassword0 || !req.body.apassword1 || (req.body.apassword1 != req.body.apassword2) || (req.session.domainid != domain.id)) { res.redirect(domain.url + getQueryPortion(req)); return; } // Get the current user var user = obj.users[req.session.userid]; - if (!user) { res.redirect(domain.url); return; } + if (!user) { res.redirect(domain.url + getQueryPortion(req)); return; } // Check old password obj.checkUserPassword(domain, user, req.body.apassword0, function (result) { @@ -1197,7 +1197,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { delete user.passtype; obj.db.SetUser(user); req.session.viewmode = 2; - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'passchange', msg: 'Account password changed: ' + user.name, domain: domain.id }); }, 0); } @@ -1232,7 +1232,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' }); // Check if we have an incomplete domain name in the path - if ((domain.id != '') && (domain.dns == null) && (req.url.split('/').length == 2)) { res.redirect(domain.url); return; } + if ((domain.id != '') && (domain.dns == null) && (req.url.split('/').length == 2)) { res.redirect(domain.url + getQueryPortion(req)); return; } if (obj.args.nousers == true) { // If in single user mode, setup things here. @@ -1300,7 +1300,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // If a user exists and is logged in, serve the default app, otherwise server the login app. 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 + if (req.session.domainid != domain.id) { req.session = null; res.redirect(domain.url + getQueryPortion(req)); 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)) { @@ -1310,7 +1310,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { delete req.session.currentNode; delete req.session.passhint; req.session.error = 'Account locked.'; - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); return; } @@ -1393,14 +1393,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { obj.db.GetUserWithVerifiedEmail(domain.id, req.session.tokenemail, function (err, docs) { if ((err != null) || (docs.length == 0)) { req.session = null; - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); } else { var user = obj.users[docs[0]._id]; if (user != null) { getHardwareKeyChallenge(req, domain, user, function (hwchallenge) { handleRootRequestLogin(req, res, domain, hwchallenge, passRequirements); }); } else { req.session = null; - res.redirect(domain.url); + res.redirect(domain.url + getQueryPortion(req)); } } }); @@ -1486,7 +1486,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // Send the terms from the database res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' }); if (req.session && 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 + if (req.session.domainid != domain.id) { req.session = null; res.redirect(domain.url + getQueryPortion(req)); return; } // Check is the session is for the correct domain var user = obj.users[req.session.userid]; var logoutcontrol = 'Welcome ' + user.name + '.'; if ((domain.ldap == null) && (domain.sspi == null) && (obj.args.user == null) && (obj.args.nousers != true)) { logoutcontrol += ' Logout'; } // If a default user is in use or no user mode, don't display the logout button @@ -1504,7 +1504,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // Send the terms from terms.txt res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' }); if (req.session && 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 + if (req.session.domainid != domain.id) { req.session = null; res.redirect(domain.url + getQueryPortion(req)); return; } // Check is the session is for the correct domain var user = obj.users[req.session.userid]; var logoutcontrol = 'Welcome ' + user.name + '.'; if ((domain.ldap == null) && (domain.sspi == null) && (obj.args.user == null) && (obj.args.nousers != true)) { logoutcontrol += ' Logout'; } // If a default user is in use or no user mode, don't display the logout button @@ -1517,7 +1517,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // Send the default terms res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' }); if (req.session && 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 + if (req.session.domainid != domain.id) { req.session = null; res.redirect(domain.url + getQueryPortion(req)); return; } // Check is the session is for the correct domain var user = obj.users[req.session.userid]; var logoutcontrol = 'Welcome ' + user.name + '.'; if ((domain.ldap == null) && (domain.sspi == null) && (obj.args.user == null) && (obj.args.nousers != true)) { logoutcontrol += ' Logout'; } // If a default user is in use or no user mode, don't display the logout button @@ -1721,7 +1721,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (splitUrl.length > 1) { urlArgs = '?' + splitUrl[1]; } if ((splitUrl.length > 0) && (splitUrl[0].length > 1)) { urlName = splitUrl[0].substring(1).toLowerCase(); } if ((urlName == null) || (domain.redirects[urlName] == null)) { res.sendStatus(404); return; } - res.redirect(domain.redirects[urlName] + urlArgs); + res.redirect(domain.redirects[urlName] + urlArgs + getQueryPortion(req)); } // Take a "user/domain/userid/path/file" format and return the actual server disk file path if access is allowed @@ -3150,5 +3150,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { return (req.headers['user-agent'].toLowerCase().indexOf('mobile') >= 0); } + // Return the query string portion of the URL, the ? and anything after. + function getQueryPortion(req) { var s = req.url.indexOf('?'); if (s == -1) { if (req.body && req.body.urlargs) { return req.body.urlargs; } return ''; } return req.url.substring(s); } + return obj; }; \ No newline at end of file