From 7ac54cadf561b79c8af4be09f07f0f713bfb5186 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Mon, 22 Jul 2019 16:00:43 -0700 Subject: [PATCH] Added new account invitation system. --- meshmail.js | 16 ++++++++++++++++ meshuser.js | 11 ++++++++++- package.json | 2 +- views/default-min.handlebars | 2 +- views/default.handlebars | 29 +++++++++++++++++++++++------ 5 files changed, 51 insertions(+), 9 deletions(-) diff --git a/meshmail.js b/meshmail.js index 02806410..d4580663 100644 --- a/meshmail.js +++ b/meshmail.js @@ -39,9 +39,12 @@ module.exports.CreateMeshMail = function (parent) { // Set default mail templates // You can override these by placing a file with the same name in "meshcentral-data/mail" // 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-invite.html'] = '[[[SERVERNAME]]] - Account Invitation\r\n
[[[SERVERNAME]]] - Account Invitation

An account was created for you on server [[[SERVERNAME]]], you can access it now with:

   Username: [[[ACCOUNTNAME]]]
   Password: [[[PASSWORD]]]

Best regards,
[[[USERNAME]]]
'; 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 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-invite.txt'] = '[[[SERVERNAME]]] - Account Invitation\r\nAn account was created for you on server [[[SERVERNAME]]] ([[[SERVERURL]]]/), you can access it now with username \"[[[ACCOUNTNAME]]]\" and password \"[[[PASSWORD]]]\".\r\n\r\nBest regards,\r\n[[[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 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]]]'; @@ -126,6 +129,19 @@ module.exports.CreateMeshMail = function (parent) { sendNextMail(); }; + // Send account invitation mail + obj.sendAccountInviteMail = function (domain, username, accountname, email, password) { + var template = getTemplateEx('account-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, invitation not possible. + + // Set all the options. + var options = { username: username, accountname: accountname, email: email, servername: domain.title, password: password }; + + // 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) }); + sendNextMail(); + }; + // Send account check mail obj.sendAccountCheckMail = function (domain, username, email) { var template = getTemplateEx('account-check', domain); diff --git a/meshuser.js b/meshuser.js index e26b071a..1d66b12c 100644 --- a/meshuser.js +++ b/meshuser.js @@ -1139,6 +1139,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // If the email is the username, set this here. if (domain.usernameisemail) { if (command.email) { command.username = command.email; } else { command.email = command.username; } } + // Randomize the password if needed + if (command.randomPassword === true) { command.pass = getRandomPassword(); } + // Add a new user account var err = null, newusername, newuserid; try { @@ -1147,7 +1150,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use else if (common.validateUsername(command.username, 1, 256) == false) { err = 'Invalid username'; } // Username is between 1 and 64 characters, no spaces else if (common.validateString(command.pass, 1, 256) == false) { err = 'Invalid password'; } // Password is between 1 and 256 characters else if (command.username.indexOf('/') >= 0) { err = 'Invalid username'; } // Usernames can't have '/' - else if (common.checkPasswordRequirements(command.pass, domain.passwordrequirements) == false) { err = 'Invalid password'; } // Password does not meet requirements + else if ((command.randomPassword !== true) && (common.checkPasswordRequirements(command.pass, domain.passwordrequirements) == false)) { err = 'Invalid password'; } // Password does not meet requirements else if ((command.email != null) && (common.validateEmail(command.email, 1, 1024) == false)) { err = 'Invalid email'; } // Check if this is a valid email address else { newusername = command.username; @@ -1210,6 +1213,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if (parent.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to create the user. Another event will come. parent.parent.DispatchEvent(targets, obj, event); + // Perform email invitation + if ((command.emailInvitation == true) && (command.emailVerified == true) && command.email && parent.parent.mailserver) { + parent.parent.mailserver.sendAccountInviteMail(domain, user.name, newusername, command.email.toLowerCase(), command.pass); + } + // OK Response if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'adduser', responseid: command.responseid, result: 'ok' })); } catch (ex) { } } } else { @@ -2857,6 +2865,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Clean a IPv6 address that encodes a IPv4 address function cleanRemoteAddr(addr) { if (addr.startsWith('::ffff:')) { return addr.substring(7); } else { return addr; } } + function getRandomPassword() { return Buffer.from(parent.crypto.randomBytes(9), 'binary').toString('base64').split('/').join('@'); } return obj; }; \ No newline at end of file diff --git a/package.json b/package.json index 24fbcb18..dd2675dc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.3.8-h", + "version": "0.3.8-i", "keywords": [ "Remote Management", "Intel AMT", diff --git a/views/default-min.handlebars b/views/default-min.handlebars index 64666d70..13b72073 100644 --- a/views/default-min.handlebars +++ b/views/default-min.handlebars @@ -1 +1 @@ - {{{title}}}
{{{title}}}
{{{title2}}}

{{{logoutControl}}}

 

\ No newline at end of file + {{{title}}}
{{{title}}}
{{{title2}}}

{{{logoutControl}}}

 

\ No newline at end of file diff --git a/views/default.handlebars b/views/default.handlebars index 99994c7c..c1ab3031 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -7365,8 +7365,12 @@ x += addHtmlValue('Email', ''); x += addHtmlValue('Password', ''); x += addHtmlValue('Password', ''); - x += '
Force password reset on next login.
'; - if (serverinfo.emailcheck) { x += '
Email is verified.
'; } + x += '
Randomize the password.
'; + x += '
Force password reset on next login.
'; + if (serverinfo.emailcheck) { + x += '
Email is verified.
'; + x += '
Send invitation email.
'; + } if (passRequirements) { var r = [], rc = 0; @@ -7380,18 +7384,31 @@ } function showCreateNewAccountDialogValidate(x) { - if ((x == null) && (Q('p4email').value.length > 0) && (validateEmail(Q('p4email').value)) == false) { QE('idx_dlgOkButton', false); return; } + var ve = validateEmail(Q('p4email').value); + if (serverinfo.emailcheck) { + QE('p4verifiedEmail', ve); + QE('p4invitationEmail', ve && Q('p4resetNextLogin').checked && Q('p4verifiedEmail').checked); + if (ve == false) { Q('p4verifiedEmail').checked = false; } + if ((Q('p4resetNextLogin').checked == false) || (Q('p4verifiedEmail').checked == false)) { Q('p4invitationEmail').checked = false; } + } + QE('p4pass1', !Q('p4randomPassword').checked); + QE('p4pass2', !Q('p4randomPassword').checked); + + if ((x == null) && (Q('p4email').value.length > 0) && (ve == false)) { QE('idx_dlgOkButton', false); return; } var ok = true; if ((features & 0x200000) == 0) { ok &= (!Q('p4name') || ((Q('p4name').value.length > 0) && (Q('p4name').value.indexOf(' ') == -1))); } - ok &= (Q('p4pass1').value.length > 0 && Q('p4pass1').value == Q('p4pass2').value && checkPasswordRequirements(Q('p4pass1').value, passRequirements)); + if (Q('p4randomPassword').checked == false) { ok &= (Q('p4pass1').value.length > 0 && Q('p4pass1').value == Q('p4pass2').value && checkPasswordRequirements(Q('p4pass1').value, passRequirements)); } if (ok && passRequirements) { if (checkPasswordRequirements(Q('p4pass1').value, passRequirements) == false) { ok = false; } } QE('idx_dlgOkButton', ok); } function showCreateNewAccountDialogEx() { var username = ((features & 0x200000) == 0) ? Q('p4name').value : Q('p4email').value; - var x = { action: 'adduser', username: username, email: Q('p4email').value, pass: Q('p4pass1').value, resetNextLogin: Q('p4resetNextLogin').checked }; - if (serverinfo.emailcheck) { x.emailVerified = Q('p4verifiedEmail').checked; } + var x = { action: 'adduser', username: username, email: Q('p4email').value, pass: Q('p4pass1').value, resetNextLogin: Q('p4resetNextLogin').checked, randomPassword: Q('p4randomPassword').checked }; + if (serverinfo.emailcheck) { + x.emailVerified = Q('p4verifiedEmail').checked; + x.emailInvitation = Q('p4invitationEmail').checked; + } meshserver.send(x); }