diff --git a/agents/MeshCentralRouter.exe b/agents/MeshCentralRouter.exe new file mode 100644 index 00000000..b0cb1cc0 Binary files /dev/null and b/agents/MeshCentralRouter.exe differ diff --git a/meshuser.js b/meshuser.js index cee03759..2d6a63eb 100644 --- a/meshuser.js +++ b/meshuser.js @@ -288,6 +288,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use try { ws.send(JSON.stringify({ action: 'authcookie', cookie: parent.parent.encodeCookie({ userid: user._id, domainid: domain.id }, parent.parent.loginCookieEncryptionKey) })); } catch (ex) { } break; } + case 'logincookie': + { + // Return a login cookie + try { ws.send(JSON.stringify({ action: 'logincookie', cookie: parent.parent.encodeCookie({ u: user._id, a: 3 }, parent.parent.loginCookieEncryptionKey) })); } catch (ex) { } + break; + } case 'servertimelinestats': { if ((user.siteadmin & 21) == 0) return; // Only site administrators with "site backup" or "site restore" or "site update" permissions can use this. diff --git a/package.json b/package.json index a40bffc8..293f1248 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.3.2-v", + "version": "0.3.2-y", "keywords": [ "Remote Management", "Intel AMT", diff --git a/sample-config.json b/sample-config.json index 229724f5..660ff887 100644 --- a/sample-config.json +++ b/sample-config.json @@ -45,7 +45,7 @@ "_TitlePicture": "title-sample.png", "_UserQuota": 1048576, "_MeshQuota": 248576, - "NewAccounts": 1, + "_NewAccounts": true, "_NewAccountEmailDomains": [ "sample.com" ], "Footer": "Twitter", "_CertUrl": "https://192.168.2.106:443/", diff --git a/views/default-min.handlebars b/views/default-min.handlebars index fe4aaac0..39c65e9f 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 545702e5..9f21b2bd 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -2294,8 +2294,13 @@ // Add a "Add Device Group" option r += '
'; - if ((view < 3) && (sort == 0) && (meshcount > 0) && ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 64) == 0))) { r += 'Add Device Group '; } - if ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 128) == 0)) { r += 'MeshCmd
'; } + if ((view < 3) && (sort == 0) && (meshcount > 0) && ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 64) == 0))) { + r += 'Add Device Group '; + } + if ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 128) == 0)) { + r += 'MeshCmd '; + if (navigator.platform.toLowerCase() == 'win32') { r += 'Router '; } + } r += '
'; QH('xdevices', r); @@ -4134,8 +4139,16 @@ setDialogMode(2, "Network Interfaces", 1, null, "
Loading...
", 'if' + currentNode._id ); meshserver.send({ action: 'getnetworkinfo', nodeid: currentNode._id }); } + + // Show MeshCentral Router dialog + function p10showMeshRouterDialog() { + if (xxdialogMode) return; + var x = "
MeshCentral Router is a Windows tool for TCP port mapping. You can, for example, RDP into a remote device thru this server.

"; + x += addHtmlValue('Win32 Executable', 'MeshCentralRouter.exe'); + setDialogMode(2, "MeshCentral Router", 1, null, x, "fileDownload"); + } - // Show router dialog + // Show MeshCmd dialog function p10showMeshCmdDialog(mode, nodeid) { if (xxdialogMode) return; var y = " Password:
\ No newline at end of file + {{{title}}} - Login
{{{title}}}
{{{title2}}}

Welcome


\ No newline at end of file diff --git a/views/login-mobile-min.handlebars b/views/login-mobile-min.handlebars index 68c45914..7b68add9 100644 --- a/views/login-mobile-min.handlebars +++ b/views/login-mobile-min.handlebars @@ -1 +1 @@ - MeshCentral - Login
{{{title}}}
{{{title2}}}
\ No newline at end of file + MeshCentral - Login
{{{title}}}
{{{title2}}}
\ No newline at end of file diff --git a/views/login-mobile.handlebars b/views/login-mobile.handlebars index 0506f220..6db853ad 100644 --- a/views/login-mobile.handlebars +++ b/views/login-mobile.handlebars @@ -279,7 +279,7 @@ validateLogin(); validateCreate(); if ('{{loginmode}}' != '') { go(parseInt('{{loginmode}}')); } else { go(1); } - QV('newAccountDiv', ('{{{newAccount}}}' != '0') && ('{{{newAccount}}}' != 'false')); // If new accounts are not allowed, don't display the new account link. + QV('newAccountDiv', ('{{{newAccount}}}' === '1') || ('{{{newAccount}}}' === 'true')); // If new accounts are not allowed, don't display the new account link. if ((passRequirements.hint === true) && (passhint != null) && (passhint.length > 0)) { QV("showPassHintLink", true); } QV("newAccountPass", (newAccountPass == 1)); QV("resetAccountDiv", (emailCheck == true)); diff --git a/views/login.handlebars b/views/login.handlebars index 34ed962d..addef315 100644 --- a/views/login.handlebars +++ b/views/login.handlebars @@ -282,7 +282,7 @@ validateLogin(); validateCreate(); if ('{{loginmode}}' != '') { go(parseInt('{{loginmode}}')); } else { go(1); } - QV('newAccountDiv', ('{{{newAccount}}}' != '0') && ('{{{newAccount}}}' != 'false')); // If new accounts are not allowed, don't display the new account link. + QV('newAccountDiv', ('{{{newAccount}}}' === '1') || ('{{{newAccount}}}' === 'true')); // If new accounts are not allowed, don't display the new account link. if ((passhint != null) && (passhint.length > 0)) { QV("showPassHintLink", true); } QV("newAccountPass", (newAccountPass == 1)); QV("resetAccountDiv", (emailCheck == true)); diff --git a/views/messenger-min.handlebars b/views/messenger-min.handlebars index b61cf01a..31fded6e 100644 --- a/views/messenger-min.handlebars +++ b/views/messenger-min.handlebars @@ -1 +1 @@ - MeshMessenger
MeshMessenger
\ No newline at end of file + MeshMessenger
MeshMessenger
\ No newline at end of file diff --git a/webserver.js b/webserver.js index d81a3e8a..0f8b2481 100644 --- a/webserver.js +++ b/webserver.js @@ -198,7 +198,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { for (i in parent.config.domains) { if (domainUserCount[i] == 0) { // If newaccounts is set to no new accounts, but no accounts exists, temporarly allow account creation. - if ((parent.config.domains[i].newaccounts === 0) || (parent.config.domains[i].newaccounts === false)) { parent.config.domains[i].newaccounts = 2; } + //if ((parent.config.domains[i].newaccounts === 0) || (parent.config.domains[i].newaccounts === false)) { parent.config.domains[i].newaccounts = 2; } console.log('Server ' + ((i == '') ? '' : (i + ' ')) + 'has no users, next new account will be site administrator.'); } } @@ -252,7 +252,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { var user = { type: 'user', _id: userid, name: username, creation: Math.floor(Date.now() / 1000), login: Math.floor(Date.now() / 1000), domain: domain.id }; var usercount = 0; for (var i in obj.users) { if (obj.users[i].domain == domain.id) { usercount++; } } - if (usercount == 0) { user.siteadmin = 0xFFFFFFFF; if (domain.newaccounts === 2) { domain.newaccounts = 0; } } // If this is the first user, give the account site admin. + if (usercount == 0) { user.siteadmin = 0xFFFFFFFF; /*if (domain.newaccounts === 2) { delete domain.newaccounts; }*/ } // If this is the first user, give the account site admin. obj.users[user._id] = user; obj.db.SetUser(user); obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', userid: userid, username: username, account: obj.CloneSafeUser(user), action: 'accountcreate', msg: 'Account created, name is ' + name, domain: domain.id }); @@ -303,7 +303,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { var user = { type: 'user', _id: userid, name: shortname, creation: Math.floor(Date.now() / 1000), login: Math.floor(Date.now() / 1000), domain: domain.id }; var usercount = 0; for (var i in obj.users) { if (obj.users[i].domain == domain.id) { usercount++; } } - if (usercount == 0) { user.siteadmin = 0xFFFFFFFF; if (domain.newaccounts === 2) { domain.newaccounts = 0; } } // If this is the first user, give the account site admin. + if (usercount == 0) { user.siteadmin = 0xFFFFFFFF; /*if (domain.newaccounts === 2) { delete domain.newaccounts; }*/ } // If this is the first user, give the account site admin. obj.users[user._id] = user; obj.db.SetUser(user); obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, account: obj.CloneSafeUser(user), action: 'accountcreate', msg: 'Account created, name is ' + name, domain: domain.id }); @@ -721,7 +721,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { const domain = checkUserIpAddress(req, res); if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) { res.sendStatus(401); return; } - if ((domain.newaccounts === 0) || (domain.newaccounts === false)) { res.sendStatus(401); return; } + // Check if we are allowed to create new users using the login screen + var domainUserCount = -1; + if ((domain.newaccounts !== 1) && (domain.newaccounts !== true)) { + domainUserCount = 0; + for (var i in obj.users) { if (obj.users[i].domain == domain.id) { domainUserCount++; } } + if (domainUserCount > 0) { res.sendStatus(401); return; } + } // Check if this request is for an allows email domain if ((domain.newaccountemaildomains != null) && Array.isArray(domain.newaccountemaildomains)) { @@ -777,9 +783,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { } else { var user = { type: 'user', _id: 'user/' + domain.id + '/' + req.body.username.toLowerCase(), name: req.body.username, email: req.body.email, creation: Math.floor(Date.now() / 1000), login: Math.floor(Date.now() / 1000), domain: domain.id }; if ((domain.passwordrequirements != null) && (domain.passwordrequirements.hint === true) && (req.body.apasswordhint)) { var hint = req.body.apasswordhint; if (hint.length > 250) { hint = hint.substring(0, 250); } user.passhint = hint; } - var usercount = 0; - for (var i in obj.users) { if (obj.users[i].domain == domain.id) { usercount++; } } - if (usercount == 0) { user.siteadmin = 0xFFFFFFFF; if (domain.newaccounts === 2) { domain.newaccounts = 0; } } // If this is the first user, give the account site admin. + if (domainUserCount == 0) { user.siteadmin = 0xFFFFFFFF; /*if (domain.newaccounts === 2) { delete domain.newaccounts; }*/ } // If this is the first user, give the account site admin. obj.users[user._id] = user; req.session.userid = user._id; req.session.domainid = domain.id; @@ -1354,21 +1358,25 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (msg != null) message = '

' + msg + '

'; var emailcheck = ((obj.parent.mailserver != null) && (domain.auth != 'sspi')); + // Check if we are allowed to create new users using the login screen + var newAccountsAllowed = true; + if ((domain.newaccounts !== 1) && (domain.newaccounts !== true)) { for (var i in obj.users) { if (obj.users[i].domain == domain.id) { newAccountsAllowed = false; break; } } } + if (obj.args.minify && !req.query.nominify) { // Try to server the minified version if we can. try { - res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile-min' : 'login-min'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: encodeURIComponent(hardwareKeyChallenge), message: message, passhint: passhint, welcometext: domain.welcometext?encodeURIComponent(domain.welcometext):null }); + res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile-min' : 'login-min'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: newAccountsAllowed, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: encodeURIComponent(hardwareKeyChallenge), message: message, passhint: passhint, welcometext: domain.welcometext?encodeURIComponent(domain.welcometext):null }); } catch (ex) { // In case of an exception, serve the non-minified version. - res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile' : 'login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: encodeURIComponent(hardwareKeyChallenge), message: message, passhint: passhint, welcometext: domain.welcometext ? encodeURIComponent(domain.welcometext) : null }); + res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile' : 'login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: newAccountsAllowed, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: encodeURIComponent(hardwareKeyChallenge), message: message, passhint: passhint, welcometext: domain.welcometext ? encodeURIComponent(domain.welcometext) : null }); } } else { // Serve non-minified version of web pages. - res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile' : 'login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: encodeURIComponent(hardwareKeyChallenge), message: message, passhint: passhint, welcometext: domain.welcometext ? encodeURIComponent(domain.welcometext) : null }); + res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile' : 'login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: newAccountsAllowed, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: encodeURIComponent(hardwareKeyChallenge), message: message, passhint: passhint, welcometext: domain.welcometext ? encodeURIComponent(domain.welcometext) : null }); } /* - var xoptions = { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: obj.parent.mailserver != null, features: features, footer: (domain.footer == null) ? '' : domain.footer }; + var xoptions = { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: newAccountsAllowed, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: obj.parent.mailserver != null, features: features, footer: (domain.footer == null) ? '' : domain.footer }; var xpath = obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile' : 'login'); console.log('Render...'); res.render(xpath, xoptions, function (err, html) { @@ -2331,6 +2339,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (obj.args.lanonly != true) { meshaction.serverUrl = ((obj.args.notls == true) ? 'ws://' : 'wss://') + obj.getWebServerName(domain) + ':' + httpsPort + '/' + ((domain.id == '') ? '' : ('/' + domain.id)) + 'meshrelay.ashx'; } res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'text/plain', 'Content-Disposition': 'attachment; filename=meshaction.txt' }); res.send(JSON.stringify(meshaction, null, ' ')); + } else if (req.query.meshaction == 'winrouter') { + var p = obj.path.join(__dirname, 'agents', 'MeshCentralRouter.exe'); + if (obj.fs.existsSync(p)) { + res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'text/plain', 'Content-Disposition': 'attachment; filename=MeshCentralRouter.exe' }); + try { res.sendFile(p); } catch (e) { res.sendStatus(404); } + } else { res.sendStatus(404); } } else { res.sendStatus(401); }