Added public agent install invitation link support.
Ylian Saint-Hilaire committed
Jun 3, 2019 at 13:15 UTC
b676ab7e163ab5464c297d90c1b53f09317b0461
13 files changed
+408
-20
MeshCentralServer.njsproj
+1
@@ -260,6 +260,7 @@
260
<Content Include="readme.txt" />
261
<Content Include="sample-config.json" />
262
<Content Include="SourceFileList.txt" />
263
+ <Content Include="views\agentinvite.handlebars" />
264
<Content Include="views\default-min.handlebars" />
265
<Content Include="views\default-mobile-min.handlebars" />
266
<Content Include="views\default-mobile.handlebars" />
agents/MeshService-signed.exe
Binary files a/agents/MeshService-signed.exe and b/agents/MeshService-signed.exe differ
agents/MeshService.exe
Binary files a/agents/MeshService.exe and b/agents/MeshService.exe differ
agents/MeshService64-signed.exe
Binary files a/agents/MeshService64-signed.exe and b/agents/MeshService64-signed.exe differ
agents/MeshService64.exe
Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ
meshcentral.js
+16
-4
@@ -1549,8 +1549,14 @@ function CreateMeshCentralServer(config, args) {
1549
if ((o.time == null) || (o.time == null) || (typeof o.time != 'number')) { obj.debug(1, 'ERR: Bad cookie due to invalid time'); return null; }
1550
o.time = o.time * 1000; // Decode the cookie creation time
1551
o.dtime = Date.now() - o.time; // Decode how long ago the cookie was created (in milliseconds)
1552
- if (timeout == null) { timeout = 2; }
1553
- if ((o.dtime > (timeout * 60000)) || (o.dtime < -30000)) { obj.debug(1, 'ERR: Bad cookie due to timeout'); return null; } // The cookie is only valid 120 seconds, or 30 seconds back in time (in case other server's clock is not quite right)
1552
+ if ((o.expire) == null || (typeof o.expire != 'number')) {
1553
+ // Use a fixed cookie expire time
1554
+ if (timeout == null) { timeout = 2; }
1555
+ if ((o.dtime > (timeout * 60000)) || (o.dtime < -30000)) { obj.debug(1, 'ERR: Bad cookie due to timeout'); return null; } // The cookie is only valid 120 seconds, or 30 seconds back in time (in case other server's clock is not quite right)
1556
+ } else {
1557
+ // An expire time is included in the cookie (in minutes), use this.
1558
+ if ((o.dtime > (o.expire * 60000)) || (o.dtime < -30000)) { obj.debug(1, 'ERR: Bad cookie due to timeout'); return null; } // The cookie is only valid 120 seconds, or 30 seconds back in time (in case other server's clock is not quite right)
1559
+ }
1560
return o;
1561
} catch (ex) { obj.debug(1, 'ERR: Bad AESGCM cookie due to exception: ' + ex); return null; }
1562
};
@@ -1571,8 +1577,14 @@ function CreateMeshCentralServer(config, args) {
1577
if ((o.time == null) || (o.time == null) || (typeof o.time != 'number')) { obj.debug(1, 'ERR: Bad cookie due to invalid time'); return null; }
1578
o.time = o.time * 1000; // Decode the cookie creation time
1579
o.dtime = Date.now() - o.time; // Decode how long ago the cookie was created (in milliseconds)
1574
- if (timeout == null) { timeout = 2; }
1575
- if ((o.dtime > (timeout * 60000)) || (o.dtime < -30000)) { obj.debug(1, 'ERR: Bad cookie due to timeout'); return null; } // The cookie is only valid 120 seconds, or 30 seconds back in time (in case other server's clock is not quite right)
1580
+ if ((o.expire) == null || (typeof o.expire != 'number')) {
1581
+ // Use a fixed cookie expire time
1582
+ if (timeout == null) { timeout = 2; }
1583
+ if ((o.dtime > (timeout * 60000)) || (o.dtime < -30000)) { obj.debug(1, 'ERR: Bad cookie due to timeout'); return null; } // The cookie is only valid 120 seconds, or 30 seconds back in time (in case other server's clock is not quite right)
1584
+ } else {
1585
+ // An expire time is included in the cookie (in minutes), use this.
1586
+ if ((o.dtime > (o.expire * 60000)) || (o.dtime < -30000)) { obj.debug(1, 'ERR: Bad cookie due to timeout'); return null; } // The cookie is only valid 120 seconds, or 30 seconds back in time (in case other server's clock is not quite right)
1587
+ }
1588
return o;
1589
} catch (ex) { obj.debug(1, 'ERR: Bad AESSHA cookie due to exception: ' + ex); return null; }
1590
};
meshuser.js
+11
@@ -2499,6 +2499,17 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2499
2500
break;
2501
}
2502
+ case 'createInviteLink': {
2503
+ if (common.validateString(command.meshid, 8, 128) == false) break; // Check the meshid
2504
+ if (common.validateInt(command.expire, 1, 99999) == false) break; // Check the expire time in hours
2505
+ if (common.validateInt(command.flags, 0, 256) == false) break; // Check the flags
2506
+ var mesh = parent.meshes[command.meshid];
2507
+ if (mesh == null) break;
2508
+ const inviteCookie = parent.parent.encodeCookie({ a: 4, mid: command.meshid, f: command.flags, expire: command.expire * 60 }, parent.parent.loginCookieEncryptionKey);
2509
+ if (inviteCookie == null) break;
2510
+ ws.send(JSON.stringify({ action: 'createInviteLink', meshid: command.meshid, expire: command.expire, cookie: inviteCookie }));
2511
+ break;
2512
+ }
2513
default: {
2514
// Unknown user action
2515
console.log('Unknown action from user ' + user.name + ': ' + command.action + '.');
package.json
+1
-1
@@ -1,6 +1,6 @@
1
{
2
"name": "meshcentral",
3
- "version": "0.3.5-v",
3
+ "version": "0.3.5-x",
4
"keywords": [
5
"Remote Management",
6
"Intel AMT",
public/images/macosagent.png
Binary files /dev/null and b/public/images/macosagent.png differ
public/images/winagent.png
Binary files /dev/null and b/public/images/winagent.png differ
views/agentinvite.handlebars
new
+293
@@ -0,0 +1,293 @@
1
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+<html>
3
+<head>
4
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
5
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
6
+ <meta name="viewport" content="user-scalable=1.0,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0" />
7
+ <meta name="apple-mobile-web-app-capable" content="yes" />
8
+ <meta name="format-detection" content="telephone=no" />
9
+ <link type="text/css" href="styles/style.css" media="screen" rel="stylesheet" title="CSS" />
10
+ <script type="text/javascript" src="scripts/common-0.0.1.js"></script>
11
+ <title>MeshCentral - Agent Installation</title>
12
+ <style>
13
+ .tab {
14
+ overflow: hidden;
15
+ border: 1px solid #ccc;
16
+ background-color: #f1f1f1;
17
+ }
18
+
19
+ .tab button {
20
+ background-color: inherit;
21
+ float: left;
22
+ border: none;
23
+ outline: none;
24
+ cursor: pointer;
25
+ padding: 14px 16px;
26
+ transition: 0.3s;
27
+ }
28
+
29
+ .tab button:hover {
30
+ background-color: #ddd;
31
+ }
32
+
33
+ .tab button.active {
34
+ background-color: #ccc;
35
+ }
36
+
37
+ .tabcontent {
38
+ display: none;
39
+ padding: 6px 12px;
40
+ border: 1px solid #ccc;
41
+ border-top: none;
42
+ }
43
+ </style>
44
+</head>
45
+<body id="body" onload="if (typeof(startup) !== 'undefined') startup();" style="display:none;overflow:hidden">
46
+ <div id="container">
47
+ <!-- Begin Masthead -->
48
+ <div id="masthead" class=noselect style="background:url(logo.png) 0px 0px;background-color:#036;background-repeat:no-repeat;height:66px;width:100%;overflow:hidden;">
49
+ <div style="float:left; height: 66px; color:#c8c8c8; padding-left:20px; padding-top:8px">
50
+ <strong><font style="font-size:46px; font-family: Arial, Helvetica, sans-serif;">{{{title}}}</font></strong>
51
+ </div>
52
+ <div style="float:left; height: 66px; color:#c8c8c8; padding-left:5px; padding-top:14px">
53
+ <strong><font style="font-size:14px; font-family: Arial, Helvetica, sans-serif;">{{{title2}}}</font></strong>
54
+ </div>
55
+ <p id="logoutControl" style="color:white;font-size:11px;margin: 10px 10px 0;">{{{logoutControl}}}</p>
56
+ </div>
57
+ <div id="page_leftbar">
58
+ <div style="height:16px"></div>
59
+ </div>
60
+ <div id=topbar class="noselect style3" style="height:24px;position:relative">
61
+ <div id=uiMenuButton title="User interface selection" onclick="showUserInterfaceSelectMenu()">
62
+ ♦
63
+ <div id=uiMenu style="display:none">
64
+ <div id=uiViewButton1 class=uiSelector onclick=userInterfaceSelectMenu(1) title="Left bar interface"><div class="uiSelector1"></div></div>
65
+ <div id=uiViewButton2 class=uiSelector onclick=userInterfaceSelectMenu(2) title="Top bar interface"><div class="uiSelector2"></div></div>
66
+ <div id=uiViewButton3 class=uiSelector onclick=userInterfaceSelectMenu(3) title="Fixed width interface"><div class="uiSelector3"></div></div>
67
+ <div id=uiViewButton4 class=uiSelector onclick=toggleNightMode() title="Toggle night mode"><div class="uiSelector4"></div></div>
68
+ </div>
69
+ </div>
70
+ </div>
71
+ <div id="column_l" style="max-height:calc(100vh - 135px);overflow-y:auto">
72
+ <h1>Agent Installation<span id="groupname"></span></h1>
73
+ <p>
74
+ You have been invited to install a software that will allow a remote operator to fully access your computer remotely including the desktop and files.
75
+ Only follow the instructions below if this invitation was expected and you know who will be accessing your computer.
76
+ Selecting your operation system and follow the instructions below.
77
+ </p>
78
+ <div>
79
+ <div class="tab">
80
+ <button id="twintab64" class="tablinks" onclick="openTab(event, 'wintab64')">Windows 64bit</button>
81
+ <button id="twintab32" class="tablinks" onclick="openTab(event, 'wintab32')">Windows 32bit</button>
82
+ <button id="tlinuxtab" class="tablinks" onclick="openTab(event, 'linuxtab')">Linux</button>
83
+ <button id="tmacostab" class="tablinks" onclick="openTab(event, 'macostab')">MacOS</button>
84
+ </div>
85
+
86
+ <div id="wintab64" class="tabcontent" style="background-color:white;color:black">
87
+ <h3>Microsoft™ Windows 64bit</h3>
88
+ <p><a id="win64url">Download the software here</a>, run it and press "Install" or "Connect".</p>
89
+ <div style="text-align:center">
90
+ <img src="images/winagent.png" />
91
+ </div>
92
+ </div>
93
+
94
+ <div id="wintab32" class="tabcontent" style="background-color:white;color:black">
95
+ <h3>Microsoft™ Windows 32bit</h3>
96
+ <p><a id="win32url">Download the software here</a>, run it and press "Install" or "Connect".</p>
97
+ <div style="text-align:center">
98
+ <img src="images/winagent.png" />
99
+ </div>
100
+ </div>
101
+
102
+ <div id="linuxtab" class="tabcontent" style="background-color:white;color:black">
103
+ <h3>Linux</h3>
104
+ <p>To install, cut and paste the following command in a root terminal.</p>
105
+ <div id="linuxinstall" style="font-family:courier,'courier new',monospace;margin-left:30px"></div>
106
+ <input type="button" value="Copy to clipboard" style="margin-left:30px;margin-top:4px" onclick="copyToClipLinuxInstall()" />
107
+ <p>To uninstall, cut and paste the following command as root.</p>
108
+ <div id="unlinuxinstall" style="font-family:courier,'courier new',monospace;margin-left:30px"></div>
109
+ <input type="button" value="Copy to clipboard" style="margin-left:30px;margin-top:4px" onclick="copyToClipLinuxUnInstall()" />
110
+ <br /><br />
111
+ </div>
112
+
113
+ <div id="macostab" class="tabcontent" style="background-color:white;color:black">
114
+ <h3>Apple™ MacOS</h3>
115
+ <p><a id="macosurl">Download the installer here</a>, right click on it and select "Open", then follow the instructions.</p>
116
+ <div style="text-align:center">
117
+ <img src="images/macosagent.png" />
118
+ </div>
119
+ </div>
120
+ </div>
121
+ </div>
122
+ <div id="footer">
123
+ <table cellpadding="0" cellspacing="10" style="width: 100%">
124
+ <tr>
125
+ <td style="text-align:left"></td>
126
+ <td style="text-align:right"></td>
127
+ </tr>
128
+ </table>
129
+ </div>
130
+ </div>
131
+ <script>
132
+ 'use strict';
133
+ var uiMode = parseInt(getstore('uiMode', 1));
134
+ var webPageStackMenu = false;
135
+ var webPageFullScreen = true;
136
+ var nightMode = (getstore('_nightMode', '0') == '1');
137
+ var domain = "{{{domain}}}";
138
+ var domainUrl = "{{{domainurl}}}";
139
+ var meshid = "{{{meshid}}}";
140
+ var serverPort = "{{{serverport}}}";
141
+ var serverHttps = "{{{serverhttps}}}";
142
+ var serverNoProxy = "{{{servernoproxy}}}";
143
+ var installFlags = "{{{installflags}}}";
144
+ var groupName = decodeURIComponent("{{{meshname}}}");
145
+ if (groupName != '') { QH('groupname', ' - ' + groupName); }
146
+ userInterfaceSelectMenu();
147
+ setup();
148
+
149
+ // Toggle user interface menu
150
+ function showUserInterfaceSelectMenu() {
151
+ Q('uiViewButton1').classList.remove('uiSelectorSel');
152
+ Q('uiViewButton2').classList.remove('uiSelectorSel');
153
+ Q('uiViewButton3').classList.remove('uiSelectorSel');
154
+ Q('uiViewButton4').classList.remove('uiSelectorSel');
155
+ try { Q('uiViewButton' + uiMode).classList.add('uiSelectorSel'); } catch (ex) { }
156
+ QV('uiMenu', (QS('uiMenu').display == 'none'));
157
+ if (nightMode) { Q('uiViewButton4').classList.add('uiSelectorSel'); }
158
+ }
159
+
160
+ function userInterfaceSelectMenu(s) {
161
+ if (s) { uiMode = s; putstore('uiMode', uiMode); }
162
+ webPageFullScreen = (uiMode < 3);
163
+ webPageStackMenu = true;//(uiMode > 1);
164
+ toggleFullScreen(0);
165
+ toggleStackMenu(0);
166
+ QC('column_l').add('room4submenu');
167
+ }
168
+
169
+ function toggleNightMode() {
170
+ nightMode = !nightMode;
171
+ if (nightMode) { QC('body').add('night'); } else { QC('body').remove('night'); }
172
+ putstore('_nightMode', nightMode ? '1' : '0');
173
+ }
174
+
175
+ // Toggle the web page to full screen
176
+ function toggleFullScreen(toggle) {
177
+ if (toggle === 1) { webPageFullScreen = !webPageFullScreen; putstore('webPageFullScreen', webPageFullScreen); }
178
+ var hide = 0;
179
+ //if (args.hide) { hide = parseInt(args.hide); }
180
+ if (webPageFullScreen == false) {
181
+ QC('body').remove("menu_stack");
182
+ QC('body').remove("fullscreen");
183
+ QC('body').remove("arg_hide");
184
+ //if (xxcurrentView >= 10) QC('column_l').add('room4submenu');
185
+ //QV('UserDummyMenuSpan', false);
186
+ //QV('page_leftbar', false);
187
+ } else {
188
+ QC('body').add("fullscreen");
189
+ if (hide & 16) QC('body').add("arg_hide"); // This is replacement for QV('page_leftbar', !(hide & 16));
190
+ //QV('UserDummyMenuSpan', (xxcurrentView < 10) && webPageFullScreen);
191
+ //QV('page_leftbar', true);
192
+ }
193
+ QV('body', true);
194
+ }
195
+
196
+ // If FullScreen, toggle menu to be horisontal or vertical
197
+ function toggleStackMenu(toggle) {
198
+ if (webPageFullScreen == true) {
199
+ if (toggle === 1) {
200
+ webPageStackMenu = !webPageStackMenu;
201
+ putstore('webPageStackMenu', webPageStackMenu);
202
+ }
203
+ if (webPageStackMenu == false) {
204
+ QC('body').remove("menu_stack");
205
+ } else {
206
+ QC('body').add("menu_stack");
207
+ //if (xxcurrentView >= 10) QC('column_l').remove('room4submenu');
208
+ }
209
+ }
210
+ }
211
+
212
+ function putstore(name, val) { try { if (typeof (localStorage) === 'undefined') return; localStorage.setItem(name, val); } catch (e) { } }
213
+ function getstore(name, val) { try { if (typeof (localStorage) === 'undefined') return val; var v = localStorage.getItem(name); if ((v == null) || (v == null)) return val; return v; } catch (e) { return val; } }
214
+
215
+ function openTab(evt, tabname) {
216
+ // Declare all variables
217
+ var i, tabcontent, tablinks;
218
+
219
+ // Get all elements with class="tabcontent" and hide them
220
+ tabcontent = document.getElementsByClassName("tabcontent");
221
+ for (i = 0; i < tabcontent.length; i++) {
222
+ tabcontent[i].style.display = "none";
223
+ }
224
+
225
+ // Get all elements with class="tablinks" and remove the class "active"
226
+ tablinks = document.getElementsByClassName("tablinks");
227
+ for (i = 0; i < tablinks.length; i++) {
228
+ tablinks[i].className = tablinks[i].className.replace(" active", "");
229
+ }
230
+
231
+ // Show the current tab, and add an "active" class to the button that opened the tab
232
+ document.getElementById(tabname).style.display = "block";
233
+ if (evt != null) { evt.currentTarget.className += " active"; } else { document.getElementById('t' + tabname).className += " active"; }
234
+ }
235
+
236
+ var linuxInstall, linuxUnInstall;
237
+ function setup() {
238
+ var servername = window.location.hostname;
239
+ var domainUrlNoSlash = domainUrl.substring(0, domainUrl.length - 1);
240
+
241
+ // Windows 64bit Setup
242
+ var url = 'meshagents?id=4&meshid=' + meshid;
243
+ if (installFlags != 0) { url += ('&installflags=' + installFlags); }
244
+ Q('win64url').href = url;
245
+
246
+ // Windows 32bit Setup
247
+ url = 'meshagents?id=3&meshid=' + meshid;
248
+ if (installFlags != 0) { url += ('&installflags=' + installFlags); }
249
+ Q('win32url').href = url;
250
+
251
+ // MacOS Setup
252
+ url = 'meshagents?id=16&meshid=' + meshid;
253
+ Q('macosurl').href = url;
254
+
255
+ // Linux Setup
256
+ if (serverHttps == 1) {
257
+ var portStr = (serverPort == 443) ? '' : (":" + serverPort);
258
+ if (serverNoProxy == 0) {
259
+ linuxInstall = "(wget https://" + servername + portStr + domainUrl + "meshagents?script=1 --no-check-certificate -O ./meshinstall.sh || wget https://" + servername + portStr + domainUrl + "meshagents?script=1 --no-proxy --no-check-certificate -O ./meshinstall.sh) && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh https://" + servername + portStr + domainUrlNoSlash + " '" + meshid + "'\r\n";
260
+ linuxUnInstall = "(wget https://" + servername + portStr + domainUrl + "meshagents?script=1 --no-check-certificate -O ./meshinstall.sh || wget https://" + servername + portStr + domainUrl + "meshagents?script=1 --no-proxy --no-check-certificate -O ./meshinstall.sh) && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh uninstall\r\n";
261
+ } else {
262
+ // Server asked that agent be installed to preferably not use a HTTP proxy.
263
+ linuxInstall = "wget https://" + servername + portStr + domainUrl + "meshagents?script=1 --no-proxy --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh https://" + servername + portStr + domainUrlNoSlash + " '" + meshid + "'\r\n";
264
+ linuxUnInstall = "wget https://" + servername + portStr + domainUrl + "meshagents?script=1 --no-proxy --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh uninstall\r\n";
265
+ }
266
+ } else {
267
+ var portStr = (serverPort == 80) ? '' : (":" + serverPort);
268
+ if (serverNoProxy == 0) {
269
+ linuxInstall = "(wget http://" + servername + portStr + domainUrl + "meshagents?script=1 -O ./meshinstall.sh || wget http://" + servername + portStr + domainUrl + "meshagents?script=1 --no-proxy -O ./meshinstall.sh) && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh http://" + servername + portStr + domainUrlNoSlash + " '" + meshid + "'\r\n";
270
+ linuxUnInstall = "(wget http://" + servername + portStr + domainUrl + "meshagents?script=1 -O ./meshinstall.sh || wget http://" + servername + portStr + domainUrl + "meshagents?script=1 --no-proxy -O ./meshinstall.sh) && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh uninstall\r\n";
271
+ } else {
272
+ // Server asked that agent be installed to preferably not use a HTTP proxy.
273
+ linuxInstall = "wget http://" + servername + portStr + domainUrl + "meshagents?script=1 --no-proxy -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh http://" + servername + portStr + domainUrlNoSlash + " '" + meshid + "'\r\n";
274
+ linuxUnInstall = "wget http://" + servername + portStr + domainUrl + "meshagents?script=1 --no-proxy -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh uninstall\r\n";
275
+ }
276
+ }
277
+ QH('linuxinstall', linuxInstall);
278
+ QH('unlinuxinstall', linuxUnInstall);
279
+
280
+ // Attempt to detect the most likely operating system for this browser
281
+ if (navigator.userAgent.indexOf('Win64')) { openTab(null, 'wintab64'); }
282
+ else if (navigator.userAgent.indexOf('Windows')) { openTab(null, 'wintab32'); }
283
+ else if (navigator.userAgent.indexOf('Linux')) { openTab(null, 'linuxtab'); }
284
+ else if (navigator.userAgent.indexOf('Macintosh')) { openTab(null, 'macostab'); }
285
+ else { openTab(null, 'wintab64'); }
286
+ }
287
+
288
+ function copyToClipLinuxInstall() { navigator.clipboard.writeText(linuxInstall); }
289
+ function copyToClipLinuxUnInstall() { navigator.clipboard.writeText(linuxUnInstall); }
290
+
291
+ </script>
292
+</body>
293
+</html>
views/default.handlebars
+61
-15
@@ -1977,6 +1977,28 @@
1977
}
1978
break;
1979
}
1980
+ case 'createInviteLink': { // Agent installation invitation link
1981
+ if (xxdialogTag != message.meshid) break;
1982
+ var servername = serverinfo.name;
1983
+ if ((servername.indexOf('.') == -1) || ((features & 2) != 0)) { servername = window.location.hostname; } // If the server name is not set or it's in LAN-only mode, use the URL hostname as server name.
1984
+ var domainUrlNoSlash = domainUrl.substring(0, domainUrl.length - 1);
1985
+ var url;
1986
+ if (serverinfo.https == true) {
1987
+ var portStr = (serverinfo.port == 443) ? '' : (":" + serverinfo.port);
1988
+ url = "https://" + servername + portStr + domainUrl + "agentinvite?c=" + message.cookie;
1989
+ } else {
1990
+ var portStr = (serverinfo.port == 80) ? '' : (":" + serverinfo.port);
1991
+ url = "http://" + servername + portStr + domainUrl + "agentinvite?c=" + message.cookie;
1992
+ }
1993
+ Q('agentInvitationLink').href = url;
1994
+ var t = message.expire + ' hour' + addLetterS(message.expire);
1995
+ if (message.expire == 24) { t = '1 day'; }
1996
+ if (message.expire == 168) { t = '1 week'; }
1997
+ if (message.expire == 5040) { t = '1 month'; }
1998
+ QH('agentInvitationLink', 'Invitation Link (' + t + ')');
1999
+ QV('agentInvitationLinkDiv', true);
2000
+ break;
2001
+ }
2002
case 'stopped': { // Server is stopping.
2003
// Disconnect
2004
autoReconnect = false;
@@ -2547,9 +2569,7 @@
2569
}
2570
if (mesh.mtype == 2) {
2571
r += ' <a style=cursor:pointer;font-size:10px title="Add a new computer to this mesh by installing the mesh agent." onclick=addAgentToMesh(\"' + mesh._id + '\")>Add Agent</a>';
2550
- if (features & 64) {
2551
- r += ' <a style=cursor:pointer;font-size:10px title="Invite someone to install the mesh agent on this mesh." onclick=inviteAgentToMesh(\"' + mesh._id + '\")>Invite</a>';
2552
- }
2572
+ r += ' <a style=cursor:pointer;font-size:10px title="Invite someone to install the mesh agent on this mesh." onclick=inviteAgentToMesh(\"' + mesh._id + '\")>Invite</a>';
2573
}
2574
return r;
2575
}
@@ -2673,23 +2693,51 @@
2693
2694
function inviteAgentToMesh(meshid) {
2695
if (xxdialogMode) return;
2676
- var mesh = meshes[meshid];
2677
- var x = "Invite someone to install the mesh agent. An email with be sent with the link to the mesh agent installation for " + EscapeHtml(mesh.name) + ".<br /><br />";
2678
- x += addHtmlValue('Name (optional)', '<input id=agentInviteName value="" style=width:230px maxlength=64 />');
2679
- x += addHtmlValue('Email', '<input id=agentInviteEmail style=width:230px placeholder="example@email.com" onkeyup=validateAgentInvite()></input>');
2680
- 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>');
2681
- 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>');
2682
- x += addHtmlValue('Message<br />(optional)', '<textarea id=agentInviteMessage value="" style=width:230px;height:100px;resize:none maxlength=1024 /></textarea>');
2696
+ var x = '', mesh = meshes[meshid];
2697
+ if (features & 64) {
2698
+ x += addHtmlValue('Invitation Type', '<select id=d2InviteType onchange=d2ChangedInviteType() style=width:236px><option value=0>Link invitation</option><option value=1>Email invitation</option></select>') + "<hr />";
2699
+ 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 />";
2700
+ x += addHtmlValue('Name (optional)', '<input id=agentInviteName value="" style=width:230px maxlength=64 />');
2701
+ x += addHtmlValue('Email', '<input id=agentInviteEmail style=width:230px placeholder="example@email.com" onkeyup=validateAgentInvite()></input>');
2702
+ 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>');
2703
+ 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>');
2704
+ x += addHtmlValue('Message<br />(optional)', '<textarea id=agentInviteMessage value="" style=width:230px;height:100px;resize:none maxlength=1024 /></textarea>');
2705
+ x += '</div>';
2706
+ }
2707
+ x += '<div id=urlInviteDiv>Invite someone to install the mesh agent by sharing a invitation link. This link points the user to installation instructions for the \"' + EscapeHtml(mesh.name) + '\" device group. The link is public and no account this server is needed.<br /><br />';
2708
+ 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></select>');
2709
+ 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>';
2710
setDialogMode(2, "Invite", 3, performAgentInvite, x, meshid);
2711
validateAgentInvite();
2712
+ d2RequestInvitationLink();
2713
+ }
2714
+
2715
+ function d2RequestInvitationLink() {
2716
+ meshserver.send({ action: 'createInviteLink', meshid: xxdialogTag, expire: parseInt(Q('d2inviteExpire').value), flags: 0 });
2717
+ }
2718
+
2719
+ function d2ChangedInviteType() {
2720
+ QV('urlInviteDiv', Q('d2InviteType').value == 0);
2721
+ if (features & 64) { QV('emailInviteDiv', Q('d2InviteType').value == 1); }
2722
+ validateAgentInvite();
2723
}
2724
2725
+ function d2CopyInviteToClip() { navigator.clipboard.writeText(Q('agentInvitationLink').href); }
2726
+
2727
function validateAgentInvite() {
2688
- QE('idx_dlgOkButton', checkEmail(Q('agentInviteEmail').value));
2728
+ if ((features & 64) && (Q('d2InviteType').value == 1)) {
2729
+ QE('idx_dlgOkButton', checkEmail(Q('agentInviteEmail').value));
2730
+ QV('idx_dlgCancelButton', true);
2731
+ } else {
2732
+ QE('idx_dlgOkButton', true);
2733
+ QV('idx_dlgCancelButton', false);
2734
+ }
2735
}
2736
2737
function performAgentInvite(button, meshid) {
2692
- 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 });
2738
+ if ((features & 64) && (Q('d2InviteType').value == 1)) {
2739
+ 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 });
2740
+ }
2741
}
2742
2743
function addAgentToMesh(meshid) {
@@ -6196,9 +6244,7 @@
6244
}
6245
if (currentMesh.mtype == 2) {
6246
x += '<a onclick=addAgentToMesh(\"' + currentMesh._id + '\") style=cursor:pointer;margin-right:10px title="Add a new computer to this mesh by installing the mesh agent."><img src=images/icon-addnew.png border=0 height=12 width=12> Install</a>';
6199
- if (features & 64) {
6200
- x += '<a onclick=inviteAgentToMesh(\"' + currentMesh._id + '\") style=cursor:pointer;margin-right:10px title="Invite someone to install the mesh agent on this mesh."><img src=images/icon-addnew.png border=0 height=12 width=12> Invite</a>';
6201
- }
6247
+ x += '<a onclick=inviteAgentToMesh(\"' + currentMesh._id + '\") style=cursor:pointer;margin-right:10px title="Invite someone to install the mesh agent on this mesh."><img src=images/icon-addnew.png border=0 height=12 width=12> Invite</a>';
6248
}
6249
}
6250
webserver.js
+25
@@ -1063,6 +1063,30 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1063
}
1064
}
1065
1066
+ // Called to process an agent invite request
1067
+ function handleAgentInviteRequest(req, res) {
1068
+ const domain = checkUserIpAddress(req, res);
1069
+ if ((domain == null) || ((req.query.m == null) && (req.query.c == null))) { res.sendStatus(404); return; }
1070
+ if (req.query.c != null) {
1071
+ // A cookie is specified in the query string, use that
1072
+ var cookie = obj.parent.decodeCookie(req.query.c, obj.parent.loginCookieEncryptionKey);
1073
+ if (cookie == null) { res.sendStatus(404); return; }
1074
+ var mesh = obj.meshes[cookie.mid];
1075
+ if (mesh == null) { res.sendStatus(404); return; }
1076
+ var installflags = cookie.f;
1077
+ if (typeof installflags != 'number') { installflags = 0; }
1078
+ res.render(obj.path.join(obj.parent.webViewsPath, 'agentinvite'), { title: domain.title, title2: domain.title2, domainurl: domain.url, meshid: mesh._id.split('/')[2], serverport: ((args.aliasport != null) ? args.aliasport : args.port), serverhttps: ((args.notls == true) ? '0' : '1'), servernoproxy: ((domain.agentnoproxy === true) ? '1' : '0'), meshname: encodeURIComponent(mesh.name), installflags: installflags });
1079
+ } else if (req.query.m != null) {
1080
+ // The MeshId is specified in the query string, use that
1081
+ var mesh = obj.meshes['mesh/' + domain.id + '/' + req.query.m.toLowerCase()];
1082
+ if (mesh == null) { res.sendStatus(404); return; }
1083
+ var installflags = 0;
1084
+ if (req.query.f) { installflags = parseInt(req.query.f); }
1085
+ if (typeof installflags != 'number') { installflags = 0; }
1086
+ res.render(obj.path.join(obj.parent.webViewsPath, 'agentinvite'), { title: domain.title, title2: domain.title2, domainurl: domain.url, meshid: mesh._id.split('/')[2], serverport: ((args.aliasport != null) ? args.aliasport : args.port), serverhttps: ((args.notls == true) ? '0' : '1'), servernoproxy: ((domain.agentnoproxy === true) ? '1' : '0'), meshname: encodeURIComponent(mesh.name), installflags: installflags });
1087
+ }
1088
+ }
1089
+
1090
function handleDeleteAccountRequest(req, res) {
1091
const domain = checkUserIpAddress(req, res);
1092
if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) { res.sendStatus(404); return; }
@@ -2670,6 +2694,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
2694
obj.app.post(url + 'resetpassword', handleResetPasswordRequest);
2695
obj.app.post(url + 'resetaccount', handleResetAccountRequest);
2696
obj.app.get(url + 'checkmail', handleCheckMailRequest);
2697
+ obj.app.get(url + 'agentinvite', handleAgentInviteRequest);
2698
obj.app.post(url + 'amtevents.ashx', obj.handleAmtEventRequest);
2699
obj.app.get(url + 'meshagents', obj.handleMeshAgentRequest);
2700
obj.app.get(url + 'messenger', handleMessengerRequest);