From e58419a6c3ffde82ac22a250862b5d5871813970 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Wed, 23 Feb 2022 16:14:32 -0800 Subject: [PATCH] Improved agent invitations, #3694 --- meshuser.js | 5 ++++- views/agentinvite.handlebars | 25 ++++++++++++++++++++++++- views/default.handlebars | 23 ++++++++++++++++++----- webserver.js | 11 ++++++++--- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/meshuser.js b/meshuser.js index 5128cf25..76f33da2 100644 --- a/meshuser.js +++ b/meshuser.js @@ -2136,6 +2136,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use return; } mesh.invite = { codes: command.invite.codes, flags: command.invite.flags }; + if (typeof command.invite.ag == 'number') { mesh.invite.ag = command.invite.ag; } if (change != '') { change += ' and invite code changed'; } else { change += 'Device group "' + mesh.name + '" invite code changed'; } changesids.push(6); } @@ -3681,7 +3682,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use break; } - const inviteCookie = parent.parent.encodeCookie({ a: 4, mid: command.meshid, f: command.flags, expire: command.expire * 60 }, parent.parent.invitationLinkEncryptionKey); + const cookie = { a: 4, mid: command.meshid, f: command.flags, expire: command.expire * 60 }; + if ((typeof command.agents == 'number') && (command.agents != 0)) { cookie.ag = command.agents; } + const inviteCookie = parent.parent.encodeCookie(cookie, parent.parent.invitationLinkEncryptionKey); if (inviteCookie == null) { if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'createInviteLink', responseid: command.responseid, result: 'Unable to generate invitation cookie' })); } catch (ex) { } } break; } // Create the server url diff --git a/views/agentinvite.handlebars b/views/agentinvite.handlebars index 7a3adaf0..bc281935 100644 --- a/views/agentinvite.handlebars +++ b/views/agentinvite.handlebars @@ -85,7 +85,7 @@ - +
@@ -180,6 +180,7 @@ var serverHttps = '{{{serverhttps}}}'; var serverNoProxy = '{{{servernoproxy}}}'; var installFlags = '{{{installflags}}}'; + var showAgents = parseInt('{{{showagents}}}'); // 0 = Show all agents, 1 = Windows only, 2 = Linux only, 4 = MacOS only, 8 = Assistant only, 16 = Android var magenturl = '{{{magenturl}}}'; var groupName = decodeURIComponent('{{{meshname}}}'); var urlargs = parseUriArgs(); @@ -215,8 +216,30 @@ document.title = "Agent Installation"; } + // Setup visible tabs + var tabcount = 0, tabselect = null; + var tab1 = (showAgents == 0) || (showAgents & 1); + var tab2 = (showAgents == 0) || (showAgents & 1); + var tab3 = (showAgents == 0) || (showAgents & 2); + var tab4 = (showAgents == 0) || (showAgents & 4); + var tab5 = (showAgents == 0) || (showAgents & 16); + var tab6 = (showAgents == 0) || (showAgents & 8); + if (tab6) { tabcount++; tabselect = 'assistab'; } + if (tab5) { tabcount++; tabselect = 'androtab'; } + if (tab4) { tabcount++; tabselect = 'macostab'; } + if (tab3) { tabcount++; tabselect = 'linuxtab'; } + if (tab2) { tabcount++; tabselect = 'wintab32'; } + if (tab1) { tabcount++; tabselect = 'wintab64'; } + QV('twintab64', tab1 && (tabcount > 1)); + QV('twintab32', tab2 && (tabcount > 1)); + QV('tlinuxtab', tab3 && (tabcount > 1)); + QV('tmacostab', tab4 && (tabcount > 1)); + QV('tandrotab', tab5 && (tabcount > 1)); + QV('tassistab', tab6 && (tabcount > 1)); + userInterfaceSelectMenu(); setup(); + openTab(null, tabselect); } // Create the QR code diff --git a/views/default.handlebars b/views/default.handlebars index 02f07f52..436d2232 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -4962,7 +4962,10 @@ } x += '
' + format("Invite someone to install the mesh agent by sharing an invitation link. This link points the user to installation instructions for the \"{0}\" device group. The link is public and no account for this server is needed.", EscapeHtml(mesh.name)) + '

'; x += addHtmlValue("Link Expiration", ''); + x += addHtmlValue("Agents", ''); + x += '
'; x += addHtmlValue("Installation Type", ''); + x += '
'; x += '
'; setDialogMode(2, "Invite", 3, performAgentInvite, x, meshid); if (features & 64) { Q('d2InviteType').focus(); d2ChangedInviteType(); } else { Q('d2inviteExpire').focus(); validateAgentInvite(); } @@ -4971,14 +4974,18 @@ } function d2RequestInvitationLink() { - meshserver.send({ action: 'createInviteLink', meshid: xxdialogTag, expire: parseInt(Q('d2inviteExpire').value), flags: parseInt(Q('d2agentInviteType').value) }); + meshserver.send({ action: 'createInviteLink', meshid: xxdialogTag, expire: parseInt(Q('d2inviteExpire').value), flags: parseInt(Q('d2agentInviteType').value), agents: parseInt(Q('d2agentType').value) }); } function d2ChangedInviteType() { - QV('urlInviteDiv', Q('d2InviteType').value == 0); - QV('d2agentexpirediv', Q('agentInviteNameOs').value == 4); - QV('emailInviteDiv', Q('d2InviteType').value == 1); + if (features & 64) { + QV('urlInviteDiv', Q('d2InviteType').value == 0); + QV('d2agentexpirediv', Q('agentInviteNameOs').value == 4); + QV('emailInviteDiv', Q('d2InviteType').value == 1); + } + QV('d2agentInstallTypeDiv', parseInt(Q('d2agentType').value) < 2); validateAgentInvite(); + d2RequestInvitationLink(); } function d2CopyInviteToClip() { copyTextToClip(Q('agentInvitationLink').href); } @@ -12758,11 +12765,15 @@ x += '
' + url + '

'; x += '
'; x += addHtmlValue("Invite Codes", ''); + x += addHtmlValue("Agents", ''); + x += '
'; x += addHtmlValue("Installation Type", ''); + x += '
'; setDialogMode(2, "Invite Codes", 3, p20editmeshInviteCodeEx, x); if (currentMesh.invite != null) { Q('agentJoinCheck').checked = true; Q('agentInviteCode').value = currentMesh.invite.codes.join(', '); + if (currentMesh.invite.ag) { Q('agentType').value = currentMesh.invite.ag; } Q('agentInviteType').value = (currentMesh.invite.flags & 3); } p20editmeshInviteCodeValidate(); @@ -12780,15 +12791,17 @@ var ok = true, codes = Q('agentInviteCode').value.split(','); for (var i in codes) { codes[i] = codes[i].trim(); if (codes[i] == '') { ok = false; } } QE('agentInviteCode', Q('agentJoinCheck').checked); + QE('agentType', Q('agentJoinCheck').checked); QE('agentInviteType', Q('agentJoinCheck').checked); QE('idx_dlgOkButton', (Q('agentJoinCheck').checked == false) || (ok)); + QV('d2agentInstallTypeDiv', parseInt(Q('agentType').value) < 2); } function p20editmeshInviteCodeEx() { if (Q('agentJoinCheck').checked == true) { var codes = Q('agentInviteCode').value.split(','); for (var i in codes) { codes[i] = codes[i].trim(); } - meshserver.send({ action: 'editmesh', meshid: currentMesh._id, invite: { codes: codes, flags: parseInt(Q('agentInviteType').value) } }); + meshserver.send({ action: 'editmesh', meshid: currentMesh._id, invite: { codes: codes, flags: parseInt(Q('agentInviteType').value), ag: parseInt(Q('agentType').value) } }); } else { meshserver.send({ action: 'editmesh', meshid: currentMesh._id, invite: '*' }); } diff --git a/webserver.js b/webserver.js index 40f5a0f3..0638e328 100644 --- a/webserver.js +++ b/webserver.js @@ -1895,7 +1895,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF for (var i in obj.meshes) { if ((obj.meshes[i].domain == domain.id) && (obj.meshes[i].deleted == null) && (obj.meshes[i].invite != null) && (obj.meshes[i].invite.codes.indexOf(req.body.inviteCode) >= 0)) { // Send invitation link, valid for 1 minute. - res.redirect(domain.url + 'agentinvite?c=' + parent.encodeCookie({ a: 4, mid: i, f: obj.meshes[i].invite.flags, expire: 1 }, parent.invitationLinkEncryptionKey) + (req.query.key ? ('&key=' + req.query.key) : '') + (req.query.hide ? ('&hide=' + req.query.hide) : '')); + res.redirect(domain.url + 'agentinvite?c=' + parent.encodeCookie({ a: 4, mid: i, f: obj.meshes[i].invite.flags, ag: obj.meshes[i].invite.ag, expire: 1 }, parent.invitationLinkEncryptionKey) + (req.query.key ? ('&key=' + req.query.key) : '') + (req.query.hide ? ('&hide=' + req.query.hide) : '')); return; } } @@ -2045,6 +2045,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF if (mesh == null) { res.sendStatus(404); return; } var installflags = cookie.f; if (typeof installflags != 'number') { installflags = 0; } + var showagents = cookie.ag; + if (typeof showagents != 'number') { showagents = 0; } parent.debug('web', 'handleAgentInviteRequest using cookie.'); // Build the mobile agent URL, this is used to connect mobile devices @@ -2057,7 +2059,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF var magenturl = 'mc://' + agentServerName + ((agentHttpsPort != 443) ? (':' + agentHttpsPort) : '') + ((xdomain != '') ? ('/' + xdomain) : '') + ',' + obj.agentCertificateHashBase64 + ',' + mesh._id.split('/')[2]; var meshcookie = parent.encodeCookie({ m: mesh._id.split('/')[2] }, parent.invitationLinkEncryptionKey); - render(req, res, getRenderPage('agentinvite', req, domain), getRenderArgs({ meshid: meshcookie, serverport: ((args.aliasport != null) ? args.aliasport : args.port), serverhttps: 1, servernoproxy: ((domain.agentnoproxy === true) ? '1' : '0'), meshname: encodeURIComponent(mesh.name).replace(/'/g, '%27'), installflags: installflags, magenturl: magenturl }, req, domain)); + render(req, res, getRenderPage('agentinvite', req, domain), getRenderArgs({ meshid: meshcookie, serverport: ((args.aliasport != null) ? args.aliasport : args.port), serverhttps: 1, servernoproxy: ((domain.agentnoproxy === true) ? '1' : '0'), meshname: encodeURIComponent(mesh.name).replace(/'/g, '%27'), installflags: installflags, showagents: showagents, magenturl: magenturl }, req, domain)); } else if (req.query.m != null) { // The MeshId is specified in the query string, use that var mesh = obj.meshes['mesh/' + domain.id + '/' + req.query.m.toLowerCase()]; @@ -2065,6 +2067,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF var installflags = 0; if (req.query.f) { installflags = parseInt(req.query.f); } if (typeof installflags != 'number') { installflags = 0; } + var showagents = 0; + if (req.query.f) { showagents = parseInt(req.query.ag); } + if (typeof showagents != 'number') { showagents = 0; } parent.debug('web', 'handleAgentInviteRequest using meshid.'); // Build the mobile agent URL, this is used to connect mobile devices @@ -2077,7 +2082,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF var magenturl = 'mc://' + agentServerName + ((agentHttpsPort != 443) ? (':' + agentHttpsPort) : '') + ((xdomain != '') ? ('/' + xdomain) : '') + ',' + obj.agentCertificateHashBase64 + ',' + mesh._id.split('/')[2]; var meshcookie = parent.encodeCookie({ m: mesh._id.split('/')[2] }, parent.invitationLinkEncryptionKey); - render(req, res, getRenderPage('agentinvite', req, domain), getRenderArgs({ meshid: meshcookie, serverport: ((args.aliasport != null) ? args.aliasport : args.port), serverhttps: 1, servernoproxy: ((domain.agentnoproxy === true) ? '1' : '0'), meshname: encodeURIComponent(mesh.name).replace(/'/g, '%27'), installflags: installflags, magenturl: magenturl }, req, domain)); + render(req, res, getRenderPage('agentinvite', req, domain), getRenderArgs({ meshid: meshcookie, serverport: ((args.aliasport != null) ? args.aliasport : args.port), serverhttps: 1, servernoproxy: ((domain.agentnoproxy === true) ? '1' : '0'), meshname: encodeURIComponent(mesh.name).replace(/'/g, '%27'), installflags: installflags, showagents: showagents, magenturl: magenturl }, req, domain)); } }