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}}}
My Devices My Account My Events My Files My Users My Server
General Desktop Terminal Files Events Intel® AMT Console
Server disconnected , click to reconnect .
My Devices No device groups.
My Account Device Groups ( New )
My Files These files are shared publicly, click "link" to get public url.
✓
✗
My Server Server Statistics
Intel® AMT Redirection port or KVM feature is disabled, click here to enable it.
Remote computer is not powered on, click here to issue a power command.
Intel® AMT Redirection port or KVM feature is disabled, click here to enable it.
Remote computer is not powered on, click here to issue a power command.
General -
File Selection
Local file upload Server file selection Agent Remote Desktop Scaling
100% 87.5% 75% 62.5% 50% 37.5% 25% 12.5% Frame rate
Fast Medium Slow Very slow Intel® AMT Hardware KVM Image Encoding
RLE8, Fastest RLE16, Recommended RAW8, Slow RAW16, Very Slow
\ No newline at end of file
+ {{{title}}}
My Devices My Account My Events My Files My Users My Server
General Desktop Terminal Files Events Intel® AMT Console
Server disconnected , click to reconnect .
My Devices No device groups.
My Account Device Groups ( New )
My Files These files are shared publicly, click "link" to get public url.
✓
✗
My Server Server Statistics
Intel® AMT Redirection port or KVM feature is disabled, click here to enable it.
Remote computer is not powered on, click here to issue a power command.
Intel® AMT Redirection port or KVM feature is disabled, click here to enable it.
Remote computer is not powered on, click here to issue a power command.
General -
File Selection
Local file upload Server file selection Agent Remote Desktop Scaling
100% 87.5% 75% 62.5% 50% 37.5% 25% 12.5% Frame rate
Fast Medium Slow Very slow Intel® AMT Hardware KVM Image Encoding
RLE8, Fastest RLE16, Recommended RAW8, Slow RAW16, Very Slow
\ 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 = "";
diff --git a/views/login-min.handlebars b/views/login-min.handlebars
index e7498489..c0da3dc5 100644
--- a/views/login-min.handlebars
+++ b/views/login-min.handlebars
@@ -1 +1 @@
- {{{title}}} - Login
Welcome Connect to your home or office devices from anywhere in the world using
MeshCentral , the real time, open source remote monitoring and management web site. You will need to download and install a management agent on your computers. Once installed, computers will show up in the "My Devices" section of this web site and you will be able to monitor them and take control of them.
\ No newline at end of file
+ {{{title}}} - Login
Welcome Connect to your home or office devices from anywhere in the world using
MeshCentral , the real time, open source remote monitoring and management web site. You will need to download and install a management agent on your computers. Once installed, computers will show up in the "My Devices" section of this web site and you will be able to monitor them and take control of them.
\ 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
\ No newline at end of file
+ MeshCentral - Login
\ 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
\ No newline at end of file
+ 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);
}