Added invitation link email, server fixes and improvements.

Ylian Saint-Hilaire committed Jun 12, 2019 at 10:23 UTC feb3542e8b02803187ba6653cfaeafa8cd40a9e4
7 files changed +98 -46
meshcentral.js
+3
@@ -234,6 +234,9 @@ function CreateMeshCentralServer(config, args) {
234 //var wincmd = require('node-windows');
235 //wincmd.list(function (svc) { console.log(svc); }, true);
236
237 + // Check top level configuration for any unreconized values
238 + 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 + '\".'); } } }
239 +
240 if (typeof obj.args.userallowedip == 'string') { if (obj.args.userallowedip == '') { obj.args.userallowedip = null; } else { obj.args.userallowedip = obj.args.userallowedip.split(','); } }
241 if (typeof obj.args.userblockedip == 'string') { if (obj.args.userblockedip == '') { obj.args.userblockedip = null; } else { obj.args.userblockedip = obj.args.userblockedip.split(','); } }
242 if (typeof obj.args.agentallowedip == 'string') { if (obj.args.agentallowedip == '') { obj.args.agentallowedip = null; } else { obj.args.agentallowedip = obj.args.agentallowedip.split(','); } }
meshmail.js
+11 -4
@@ -39,10 +39,10 @@ module.exports.CreateMeshMail = function (parent) {
39 // If the server hash many domains, just add the domainid to the file like this: 'account-check-customer1.html', 'mesh-invite-customer1.txt'.
40 obj.mailTemplates['account-check.html'] = '<title>[[[SERVERNAME]]] - Email Verification</title>\r\n<div style="font-family:Arial,Helvetica,sans-serif"><table style="background-color:#003366;color:lightgray;width:100%" cellpadding=8><tr><td><b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Verification</b></td></tr></table><p>Hi [[[USERNAME]]], <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> is requesting email verification, click on the following link to complete the process.</p><p style="margin-left:30px"><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]">Click here to verify your e-mail address.</a></p>If you did not initiate this request, please ignore this mail.</div>';
41 obj.mailTemplates['account-reset.html'] = '<title>[[[SERVERNAME]]] - Account Reset</title>\r\n<div style="font-family:Arial,Helvetica,sans-serif"><table style="background-color:#003366;color:lightgray;width:100%" cellpadding=8><tr><td><b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Verification</b></td></tr></table><p>Hi [[[USERNAME]]], <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> is requesting an account password reset, click on the following link to complete the process.</p><p style="margin-left:30px"><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]">Click here to reset your account password.</a></p>If you did not initiate this request, please ignore this mail.</div>';
42 - obj.mailTemplates['mesh-invite.html'] = '<title>[[[SERVERNAME]]] - Invitation</title>\r\n<div style="font-family:Arial,Helvetica,sans-serif"><table style="background-color:#003366;color:lightgray;width:100%" cellpadding=8><tr><td><b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Agent Installation</b></td></tr></table>[[[AREA-NAME]]]<p>Hello [[[NAME]]],</p>[[[/AREA-NAME]]]<p>User [[[USERNAME]]] on server <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> is requesting you to download the following software to start the remote control session.</p>[[[AREA-MSG]]]<p>Message: <b>[[[MSG]]]</b></p>[[[/AREA-MSG]]][[[AREA-WINDOWS]]]<p style="margin-left:30px"><a href="[[[SERVERURL]]]/meshagents?id=3&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Click here to download the MeshAgent for Windows.</a></p>[[[/AREA-WINDOWS]]][[[AREA-OSX]]]<p style="margin-left:30px"><a href="[[[SERVERURL]]]/meshagents?id=16&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Click here to download the MeshAgent for Apple OSX.</a></p>[[[/AREA-OSX]]][[[AREA-LINUX]]]<p>For Linux, cut & paste the following in a terminal to install the agent:<br /><pre style="margin-left:30px">wget -q [[[SERVERURL]]]/meshagents?script=1 --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] [[[MESHIDHEX]]]</pre></p>[[[/AREA-LINUX]]]<p>If you did not initiate this request, please ignore this mail.</p>Best regards,<br>[[[USERNAME]]]<br></div>';
42 + obj.mailTemplates['mesh-invite.html'] = '<title>[[[SERVERNAME]]] - Invitation</title>\r\n<div style="font-family:Arial,Helvetica,sans-serif"><table style="background-color:#003366;color:lightgray;width:100%" cellpadding=8><tr><td><b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Agent Installation</b></td></tr></table>[[[AREA-NAME]]]<p>Hello [[[NAME]]],</p>[[[/AREA-NAME]]]<p>User [[[USERNAME]]] on server <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> is requesting you to install software to start a remote control session.</p>[[[AREA-MSG]]]<p>Message: <b>[[[MSG]]]</b></p>[[[/AREA-MSG]]][[[AREA-WINDOWS]]]<p style="margin-left:30px"><a href="[[[SERVERURL]]]/meshagents?id=3&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Click here to download the MeshAgent for Windows.</a></p>[[[/AREA-WINDOWS]]][[[AREA-OSX]]]<p style="margin-left:30px"><a href="[[[SERVERURL]]]/meshagents?id=16&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Click here to download the MeshAgent for Apple OSX.</a></p>[[[/AREA-OSX]]][[[AREA-LINUX]]]<p>For Linux, cut & paste the following in a terminal to install the agent:<br /><pre style="margin-left:30px">wget -q [[[SERVERURL]]]/meshagents?script=1 --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] [[[MESHIDHEX]]]</pre></p>[[[/AREA-LINUX]]][[[AREA-LINK]]]<p>To install the software, <a href="[[[SERVERURL]]][[[LINKURL]]]">click here</a> and follow the instructions.</p>[[[/AREA-LINK]]]<p>If you did not initiate this request, please ignore this mail.</p>Best regards,<br>[[[USERNAME]]]<br></div>';
43 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';
44 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.';
45 - 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]]]';
45 + 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]]]';
46
47 // Load all of the mail templates if present
48 if (obj.parent.fs.existsSync(obj.parent.path.join(obj.parent.datapath, 'mail-templates'))) {
@@ -93,7 +93,7 @@ module.exports.CreateMeshMail = function (parent) {
93 return str.substring(0, si) + str.substring(ei + end.length);
94 }
95
96 - // Remove the string between two markers
96 + // Keep or remove the string between two markers
97 function strZone(str, marker, keep) {
98 marker = marker.toUpperCase();
99 if (keep) { return str.split('[[[AREA-' + marker + ']]]').join('').split('[[[/AREA-' + marker + ']]]').join(''); }
@@ -153,7 +153,7 @@ module.exports.CreateMeshMail = function (parent) {
153 };
154
155 // Send agent invite mail
156 - obj.sendAgentInviteMail = function (domain, username, email, meshid, name, os, msg, flags) {
156 + obj.sendAgentInviteMail = function (domain, username, email, meshid, name, os, msg, flags, expirehours) {
157 var template = getTemplateEx('mesh-invite', domain);
158 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.
159
@@ -162,6 +162,8 @@ module.exports.CreateMeshMail = function (parent) {
162 options.windows = ((os == 0) || (os == 1)) ? 1 : 0;
163 options.linux = ((os == 0) || (os == 2)) ? 1 : 0;
164 options.osx = ((os == 0) || (os == 3)) ? 1 : 0;
165 + options.link = (os == 4) ? 1 : 0;
166 + options.linkurl = createInviteLink(domain, meshid, flags, expirehours);
167
168 // Send the email
169 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) {
215 }
216 });
217
218 + // Create a agent invitation link
219 + function createInviteLink(domain, meshid, flags, expirehours) {
220 + return '/agentinvite?c=' + parent.encodeCookie({ a: 4, mid: meshid, f: flags, expire: expirehours * 60 }, parent.invitationLinkEncryptionKey);
221 + }
222 +
223 return obj;
224 };
\ No newline at end of file
meshuser.js
+1 -1
@@ -2103,7 +2103,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2103 //if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 4) == 0)) return;
2104
2105 // Perform email invitation
2106 - parent.parent.mailserver.sendAgentInviteMail(domain, user.name, command.email, command.meshid, command.name, command.os, command.msg, command.flags);
2106 + parent.parent.mailserver.sendAgentInviteMail(domain, user.name, command.email, command.meshid, command.name, command.os, command.msg, command.flags, command.expire);
2107 }
2108 break;
2109 }
views/default.handlebars
+7 -2
@@ -2710,7 +2710,10 @@
2710 x += "<div id=emailInviteDiv style=display:none>Invite someone to install the mesh agent. An email with be sent with the link to the mesh agent installation for the \"" + EscapeHtml(mesh.name) + "\" device group.<br /><br />";
2711 x += addHtmlValue('Name (optional)', '<input id=agentInviteName value="" style=width:230px maxlength=64 />');
2712 x += addHtmlValue('Email', '<input id=agentInviteEmail style=width:230px placeholder="example@email.com" onkeyup=validateAgentInvite()></input>');
2713 - x += addHtmlValue('Operating System', '<select id=agentInviteNameOs style=width:236px><option value=0>Any supported</option><option value=1>Windows only</option><option value=3>Apple MacOS only</option><option value=2>Linux only</option></select>');
2713 + x += addHtmlValue('Operating System', '<select id=agentInviteNameOs onchange=d2ChangedInviteType() style=width:236px><option value=4>Send installation link</option><option value=0 selected>Any supported</option><option value=1>Windows only</option><option value=3>Apple MacOS only</option><option value=2>Linux only</option></select>');
2714 + x += '<div id=d2agentexpirediv>';
2715 + x += addHtmlValue('Link Expiration', '<select id=agentInviteExpire style=width:236px><option value=1>1 hour</option><option value=8>8 hours</option><option value=24>1 day</option><option value=168>1 week</option><option value=5040>1 month</option><option value=0>Unlimited</option></select>');
2716 + x += '</div>';
2717 x += addHtmlValue('Installation Type', '<select id=agentInviteType style=width:236px><option value=0>Background and interactive</option><option value=2>Background only</option><option value=1>Interactive only</option></select>');
2718 x += addHtmlValue('Message<br />(optional)', '<textarea id=agentInviteMessage value="" style=width:230px;height:100px;resize:none maxlength=1024 /></textarea>');
2719 x += '</div>';
@@ -2719,6 +2722,7 @@
2722 x += addHtmlValue('Link Expiration', '<select id=d2inviteExpire style=width:236px onchange=d2RequestInvitationLink()><option value=1>1 hour</option><option value=8>8 hours</option><option value=24>1 day</option><option value=168>1 week</option><option value=5040>1 month</option><option value=0>Unlimited</option></select>');
2723 x += '<div id=agentInvitationLinkDiv style="text-align:center;font-size:large;margin:16px;display:none"><a id=agentInvitationLink target="_blank" href="" style=cursor:pointer></a> <img src=images/link4.png height=10 width=10 title="Copy link to clipboard" style=cursor:pointer onclick=d2CopyInviteToClip()></div></div>';
2724 setDialogMode(2, "Invite", 3, performAgentInvite, x, meshid);
2725 + d2ChangedInviteType();
2726 validateAgentInvite();
2727 d2RequestInvitationLink();
2728 }
@@ -2729,6 +2733,7 @@
2733
2734 function d2ChangedInviteType() {
2735 QV('urlInviteDiv', Q('d2InviteType').value == 0);
2736 + QV('d2agentexpirediv', Q('agentInviteNameOs').value == 4);
2737 if (features & 64) { QV('emailInviteDiv', Q('d2InviteType').value == 1); }
2738 validateAgentInvite();
2739 }
@@ -2747,7 +2752,7 @@
2752
2753 function performAgentInvite(button, meshid) {
2754 if ((features & 64) && (Q('d2InviteType').value == 1)) {
2750 - 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 });
2755 + 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) });
2756 }
2757 }
2758
views/login-mobile.handlebars
+17
@@ -72,6 +72,7 @@
72 <div id="newAccountDiv" style="display:none;padding:2px">
73 Don&#39;t have an account? <a onclick=xgo(2) style=cursor:pointer>Create one</a>.
74 </div>
75 + <input id=loginformargs name="urlargs" type="hidden" value="" />
76 </form>
77 </div>
78 <div id=createpanel style="display:none">
@@ -117,6 +118,7 @@
118 </tr>
119 </table>
120 <hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
121 + <input id=createformargs name="urlargs" type="hidden" value="" />
122 </form>
123 </div>
124 </div>
@@ -141,6 +143,7 @@
143 </tr>
144 </table>
145 <hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
146 + <input id=resetformargs name="urlargs" type="hidden" value="" />
147 </form>
148 </div>
149 <div id=tokenpanel style="background-color:#979797;border-radius:16px;width:260px;padding:16px;text-align:center;display:none;clear:both">
@@ -163,6 +166,7 @@
166 </tr>
167 </table>
168 <hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
169 + <input id=tokenformargs name="urlargs" type="hidden" value="" />
170 </form>
171 </div>
172
@@ -186,6 +190,7 @@
190 </tr>
191 </table>
192 <hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
193 + <input id=resettokenformargs name="urlargs" type="hidden" value="" />
194 </form>
195 </div>
196
@@ -216,6 +221,7 @@
221 </tr>
222 </table>
223 <hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
224 + <input id=resetpasswordformargs name="urlargs" type="hidden" value="" />
225 </form>
226 </div>
227
@@ -264,6 +270,17 @@
270 var hardwareKeyChallenge = decodeURIComponent('{{{hkey}}}');
271 var currentpanel = 0;
272
273 + // If URL arguments are provided, add them to form posts
274 + if (window.location.href.indexOf('?') > 0) {
275 + var urlargs = window.location.href.substring(window.location.href.indexOf('?'));
276 + Q('loginformargs').value = urlargs;
277 + Q('createformargs').value = urlargs;
278 + Q('resetformargs').value = urlargs;
279 + Q('tokenformargs').value = urlargs;
280 + Q('resettokenformargs').value = urlargs;
281 + Q('resetpasswordformargs').value = urlargs;
282 + }
283 +
284 function startup() {
285 if ((features & 32) == 0) {
286 // Guard against other site's top frames (web bugs).
views/login.handlebars
+17
@@ -69,6 +69,7 @@
69 <div id="newAccountDiv" style="display:none;padding:2px">
70 Don&#39;t have an account? <a onclick=xgo(2) style=cursor:pointer>Create one</a>.
71 </div>
72 + <input id=loginformargs name="urlargs" type="hidden" value="" />
73 </form>
74 </div>
75 <div id=createpanel style="display:none;position:relative">
@@ -113,6 +114,7 @@
114 </tr>
115 </table>
116 <hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
117 + <input id=createformargs name="urlargs" type="hidden" value="" />
118 </form>
119 </div>
120 <div id=resetpanel style="display:none">
@@ -136,6 +138,7 @@
138 </tr>
139 </table>
140 <hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
141 + <input id=resetformargs name="urlargs" type="hidden" value="" />
142 </form>
143 </div>
144 <div id=tokenpanel style="display:none">
@@ -158,6 +161,7 @@
161 </tr>
162 </table>
163 <hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
164 + <input id=tokenformargs name="urlargs" type="hidden" value="" />
165 </form>
166 </div>
167 <div id=resettokenpanel style="display:none">
@@ -180,6 +184,7 @@
184 </tr>
185 </table>
186 <hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
187 + <input id=resettokenformargs name="urlargs" type="hidden" value="" />
188 </form>
189 </div>
190 <div id=resetpasswordpanel style="display:none;position:relative">
@@ -209,6 +214,7 @@
214 </tr>
215 </table>
216 <hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
217 + <input id=resetpasswordformargs name="urlargs" type="hidden" value="" />
218 </form>
219 </div>
220 </td>
@@ -260,6 +266,17 @@
266 var webPageFullScreen = true;
267 var nightMode = (getstore('_nightMode', '0') == '1');
268
269 + // If URL arguments are provided, add them to form posts
270 + if (window.location.href.indexOf('?') > 0) {
271 + var urlargs = window.location.href.substring(window.location.href.indexOf('?'));
272 + Q('loginformargs').value = urlargs;
273 + Q('createformargs').value = urlargs;
274 + Q('resetformargs').value = urlargs;
275 + Q('tokenformargs').value = urlargs;
276 + Q('resettokenformargs').value = urlargs;
277 + Q('resetpasswordformargs').value = urlargs;
278 + }
279 +
280 //var webPageFullScreen = getstore('webPageFullScreen', true);
281 //if (webPageFullScreen == 'false') { webPageFullScreen = false; }
282 //if (webPageFullScreen == 'true') { webPageFullScreen = true; }
webserver.js
+42 -39
@@ -647,7 +647,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
647 req.session.loginmode = '4';
648 req.session.tokenusername = xusername;
649 req.session.tokenpassword = xpassword;
650 - res.redirect(domain.url);
650 + res.redirect(domain.url + getQueryPortion(req));
651 }, randomWaitTime);
652 } else {
653 // Login succesful
@@ -672,7 +672,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
672 } else {
673 delete req.session.passhint;
674 }
675 - res.redirect(domain.url);
675 + res.redirect(domain.url + getQueryPortion(req));
676 }, 2000 + (obj.crypto.randomBytes(2).readUInt16BE(0) % 4095)); // Wait for 2 to ~6 seconds.
677 }
678 });
@@ -686,7 +686,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
686 req.session.error = '<b style=color:#8C001A>Password change requested.</b>';
687 req.session.resettokenusername = xusername;
688 req.session.resettokenpassword = xpassword;
689 - res.redirect(domain.url);
689 + res.redirect(domain.url + getQueryPortion(req));
690 return;
691 }
692
@@ -726,12 +726,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
726 }
727 console.log("CurrentNode: " + req.session.currentNode);
728 // This redirect happens after finding node is completed
729 - res.redirect(domain.url);
729 + res.redirect(domain.url + getQueryPortion(req));
730 });
731 */
732 - res.redirect(domain.url); // Temporary
732 + res.redirect(domain.url + getQueryPortion(req)); // Temporary
733 } else {
734 - res.redirect(domain.url);
734 + res.redirect(domain.url + getQueryPortion(req));
735 }
736 //});
737 }
@@ -755,7 +755,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
755 if (i == -1) {
756 req.session.loginmode = '2';
757 req.session.error = '<b style=color:#8C001A>Unable to create account.</b>';
758 - res.redirect(domain.url);
758 + res.redirect(domain.url + getQueryPortion(req));
759 return;
760 }
761 var emailok = false, emaildomain = req.body.email.substring(i + 1).toLowerCase();
@@ -763,7 +763,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
763 if (emailok == false) {
764 req.session.loginmode = '2';
765 req.session.error = '<b style=color:#8C001A>Unable to create account.</b>';
766 - res.redirect(domain.url);
766 + res.redirect(domain.url + getQueryPortion(req));
767 return;
768 }
769 }
@@ -774,25 +774,25 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
774 req.session.loginmode = '2';
775 req.session.error = '<b style=color:#8C001A>Account limit reached.</b>';
776 console.log('max', req.session);
777 - res.redirect(domain.url);
777 + res.redirect(domain.url + getQueryPortion(req));
778 } else {
779 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)) {
780 req.session.loginmode = '2';
781 req.session.error = '<b style=color:#8C001A>Unable to create account.</b>';
782 - res.redirect(domain.url);
782 + res.redirect(domain.url + getQueryPortion(req));
783 } else {
784 // Check if this email was already verified
785 obj.db.GetUserWithVerifiedEmail(domain.id, req.body.email, function (err, docs) {
786 if (docs.length > 0) {
787 req.session.loginmode = '2';
788 req.session.error = '<b style=color:#8C001A>Existing account with this email address.</b>';
789 - res.redirect(domain.url);
789 + res.redirect(domain.url + getQueryPortion(req));
790 } else {
791 // Check if there is domain.newAccountToken, check if supplied token is valid
792 if ((domain.newaccountspass != null) && (domain.newaccountspass != '') && (req.body.anewaccountpass != domain.newaccountspass)) {
793 req.session.loginmode = '2';
794 req.session.error = '<b style=color:#8C001A>Invalid account creation token.</b>';
795 - res.redirect(domain.url);
795 + res.redirect(domain.url + getQueryPortion(req));
796 return;
797 }
798 // Check if user exists
@@ -822,7 +822,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
822 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.
823 obj.parent.DispatchEvent(['*', 'server-users'], obj, event);
824 }
825 - res.redirect(domain.url);
825 + res.redirect(domain.url + getQueryPortion(req));
826 }
827 });
828 }
@@ -845,7 +845,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
845 delete req.session.success;
846 delete req.session.error;
847 delete req.session.passhint;
848 - res.redirect(domain.url);
848 + res.redirect(domain.url + getQueryPortion(req));
849 return;
850 }
851
@@ -859,7 +859,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
859 if (!obj.common.checkPasswordRequirements(req.body.rpassword1, domain.passwordrequirements)) {
860 req.session.loginmode = '6';
861 req.session.error = '<b style=color:#8C001A>Password rejected, use a different one.</b>';
862 - res.redirect(domain.url);
862 + res.redirect(domain.url + getQueryPortion(req));
863 return;
864 }
865
@@ -869,7 +869,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
869 // This is the same password, request a password change again
870 req.session.loginmode = '6';
871 req.session.error = '<b style=color:#8C001A>Password rejected, use a different one.</b>';
872 - res.redirect(domain.url);
872 + res.redirect(domain.url + getQueryPortion(req));
873 } else {
874 // Update the password, use a different salt.
875 require('./pass').hash(req.body.rpassword1, function (err, salt, hash, tag) {
@@ -902,7 +902,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
902 delete req.session.success;
903 delete req.session.error;
904 delete req.session.passhint;
905 - res.redirect(domain.url);
905 + res.redirect(domain.url + getQueryPortion(req));
906 return;
907 }
908 });
@@ -921,13 +921,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
921 if (!email || checkEmail(email) == false) {
922 req.session.loginmode = '3';
923 req.session.error = '<b style=color:#8C001A>Invalid email.</b>';
924 - res.redirect(domain.url);
924 + res.redirect(domain.url + getQueryPortion(req));
925 } else {
926 obj.db.GetUserWithVerifiedEmail(domain.id, email, function (err, docs) {
927 if ((err != null) || (docs.length == 0)) {
928 req.session.loginmode = '3';
929 req.session.error = '<b style=color:#8C001A>Account not found.</b>';
930 - res.redirect(domain.url);
930 + res.redirect(domain.url + getQueryPortion(req));
931 } else {
932 // 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.
933 var responseSent = false;
@@ -942,7 +942,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
942 if ((req.body.token != null) || (req.body.hwtoken != null)) { req.session.error = '<b style=color:#8C001A>Invalid token, try again.</b>'; }
943 req.session.loginmode = '5';
944 req.session.tokenemail = email;
945 - res.redirect(domain.url);
945 + res.redirect(domain.url + getQueryPortion(req));
946 }
947 } else {
948 // Send email to perform recovery.
@@ -952,13 +952,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
952 if (i == 0) {
953 req.session.loginmode = '1';
954 req.session.error = '<b style=color:darkgreen>Hold on, reset mail sent.</b>';
955 - res.redirect(domain.url);
955 + res.redirect(domain.url + getQueryPortion(req));
956 }
957 } else {
958 if (i == 0) {
959 req.session.loginmode = '3';
960 req.session.error = '<b style=color:#8C001A>Unable to sent email.</b>';
961 - res.redirect(domain.url);
961 + res.redirect(domain.url + getQueryPortion(req));
962 }
963 }
964 }
@@ -970,13 +970,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
970 if (i == 0) {
971 req.session.loginmode = '1';
972 req.session.error = '<b style=color:darkgreen>Hold on, reset mail sent.</b>';
973 - res.redirect(domain.url);
973 + res.redirect(domain.url + getQueryPortion(req));
974 }
975 } else {
976 if (i == 0) {
977 req.session.loginmode = '3';
978 req.session.error = '<b style=color:#8C001A>Unable to sent email.</b>';
979 - res.redirect(domain.url);
979 + res.redirect(domain.url + getQueryPortion(req));
980 }
981 }
982 }
@@ -1107,7 +1107,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1107 if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) { res.sendStatus(404); return; }
1108
1109 // Check if the user is logged and we have all required parameters
1110 - 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; }
1110 + 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; }
1111 var user = obj.users[req.session.userid];
1112 if (!user) return;
1113
@@ -1138,10 +1138,10 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1138 obj.db.Remove(user._id);
1139 delete obj.users[user._id];
1140 req.session = null;
1141 - res.redirect(domain.url);
1141 + res.redirect(domain.url + getQueryPortion(req));
1142 obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'accountremove', msg: 'Account removed', domain: domain.id });
1143 } else {
1144 - res.redirect(domain.url);
1144 + res.redirect(domain.url + getQueryPortion(req));
1145 }
1146 });
1147 }
@@ -1178,11 +1178,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1178 if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) { res.sendStatus(404); return; }
1179
1180 // Check if the user is logged and we have all required parameters
1181 - 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; }
1181 + 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; }
1182
1183 // Get the current user
1184 var user = obj.users[req.session.userid];
1185 - if (!user) { res.redirect(domain.url); return; }
1185 + if (!user) { res.redirect(domain.url + getQueryPortion(req)); return; }
1186
1187 // Check old password
1188 obj.checkUserPassword(domain, user, req.body.apassword0, function (result) {
@@ -1197,7 +1197,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1197 delete user.passtype;
1198 obj.db.SetUser(user);
1199 req.session.viewmode = 2;
1200 - res.redirect(domain.url);
1200 + res.redirect(domain.url + getQueryPortion(req));
1201 obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'passchange', msg: 'Account password changed: ' + user.name, domain: domain.id });
1202 }, 0);
1203 }
@@ -1232,7 +1232,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1232 res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' });
1233
1234 // Check if we have an incomplete domain name in the path
1235 - if ((domain.id != '') && (domain.dns == null) && (req.url.split('/').length == 2)) { res.redirect(domain.url); return; }
1235 + if ((domain.id != '') && (domain.dns == null) && (req.url.split('/').length == 2)) { res.redirect(domain.url + getQueryPortion(req)); return; }
1236
1237 if (obj.args.nousers == true) {
1238 // If in single user mode, setup things here.
@@ -1300,7 +1300,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1300 // If a user exists and is logged in, serve the default app, otherwise server the login app.
1301 if (req.session && req.session.userid && obj.users[req.session.userid]) {
1302 var user = obj.users[req.session.userid];
1303 - if (req.session.domainid != domain.id) { req.session = null; res.redirect(domain.url); return; } // Check is the session is for the correct domain
1303 + 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
1304
1305 // Check if this is a locked account
1306 if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) {
@@ -1310,7 +1310,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1310 delete req.session.currentNode;
1311 delete req.session.passhint;
1312 req.session.error = '<b style=color:#8C001A>Account locked.</b>';
1313 - res.redirect(domain.url);
1313 + res.redirect(domain.url + getQueryPortion(req));
1314 return;
1315 }
1316
@@ -1393,14 +1393,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1393 obj.db.GetUserWithVerifiedEmail(domain.id, req.session.tokenemail, function (err, docs) {
1394 if ((err != null) || (docs.length == 0)) {
1395 req.session = null;
1396 - res.redirect(domain.url);
1396 + res.redirect(domain.url + getQueryPortion(req));
1397 } else {
1398 var user = obj.users[docs[0]._id];
1399 if (user != null) {
1400 getHardwareKeyChallenge(req, domain, user, function (hwchallenge) { handleRootRequestLogin(req, res, domain, hwchallenge, passRequirements); });
1401 } else {
1402 req.session = null;
1403 - res.redirect(domain.url);
1403 + res.redirect(domain.url + getQueryPortion(req));
1404 }
1405 }
1406 });
@@ -1486,7 +1486,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1486 // Send the terms from the database
1487 res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' });
1488 if (req.session && req.session.userid) {
1489 - if (req.session.domainid != domain.id) { req.session = null; res.redirect(domain.url); return; } // Check is the session is for the correct domain
1489 + 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
1490 var user = obj.users[req.session.userid];
1491 var logoutcontrol = 'Welcome ' + user.name + '.';
1492 if ((domain.ldap == null) && (domain.sspi == null) && (obj.args.user == null) && (obj.args.nousers != true)) { logoutcontrol += ' <a href=' + domain.url + 'logout?' + Math.random() + ' style=color:white>Logout</a>'; } // 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) {
1504 // Send the terms from terms.txt
1505 res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' });
1506 if (req.session && req.session.userid) {
1507 - if (req.session.domainid != domain.id) { req.session = null; res.redirect(domain.url); return; } // Check is the session is for the correct domain
1507 + 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
1508 var user = obj.users[req.session.userid];
1509 var logoutcontrol = 'Welcome ' + user.name + '.';
1510 if ((domain.ldap == null) && (domain.sspi == null) && (obj.args.user == null) && (obj.args.nousers != true)) { logoutcontrol += ' <a href=' + domain.url + 'logout?' + Math.random() + ' style=color:white>Logout</a>'; } // 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) {
1517 // Send the default terms
1518 res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' });
1519 if (req.session && req.session.userid) {
1520 - if (req.session.domainid != domain.id) { req.session = null; res.redirect(domain.url); return; } // Check is the session is for the correct domain
1520 + 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
1521 var user = obj.users[req.session.userid];
1522 var logoutcontrol = 'Welcome ' + user.name + '.';
1523 if ((domain.ldap == null) && (domain.sspi == null) && (obj.args.user == null) && (obj.args.nousers != true)) { logoutcontrol += ' <a href=' + domain.url + 'logout?' + Math.random() + ' style=color:white>Logout</a>'; } // 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) {
1721 if (splitUrl.length > 1) { urlArgs = '?' + splitUrl[1]; }
1722 if ((splitUrl.length > 0) && (splitUrl[0].length > 1)) { urlName = splitUrl[0].substring(1).toLowerCase(); }
1723 if ((urlName == null) || (domain.redirects[urlName] == null)) { res.sendStatus(404); return; }
1724 - res.redirect(domain.redirects[urlName] + urlArgs);
1724 + res.redirect(domain.redirects[urlName] + urlArgs + getQueryPortion(req));
1725 }
1726
1727 // 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) {
3150 return (req.headers['user-agent'].toLowerCase().indexOf('mobile') >= 0);
3151 }
3152
3153 + // Return the query string portion of the URL, the ? and anything after.
3154 + 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); }
3155 +
3156 return obj;
3157 };
\ No newline at end of file