mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-12-25 14:45:52 -05:00
Added guest web sharing of HTTP/HTTPS (#4413)
This commit is contained in:
parent
d6a1f04d4a
commit
5d7fabfc21
Binary file not shown.
Binary file not shown.
1234
agents/meshcore.js
1234
agents/meshcore.js
File diff suppressed because it is too large
Load Diff
@ -69,7 +69,7 @@ function SerialTunnel(options) {
|
||||
}
|
||||
|
||||
// Construct a Web relay object
|
||||
module.exports.CreateWebRelaySession = function (parent, db, req, args, domain, userid, nodeid, addr, port, appid, sessionid) {
|
||||
module.exports.CreateWebRelaySession = function (parent, db, req, args, domain, userid, nodeid, addr, port, appid, sessionid, expire) {
|
||||
const obj = {};
|
||||
obj.parent = parent;
|
||||
obj.lastOperation = Date.now();
|
||||
@ -80,6 +80,7 @@ module.exports.CreateWebRelaySession = function (parent, db, req, args, domain,
|
||||
obj.port = port;
|
||||
obj.appid = appid;
|
||||
obj.sessionid = sessionid;
|
||||
obj.expireTimer = null;
|
||||
var pendingRequests = [];
|
||||
var nextTunnelId = 1;
|
||||
var tunnels = {};
|
||||
@ -90,6 +91,9 @@ module.exports.CreateWebRelaySession = function (parent, db, req, args, domain,
|
||||
// Any HTTP cookie set by the device is going to be shared between all tunnels to that device.
|
||||
obj.webCookies = {};
|
||||
|
||||
// Setup an expire time if needed
|
||||
if (expire != null) { var timeout = (expire - Date.now()); if (timeout < 10) { timeout = 10; } obj.expireTimer = setTimeout(close, timeout); }
|
||||
|
||||
// Events
|
||||
obj.closed = false;
|
||||
obj.onclose = null;
|
||||
@ -202,6 +206,9 @@ module.exports.CreateWebRelaySession = function (parent, db, req, args, domain,
|
||||
parent.parent.debug('webrelay', 'tunnel-close');
|
||||
obj.closed = true;
|
||||
|
||||
// Clear the time if present
|
||||
if (obj.expireTimer != null) { clearTimeout(obj.expireTimer); delete obj.expireTimer; }
|
||||
|
||||
// Close all tunnels
|
||||
for (var i in tunnels) { tunnels[i].close(); }
|
||||
tunnels = null;
|
||||
|
@ -466,7 +466,7 @@ function CreateMeshCentralServer(config, args) {
|
||||
const npmproxy = ((typeof obj.args.npmproxy == 'string') ? (' --proxy ' + obj.args.npmproxy) : '');
|
||||
const env = Object.assign({}, process.env); // Shallow clone
|
||||
if (typeof obj.args.npmproxy == 'string') { env['HTTP_PROXY'] = env['HTTPS_PROXY'] = env['http_proxy'] = env['https_proxy'] = obj.args.npmproxy; }
|
||||
const xxprocess = child_process.exec(npmpath + ' install meshcentral' + version + npmproxy, { maxBuffer: Infinity, cwd: obj.parentpath, env: env }, function (error, stdout, stderr) {
|
||||
const xxprocess = child_process.exec(npmpath + ' install --no-package-lock meshcentral' + version + npmproxy, { maxBuffer: Infinity, cwd: obj.parentpath, env: env }, function (error, stdout, stderr) {
|
||||
if ((error != null) && (error != '')) { console.log('Update failed: ' + error); }
|
||||
});
|
||||
xxprocess.data = '';
|
||||
|
@ -4137,8 +4137,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
else if ((command.start != null) && (typeof command.start != 'number')) { err = 'Invalid start time'; } // Check the start time in UTC seconds
|
||||
else if ((command.end != null) && (typeof command.end != 'number')) { err = 'Invalid end time'; } // Check the end time in UTC seconds
|
||||
else if (common.validateInt(command.consent, 0, 256) == false) { err = 'Invalid flags'; } // Check the flags
|
||||
else if (common.validateInt(command.p, 1, 7) == false) { err = 'Invalid protocol'; } // Check the protocol, 1 = Terminal, 2 = Desktop, 4 = Files
|
||||
else if (common.validateInt(command.p, 1, 31) == false) { err = 'Invalid protocol'; } // Check the protocol, 1 = Terminal, 2 = Desktop, 4 = Files, 8 = HTTP, 16 = HTTPS
|
||||
else if ((command.recurring != null) && (common.validateInt(command.recurring, 1, 2) == false)) { err = 'Invalid recurring value'; } // Check the recurring value, 1 = Daily, 2 = Weekly
|
||||
else if ((command.port != null) && (common.validateInt(command.port, 1, 65535) == false)) { err = 'Invalid port value'; } // Check the port if present
|
||||
else if ((command.recurring != null) && ((command.end != null) || (command.start == null) || (command.expire == null))) { err = 'Invalid recurring command'; }
|
||||
else if ((command.expire == null) && ((command.start == null) || (command.end == null) || (command.start > command.end))) { err = 'No time specified'; } // Check that a time range is present
|
||||
else {
|
||||
@ -4238,7 +4239,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
try { ws.send(JSON.stringify(command)); } catch (ex) { }
|
||||
|
||||
// Create a device sharing database entry
|
||||
var shareEntry = { _id: 'deviceshare-' + publicid, type: 'deviceshare', xmeshid: node.meshid, nodeid: node._id, p: command.p, domain: node.domain, publicid: publicid, userid: user._id, guestName: command.guestname, consent: command.consent, url: url };
|
||||
var shareEntry = { _id: 'deviceshare-' + publicid, type: 'deviceshare', xmeshid: node.meshid, nodeid: node._id, p: command.p, domain: node.domain, publicid: publicid, userid: user._id, guestName: command.guestname, consent: command.consent, port: command.port, url: url };
|
||||
if ((startTime != null) && (expireTime != null)) { shareEntry.startTime = startTime; shareEntry.expireTime = expireTime; }
|
||||
else if ((startTime != null) && (duration != null)) { shareEntry.startTime = startTime; shareEntry.duration = duration; }
|
||||
if (command.recurring) { shareEntry.recurring = command.recurring; }
|
||||
|
@ -3650,7 +3650,7 @@
|
||||
if (message.consent & 0x0040) { y.push("Privacy bar"); }
|
||||
if (y.length == 0) { y.push("None"); }
|
||||
x += addHtmlValue("User Consent", y.join(', '));
|
||||
var type = ['', "Remote Terminal Link", "Remote Desktop Link", "Remote Desktop + Terminal Link", "Remote Files Link", "Remote Terminal + Files Link", "Remote Desktop + Files Link", "Remote Desktop + Terminal + Files Link"][message.p];
|
||||
var type = ''; if (message.p <= 7) { type = ['', "Remote Terminal Link", "Remote Desktop Link", "Remote Desktop + Terminal Link", "Remote Files Link", "Remote Terminal + Files Link", "Remote Desktop + Files Link", "Remote Desktop + Terminal + Files Link"][message.p]; } else if (message.p == 8) { type = format("HTTP/{0} link", message.port); } else if (message.p == 16) { type = format("HTTPS/{0}", message.port); }
|
||||
x += '<div id=agentInvitationLinkDiv style="text-align:center;font-size:large;margin:16px"><a href="' + message.url + '" id=agentInvitationLink rel="noreferrer noopener" target="_blank" style=cursor:pointer>' + type + '</a> <img src=images/link4.png height=10 width=10 title="' + "Copy link to clipboard" + '" style=cursor:pointer onclick=d2CopyInviteToClip()></div></div>';
|
||||
setDialogMode(2, "Share Device", 1, null, x);
|
||||
break;
|
||||
@ -7588,7 +7588,7 @@
|
||||
var dshare = deviceShares[i], trash = '';
|
||||
if (dshare.url != null) { trash += '<a href="' + dshare.url + '" rel="noreferrer noopener" target=_blank title="' + "Device Sharing Link" + '" style=cursor:pointer><img src=images/link2.png border=0 height=10 width=10></a> '; }
|
||||
trash += '<a href=# onclick=\'return p30removeDeviceSharing(event,"' + encodeURIComponentEx(currentNode._id) + '","' + encodeURIComponentEx(dshare.publicid) + '","' + encodeURIComponentEx(dshare.guestName) + '")\' title="' + "Remove device sharing" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>';
|
||||
var type = ['', "Terminal", "Desktop", "Desktop + Terminal", "Files", "Terminal + Files", "Desktop + Files", "Desktop + Terminal + Files"][dshare.p];
|
||||
var type = ''; if (dshare.p <= 7) { type = ['', "Terminal", "Desktop", "Desktop + Terminal", "Files", "Terminal + Files", "Desktop + Files", "Desktop + Terminal + Files"][dshare.p]; } else if (dshare.p == 8) { type = "HTTP/" + dshare.port; } else if (dshare.p == 16) { type = "HTTPS/" + dshare.port; }
|
||||
var details = type;
|
||||
if ((dshare.startTime != null) && (dshare.expireTime != null)) { details = format("{0}, {1} to {2}", type, printFlexDateTime(new Date(dshare.startTime)), printFlexDateTime(new Date(dshare.expireTime))); }
|
||||
if ((dshare.startTime != null) && (dshare.duration != null)) {
|
||||
@ -7896,6 +7896,11 @@
|
||||
if ((rights != 0xFFFFFFFF) && ((rights & 0x600) != 0)) { termFiles = ''; }
|
||||
var allFeatures = '<option value=7>' + "Desktop + Terminal + Files" + '</option>';
|
||||
if ((rights != 0xFFFFFFFF) && ((rights & 0x700) != 0)) { allFeatures = ''; }
|
||||
var httpFeature = '';
|
||||
if (webRelayPort != 0) {
|
||||
httpFeature = '<option value=8>' + "HTTP" + '</option><option value=9>' + "HTTPS" + '</option>';
|
||||
if ((rights != 0xFFFFFFFF) && ((rights & 8) != 0)) { httpFeature = ''; }
|
||||
}
|
||||
|
||||
var y = '', z = '';
|
||||
if ((currentNode.agent.caps & 1) == 1) { y += (deskFull + '<option value=3>' + "Desktop, View only" + '</option>'); } // Agent is desktop capable
|
||||
@ -7904,6 +7909,7 @@
|
||||
if ((currentNode.agent.caps & 5) == 5) { y += deskFiles; } // Agent is desktop + files capable
|
||||
if ((currentNode.agent.caps & 6) == 6) { y += termFiles; } // Agent is terminal + files capable
|
||||
if ((currentNode.agent.caps & 7) == 7) { y += allFeatures; } // Agent is desktop + terminal + files capable
|
||||
y += httpFeature;
|
||||
|
||||
x += addHtmlValue("Type", '<select id=d2shareType style=float:right;width:250px onchange=showShareDeviceValidate()>' + y + '</select>');
|
||||
var options = { 1 : "1 minute", 5 : "5 minutes", 10 : "10 minutes", 15 : "15 minutes", 30 : "30 minutes", 45 : "45 minutes", 60 : "60 minutes", 120 : "2 hours", 240 : "4 hours", 480 : "8 hours", 720 : "12 hours", 960 : "16 hours", 1440 : "24 hours", 2880 : "2 days", 5760 : "4 days", 0 : "Unlimited" }
|
||||
@ -7925,7 +7931,9 @@
|
||||
x += addHtmlValue("Start Time", '<input id=d2timeStartSelector style=float:right;width:250px class=flatpickr type="text" placeholder="' + "Select Date & Time..." + '" data-id="altinput">');
|
||||
x += addHtmlValue("Duration", '<select id=d2inviteDuration style=float:right;width:250px>' + z + '</select>');
|
||||
x += '</div>';
|
||||
if (currentNode.agent.caps & 1) { x += addHtmlValue("User Consent", '<select id=d2userConsent style=float:right;width:250px><option value=1>' + "Prompt for consent" + '</option><option value=0>' + "Notify Only" + '</option></select>'); }
|
||||
if (currentNode.agent.caps & 1) { x += '<div id=d2userConsentSelector>' + addHtmlValue("User Consent", '<select id=d2userConsent style=float:right;width:250px><option value=1>' + "Prompt for consent" + '</option><option value=0>' + "Notify Only" + '</option></select>') + '</div>'; }
|
||||
x += '<div id=d2httpPortSelector>' + addHtmlValue("Port", '<input id=d2httpPort style=float:right;width:250px value=80 onkeyup=showShareDeviceValidate()></input>') + '</div>';
|
||||
x += '<div id=d2httpsPortSelector>' + addHtmlValue("Port", '<input id=d2httpsPort style=float:right;width:250px value=443 onkeyup=showShareDeviceValidate()></input>') + '</div>';
|
||||
setDialogMode(2, "Share Device", 3, showShareDeviceEx, x);
|
||||
showShareDeviceValidate();
|
||||
var tomorrow = new Date();
|
||||
@ -7936,18 +7944,25 @@
|
||||
}
|
||||
|
||||
function showShareDeviceValidate() {
|
||||
if (currentNode.agent.caps & 1) { QV('d2userConsentSelector', Q('d2shareType').value < 8); }
|
||||
QV('d2httpPortSelector', Q('d2shareType').value == 8);
|
||||
QV('d2httpsPortSelector', Q('d2shareType').value == 9);
|
||||
QV('d2modenow', Q('d2timeRange').value == 0);
|
||||
QV('d2moderange', Q('d2timeRange').value == 1);
|
||||
QV('d2moderecurring', Q('d2timeRange').value >= 2);
|
||||
var ok = true;
|
||||
if (Q('d2shareType').value == 8) { var port = parseInt(Q('d2httpPort').value); if ((Q('d2httpPort').value != port) || (port < 1) || (port > 65535)) { ok = false; } }
|
||||
if (Q('d2shareType').value == 9) { var port = parseInt(Q('d2httpsPort').value); if ((Q('d2httpsPort').value != port) || (port < 1) || (port > 65535)) { ok = false; } }
|
||||
if (Q('d2inviteName').value.trim().length == 0) { ok = false; }
|
||||
QE('idx_dlgOkButton', ok);
|
||||
}
|
||||
|
||||
function showShareDeviceEx(b, tag) {
|
||||
var consent = 0, p = parseInt(Q('d2shareType').value), viewOnly = false, q = 0;
|
||||
if (p == 8) { meshserver.send({ action: 'createDeviceShareLink', nodeid: currentNode._id, guestname: Q('d2inviteName').value.trim(), p: 8, expire: parseInt(Q('d2inviteExpire').value), port: parseInt(Q('d2httpPort').value), consent: 0 }); return; }
|
||||
if (p == 9) { meshserver.send({ action: 'createDeviceShareLink', nodeid: currentNode._id, guestname: Q('d2inviteName').value.trim(), p: 16, expire: parseInt(Q('d2inviteExpire').value), port: parseInt(Q('d2httpsPort').value), consent: 0 }); return; }
|
||||
if (p == 3) { viewOnly = true; }
|
||||
var q = [0, 1, 2, 2, 4, 6, 5, 7][p]; // Protocol flags: 1 = Terminal, 2 = Desktop, 4 = Files.
|
||||
var q = [0, 1, 2, 2, 4, 6, 5, 7][p]; // Protocol flags: 1 = Terminal, 2 = Desktop, 4 = Files, 8 = HTTP, 16 = HTTPS.
|
||||
|
||||
if (q & 1) {
|
||||
consent |= 0x0002; // Terminal notify
|
||||
@ -12641,7 +12656,7 @@
|
||||
var dshare = deviceShares[i], trash = '';
|
||||
if (dshare.url != null) { trash += '<a href="' + dshare.url + '" rel="noreferrer noopener" target=_blank title="' + "Device Sharing Link" + '" style=cursor:pointer><img src=images/link2.png border=0 height=10 width=10></a> '; }
|
||||
trash += '<a href=# onclick=\'return p30removeDeviceSharing(event,"' + encodeURIComponentEx(dshare.nodeid) + '","' + encodeURIComponentEx(dshare.publicid) + '","' + encodeURIComponentEx(dshare.guestName) + '")\' title="' + "Remove device sharing" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>';
|
||||
var type = ['', "Terminal", "Desktop", "Desktop + Terminal", "Files", "Terminal + Files", "Desktop + Files", "Desktop + Terminal + Files"][dshare.p];
|
||||
var type = ''; if (dshare.p <= 7) { type = ['', "Terminal", "Desktop", "Desktop + Terminal", "Files", "Terminal + Files", "Desktop + Files", "Desktop + Terminal + Files"][dshare.p]; } else if (dshare.p == 8) { type = "HTTP/" + dshare.port; } else if (dshare.p == 16) { type = "HTTPS/" + dshare.port; }
|
||||
var details = type;
|
||||
if ((dshare.startTime != null) && (dshare.expireTime != null)) { details = format("{0}, {1} to {2}", type, printFlexDateTime(new Date(dshare.startTime)), printFlexDateTime(new Date(dshare.expireTime))); }
|
||||
if ((dshare.startTime != null) && (dshare.duration != null)) {
|
||||
|
@ -124,8 +124,11 @@ module.exports.CreateWebRelayServer = function (parent, db, args, certificates,
|
||||
return next();
|
||||
} else {
|
||||
// If this is a normal request (GET, POST, etc) handle it here
|
||||
if ((req.session.userid != null) && (req.session.x != null) && (parent.webserver.destroyedSessions[req.session.userid + '/' + req.session.x] == null)) {
|
||||
var relaySession = relaySessions[req.session.userid + '/' + req.session.x];
|
||||
var webSessionId = null;
|
||||
if ((req.session.userid != null) && (req.session.x != null)) { webSessionId = req.session.userid + '/' + req.session.x; }
|
||||
else if (req.session.z != null) { webSessionId = req.session.z; }
|
||||
if ((webSessionId != null) && (parent.webserver.destroyedSessions[webSessionId] == null)) {
|
||||
var relaySession = relaySessions[webSessionId];
|
||||
if (relaySession != null) {
|
||||
// The web relay session is valid, use it
|
||||
relaySession.handleRequest(req, res);
|
||||
@ -157,8 +160,11 @@ module.exports.CreateWebRelayServer = function (parent, db, args, certificates,
|
||||
|
||||
// Handle incoming web socket calls
|
||||
obj.app.ws('/*', function (ws, req) {
|
||||
if ((req.session.userid != null) && (req.session.x != null) && (parent.webserver.destroyedSessions[req.session.userid + '/' + req.session.x] == null)) {
|
||||
var relaySession = relaySessions[req.session.userid + '/' + req.session.x];
|
||||
var webSessionId = null;
|
||||
if ((req.session.userid != null) && (req.session.x != null)) { webSessionId = req.session.userid + '/' + req.session.x; }
|
||||
else if (req.session.z != null) { webSessionId = req.session.z; }
|
||||
if ((webSessionId != null) && (parent.webserver.destroyedSessions[webSessionId] == null)) {
|
||||
var relaySession = relaySessions[webSessionId];
|
||||
if (relaySession != null) {
|
||||
// The multi-tunnel session is valid, use it
|
||||
relaySession.handleWebSocket(ws, req);
|
||||
@ -178,55 +184,85 @@ module.exports.CreateWebRelayServer = function (parent, db, args, certificates,
|
||||
parent.debug('webrelay', 'webRelaySetup');
|
||||
|
||||
// Decode the relay cookie
|
||||
if (req.query.c != null) {
|
||||
// Decode and check if this relay cookie is valid
|
||||
const urlCookie = obj.parent.decodeCookie(req.query.c, parent.loginCookieEncryptionKey);
|
||||
if ((urlCookie != null) && (urlCookie.ruserid != null) && (urlCookie.x != null) && (parent.webserver.destroyedSessions[urlCookie.ruserid + '/' + urlCookie.x] == null)) {
|
||||
if (req.session.x != urlCookie.x) { req.session.x = urlCookie.x; } // Set the sessionid if missing
|
||||
if (req.session.userid != urlCookie.ruserid) { req.session.userid = urlCookie.ruserid; } // Set the session userid if missing
|
||||
}
|
||||
if (req.query.c == null) { res.sendStatus(404); return; }
|
||||
|
||||
// Decode and check if this relay cookie is valid
|
||||
var userid, domainid, domain, nodeid, addr, port, appid, webSessionId, expire;
|
||||
const urlCookie = obj.parent.decodeCookie(req.query.c, parent.loginCookieEncryptionKey);
|
||||
if (urlCookie == null) { res.sendStatus(404); return; }
|
||||
|
||||
// Decode the incomign cookie
|
||||
if ((urlCookie.ruserid != null) && (urlCookie.x != null)) {
|
||||
if (parent.webserver.destroyedSessions[urlCookie.ruserid + '/' + urlCookie.x] != null) { res.sendStatus(404); return; }
|
||||
|
||||
// This is a standard user, figure out what our web relay will be.
|
||||
if (req.session.x != urlCookie.x) { req.session.x = urlCookie.x; } // Set the sessionid if missing
|
||||
if (req.session.userid != urlCookie.ruserid) { req.session.userid = urlCookie.ruserid; } // Set the session userid if missing
|
||||
if (req.session.z) { delete req.session.z; } // Clear the web relay guest session
|
||||
userid = req.session.userid;
|
||||
domainid = userid.split('/')[1];
|
||||
domain = parent.config.domains[domainid];
|
||||
nodeid = ((req.query.relayid != null) ? req.query.relayid : req.query.n);
|
||||
addr = (req.query.addr != null) ? req.query.addr : '127.0.0.1';
|
||||
port = parseInt(req.query.p);
|
||||
appid = parseInt(req.query.appid);
|
||||
webSessionId = req.session.userid + '/' + req.session.x;
|
||||
|
||||
// Check that all the required arguments are present
|
||||
if ((req.session.userid == null) || (req.session.x == null) || (req.query.n == null) || (req.query.p == null) || (parent.webserver.destroyedSessions[webSessionId] != null) || ((req.query.appid != 1) && (req.query.appid != 2))) { res.redirect('/'); return; }
|
||||
} else if (urlCookie.r == 8) {
|
||||
// This is a guest user, figure out what our web relay will be.
|
||||
userid = urlCookie.userid;
|
||||
domainid = userid.split('/')[1];
|
||||
domain = parent.config.domains[domainid];
|
||||
nodeid = urlCookie.nid;
|
||||
addr = (urlCookie.addr != null) ? urlCookie.addr : '127.0.0.1';
|
||||
port = urlCookie.port;
|
||||
appid = (urlCookie.p == 16) ? 2 : 1; // appid: 1 = HTTP, 2 = HTTPS
|
||||
webSessionId = userid + '/' + urlCookie.pid;
|
||||
if (req.session.x) { delete req.session.x; } // Clear the web relay sessionid
|
||||
if (req.session.userid) { delete req.session.userid; } // Clear the web relay userid
|
||||
if (req.session.z != webSessionId) { req.session.z = webSessionId; } // Set the web relay guest session
|
||||
expire = urlCookie.expire;
|
||||
}
|
||||
|
||||
// Check that all the required arguments are present
|
||||
if ((req.session.userid == null) || (req.session.x == null) || (req.query.n == null) || (req.query.p == null) || (parent.webserver.destroyedSessions[req.session.userid + '/' + req.session.x] != null) || ((req.query.appid != 1) && (req.query.appid != 2))) { res.redirect('/'); return; }
|
||||
|
||||
// Get the user and domain information
|
||||
const userid = req.session.userid;
|
||||
const domainid = userid.split('/')[1];
|
||||
const domain = parent.config.domains[domainid];
|
||||
const nodeid = ((req.query.relayid != null) ? req.query.relayid : req.query.n);
|
||||
const addr = (req.query.addr != null) ? req.query.addr : '127.0.0.1';
|
||||
const port = parseInt(req.query.p);
|
||||
const appid = parseInt(req.query.appid);
|
||||
// No session identifier was setup, exit now
|
||||
if (webSessionId == null) { res.sendStatus(404); return; }
|
||||
|
||||
// Check to see if we already have a multi-relay session that matches exactly this device and port for this user
|
||||
const xrelaySession = relaySessions[req.session.userid + '/' + req.session.x];
|
||||
const xrelaySession = relaySessions[webSessionId];
|
||||
if ((xrelaySession != null) && (xrelaySession.domain.id == domain.id) && (xrelaySession.userid == userid) && (xrelaySession.nodeid == nodeid) && (xrelaySession.addr == addr) && (xrelaySession.port == port) && (xrelaySession.appid == appid)) {
|
||||
// We found an exact match, we are all setup already, redirect to root
|
||||
res.redirect('/');
|
||||
return;
|
||||
}
|
||||
|
||||
// There is a relay session, but it's not correct, close it.
|
||||
if (xrelaySession != null) { xrelaySession.close(); delete relaySessions[req.session.userid + '/' + req.session.x]; }
|
||||
// Check that the user has rights to access this device
|
||||
parent.webserver.GetNodeWithRights(domain, userid, nodeid, function (node, rights, visible) {
|
||||
// If there is no remote control rights, reject this web relay
|
||||
if ((rights & 8) == 0) { res.sendStatus(404); return; }
|
||||
|
||||
// Create a web relay session
|
||||
const relaySession = require('./apprelays.js').CreateWebRelaySession(obj, db, req, args, domain, userid, nodeid, addr, port, appid, xrelaySession);
|
||||
relaySession.onclose = function (sessionId) {
|
||||
// Remove the relay session
|
||||
delete relaySessions[sessionId];
|
||||
// If there are not more relay sessions, clear the cleanup timer
|
||||
if ((Object.keys(relaySessions).length == 0) && (obj.cleanupTimer != null)) { clearInterval(obj.cleanupTimer); obj.cleanupTimer = null; }
|
||||
}
|
||||
// There is a relay session, but it's not correct, close it.
|
||||
if (xrelaySession != null) { xrelaySession.close(); delete relaySessions[webSessionId]; }
|
||||
|
||||
// Set the multi-tunnel session
|
||||
relaySessions[userid + '/' + req.session.x] = relaySession;
|
||||
// Create a web relay session
|
||||
const relaySession = require('./apprelays.js').CreateWebRelaySession(obj, db, req, args, domain, userid, nodeid, addr, port, appid, xrelaySession, expire);
|
||||
relaySession.onclose = function (sessionId) {
|
||||
// Remove the relay session
|
||||
delete relaySessions[sessionId];
|
||||
// If there are not more relay sessions, clear the cleanup timer
|
||||
if ((Object.keys(relaySessions).length == 0) && (obj.cleanupTimer != null)) { clearInterval(obj.cleanupTimer); obj.cleanupTimer = null; }
|
||||
}
|
||||
|
||||
// Setup the cleanup timer if needed
|
||||
if (obj.cleanupTimer == null) { obj.cleanupTimer = setInterval(checkTimeout, 10000); }
|
||||
// Set the multi-tunnel session
|
||||
relaySessions[webSessionId] = relaySession;
|
||||
|
||||
// Redirect to root
|
||||
res.redirect('/');
|
||||
// Setup the cleanup timer if needed
|
||||
if (obj.cleanupTimer == null) { obj.cleanupTimer = setInterval(checkTimeout, 10000); }
|
||||
|
||||
// Redirect to root
|
||||
res.redirect('/');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
215
webserver.js
215
webserver.js
@ -3848,7 +3848,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
}
|
||||
|
||||
// Generate an old style cookie from the information in the database
|
||||
var cookie = { a: 5, p: doc.p, gn: doc.guestName, nid: doc.nodeid, cf: doc.consent, pid: doc.publicid, k: doc.extrakey };
|
||||
var cookie = { a: 5, p: doc.p, gn: doc.guestName, nid: doc.nodeid, cf: doc.consent, pid: doc.publicid, k: doc.extrakey ? doc.extrakey : null, port: doc.port };
|
||||
if (doc.userid) { cookie.uid = doc.userid; }
|
||||
if ((cookie.userid == null) && (cookie.pid.startsWith('AS:node/'))) { cookie.nouser = 1; }
|
||||
if (doc.startTime != null) {
|
||||
@ -3870,7 +3870,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
|
||||
// Check the public id
|
||||
obj.db.GetAllTypeNodeFiltered([c.nid], domain.id, 'deviceshare', null, function (err, docs) {
|
||||
// Check if any desktop sharing links are present, expire message.
|
||||
// Check if any sharing links are present, expire message.
|
||||
if ((err != null) || (docs.length == 0)) { render(req, res, getRenderPage((domain.sitestyle == 2) ? 'message2' : 'message', req, domain), getRenderArgs({ titleid: 2, msgid: 12, domainurl: encodeURIComponent(domain.url).replace(/'/g, '%27') }, req, domain)); return; }
|
||||
|
||||
// Search for the device share public identifier, expire message.
|
||||
@ -3886,22 +3886,43 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
// Check the start time, not yet valid message.
|
||||
if ((c.start != null) && (c.expire != null) && ((c.start > Date.now()) || (c.start > c.expire))) { render(req, res, getRenderPage((domain.sitestyle == 2) ? 'message2' : 'message', req, domain), getRenderArgs({ titleid: 2, msgid: 11, domainurl: encodeURIComponent(domain.url).replace(/'/g, '%27') }, req, domain)); return; }
|
||||
|
||||
// Looks good, let's create the outbound session cookies.
|
||||
// Consent flags are 1 = Notify, 8 = Prompt, 64 = Privacy Bar.
|
||||
const authCookieData = { userid: c.uid, domainid: domain.id, nid: c.nid, ip: req.clientIp, p: c.p, gn: c.gn, cf: c.cf, r: 8, expire: c.expire, pid: c.pid, vo: c.vo };
|
||||
if ((authCookieData.userid == null) && (authCookieData.pid.startsWith('AS:node/'))) { authCookieData.nouser = 1; }
|
||||
if (c.k != null) { authCookieData.k = c.k; }
|
||||
const authCookie = obj.parent.encodeCookie(authCookieData, obj.parent.loginCookieEncryptionKey);
|
||||
// If this is a web relay share, check if this feature is active
|
||||
if ((c.p == 8) || (c.p == 16)) {
|
||||
// This is a HTTP or HTTPS share
|
||||
var webRelayPort = ((args.relaydns != null) ? ((typeof args.aliasport == 'number') ? args.aliasport : args.port) : ((parent.webrelayserver != null) ? ((typeof args.relayaliasport == 'number') ? args.relayaliasport : parent.webrelayserver.port) : 0));
|
||||
if (webRelayPort == 0) { res.sendStatus(404); return; }
|
||||
|
||||
// Server features
|
||||
var features2 = 0;
|
||||
if (obj.args.allowhighqualitydesktop !== false) { features2 += 1; } // Enable AllowHighQualityDesktop (Default true)
|
||||
// Create the authentication cookie
|
||||
const authCookieData = { userid: c.uid, domainid: domain.id, nid: c.nid, ip: req.clientIp, p: c.p, gn: c.gn, r: 8, expire: c.expire, pid: c.pid, port: c.port };
|
||||
if ((authCookieData.userid == null) && (authCookieData.pid.startsWith('AS:node/'))) { authCookieData.nouser = 1; }
|
||||
const authCookie = obj.parent.encodeCookie(authCookieData, obj.parent.loginCookieEncryptionKey);
|
||||
|
||||
// Lets respond by sending out the desktop viewer.
|
||||
var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified
|
||||
parent.debug('web', 'handleSharingRequest: Sending guest sharing page for \"' + c.uid + '\", guest \"' + c.gn + '\".');
|
||||
res.set({ 'Cache-Control': 'no-store' });
|
||||
render(req, res, getRenderPage('sharing', req, domain), getRenderArgs({ authCookie: authCookie, authRelayCookie: '', domainurl: encodeURIComponent(domain.url).replace(/'/g, '%27'), nodeid: c.nid, serverDnsName: obj.getWebServerName(domain, req), serverRedirPort: args.redirport, serverPublicPort: httpsPort, expire: c.expire, viewOnly: (c.vo == 1) ? 1 : 0, nodeName: encodeURIComponent(node.name).replace(/'/g, '%27'), features: c.p, features2: features2 }, req, domain));
|
||||
// Redirect to a URL
|
||||
var webRelayDns = (args.relaydns != null) ? args.relaydns[0] : obj.getWebServerName(domain, req);
|
||||
var url = 'https://' + webRelayDns + ':' + webRelayPort + '/control-redirect.ashx?n=' + c.nid + '&p=' + c.port + '&appid=' + c.p + '&c=' + authCookie;
|
||||
if (c.addr != null) { url += '&addr=' + c.addr; }
|
||||
if (c.pid != null) { url += '&relayid=' + c.pid; }
|
||||
parent.debug('web', 'handleSharingRequest: Redirecting guest to HTTP relay page for \"' + c.uid + '\", guest \"' + c.gn + '\".');
|
||||
res.redirect(url);
|
||||
} else {
|
||||
// Looks good, let's create the outbound session cookies.
|
||||
// This is a desktop, terminal or files share. We need to display the sharing page.
|
||||
// Consent flags are 1 = Notify, 8 = Prompt, 64 = Privacy Bar.
|
||||
const authCookieData = { userid: c.uid, domainid: domain.id, nid: c.nid, ip: req.clientIp, p: c.p, gn: c.gn, cf: c.cf, r: 8, expire: c.expire, pid: c.pid, vo: c.vo };
|
||||
if ((authCookieData.userid == null) && (authCookieData.pid.startsWith('AS:node/'))) { authCookieData.nouser = 1; }
|
||||
if (c.k != null) { authCookieData.k = c.k; }
|
||||
const authCookie = obj.parent.encodeCookie(authCookieData, obj.parent.loginCookieEncryptionKey);
|
||||
|
||||
// Server features
|
||||
var features2 = 0;
|
||||
if (obj.args.allowhighqualitydesktop !== false) { features2 += 1; } // Enable AllowHighQualityDesktop (Default true)
|
||||
|
||||
// Lets respond by sending out the desktop viewer.
|
||||
var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified
|
||||
parent.debug('web', 'handleSharingRequest: Sending guest sharing page for \"' + c.uid + '\", guest \"' + c.gn + '\".');
|
||||
res.set({ 'Cache-Control': 'no-store' });
|
||||
render(req, res, getRenderPage('sharing', req, domain), getRenderArgs({ authCookie: authCookie, authRelayCookie: '', domainurl: encodeURIComponent(domain.url).replace(/'/g, '%27'), nodeid: c.nid, serverDnsName: obj.getWebServerName(domain, req), serverRedirPort: args.redirport, serverPublicPort: httpsPort, expire: c.expire, viewOnly: (c.vo == 1) ? 1 : 0, nodeName: encodeURIComponent(node.name).replace(/'/g, '%27'), features: c.p, features2: features2 }, req, domain));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -6549,32 +6570,56 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
parent.debug('web', 'webRelaySetup');
|
||||
|
||||
// Decode the relay cookie
|
||||
if (req.query.c != null) {
|
||||
// Decode and check if this relay cookie is valid
|
||||
const urlCookie = obj.parent.decodeCookie(req.query.c, obj.parent.loginCookieEncryptionKey);
|
||||
if ((urlCookie != null) && (urlCookie.ruserid != null) && (urlCookie.x != null)) {
|
||||
if (req.session.x != urlCookie.x) { req.session.x = urlCookie.x; } // Set the sessionid if missing
|
||||
if (req.session.userid != urlCookie.ruserid) { req.session.userid = urlCookie.ruserid; } // Set the session userid if missing
|
||||
}
|
||||
if (req.query.c == null) { res.sendStatus(404); return; }
|
||||
|
||||
// Decode and check if this relay cookie is valid
|
||||
var userid, domainid, domain, nodeid, addr, port, appid, webSessionId, expire;
|
||||
const urlCookie = obj.parent.decodeCookie(req.query.c, parent.loginCookieEncryptionKey);
|
||||
if (urlCookie == null) { res.sendStatus(404); return; }
|
||||
|
||||
// Decode the incomign cookie
|
||||
if ((urlCookie.ruserid != null) && (urlCookie.x != null)) {
|
||||
if (parent.webserver.destroyedSessions[urlCookie.ruserid + '/' + urlCookie.x] != null) { res.sendStatus(404); return; }
|
||||
|
||||
// This is a standard user, figure out what our web relay will be.
|
||||
if (req.session.x != urlCookie.x) { req.session.x = urlCookie.x; } // Set the sessionid if missing
|
||||
if (req.session.userid != urlCookie.ruserid) { req.session.userid = urlCookie.ruserid; } // Set the session userid if missing
|
||||
if (req.session.z) { delete req.session.z; } // Clear the web relay guest session
|
||||
userid = req.session.userid;
|
||||
domainid = userid.split('/')[1];
|
||||
domain = parent.config.domains[domainid];
|
||||
nodeid = ((req.query.relayid != null) ? req.query.relayid : req.query.n);
|
||||
addr = (req.query.addr != null) ? req.query.addr : '127.0.0.1';
|
||||
port = parseInt(req.query.p);
|
||||
appid = parseInt(req.query.appid);
|
||||
webSessionId = req.session.userid + '/' + req.session.x;
|
||||
|
||||
// Check that all the required arguments are present
|
||||
if ((req.session.userid == null) || (req.session.x == null) || (req.query.n == null) || (req.query.p == null) || (parent.webserver.destroyedSessions[webSessionId] != null) || ((req.query.appid != 1) && (req.query.appid != 2))) { res.redirect('/'); return; }
|
||||
} else if (urlCookie.r == 8) {
|
||||
// This is a guest user, figure out what our web relay will be.
|
||||
userid = urlCookie.userid;
|
||||
domainid = userid.split('/')[1];
|
||||
domain = parent.config.domains[domainid];
|
||||
nodeid = urlCookie.nid;
|
||||
addr = (urlCookie.addr != null) ? urlCookie.addr : '127.0.0.1';
|
||||
port = urlCookie.port;
|
||||
appid = (urlCookie.p == 16) ? 2 : 1; // appid: 1 = HTTP, 2 = HTTPS
|
||||
webSessionId = userid + '/' + urlCookie.pid;
|
||||
if (req.session.x) { delete req.session.x; } // Clear the web relay sessionid
|
||||
if (req.session.userid) { delete req.session.userid; } // Clear the web relay userid
|
||||
if (req.session.z != webSessionId) { req.session.z = webSessionId; } // Set the web relay guest session
|
||||
expire = urlCookie.expire;
|
||||
}
|
||||
|
||||
// Check that all the required arguments are present
|
||||
if ((req.session.userid == null) || (req.session.x == null) || (req.query.n == null) || (req.query.p == null) || ((obj.destroyedSessions[req.session.userid + '/' + req.session.x] != null)) || ((req.query.appid != 1) && (req.query.appid != 2))) { res.redirect('/'); return; }
|
||||
|
||||
// Get the user and domain information
|
||||
const userid = req.session.userid;
|
||||
const domainid = userid.split('/')[1];
|
||||
const domain = parent.config.domains[domainid];
|
||||
const nodeid = ((req.query.relayid != null) ? req.query.relayid : req.query.n);
|
||||
const addr = (req.query.addr != null) ? req.query.addr : '127.0.0.1';
|
||||
const port = parseInt(req.query.p);
|
||||
const appid = parseInt(req.query.appid);
|
||||
// No session identifier was setup, exit now
|
||||
if (webSessionId == null) { res.sendStatus(404); return; }
|
||||
|
||||
// Check that we have an exact session on any of the relay DNS names
|
||||
var xrelaySessionId, xrelaySession, freeRelayHost, oldestRelayTime, oldestRelayHost;
|
||||
for (var hostIndex in obj.args.relaydns) {
|
||||
const host = obj.args.relaydns[hostIndex];
|
||||
xrelaySessionId = req.session.userid + '/' + req.session.x + '/' + host;
|
||||
xrelaySessionId = webSessionId + '/' + host;
|
||||
xrelaySession = webRelaySessions[xrelaySessionId];
|
||||
if (xrelaySession == null) {
|
||||
// We found an unused hostname, save this as it could be useful.
|
||||
@ -6609,49 +6654,55 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there is a free relay DNS name we can use
|
||||
var selectedHost = null;
|
||||
if (freeRelayHost != null) {
|
||||
// There is a free one, use it.
|
||||
selectedHost = freeRelayHost;
|
||||
} else {
|
||||
// No free ones, close the oldest one
|
||||
selectedHost = oldestRelayHost;
|
||||
}
|
||||
xrelaySessionId = req.session.userid + '/' + req.session.x + '/' + selectedHost;
|
||||
// Check that the user has rights to access this device
|
||||
parent.webserver.GetNodeWithRights(domain, userid, nodeid, function (node, rights, visible) {
|
||||
// If there is no remote control rights, reject this web relay
|
||||
if ((rights & 8) == 0) { res.sendStatus(404); return; }
|
||||
|
||||
if (selectedHost == req.hostname) {
|
||||
// If this web relay session id is not free, close it now
|
||||
xrelaySession = webRelaySessions[xrelaySessionId];
|
||||
if (xrelaySession != null) { xrelaySession.close(); delete webRelaySessions[xrelaySessionId]; }
|
||||
|
||||
// Create a web relay session
|
||||
const relaySession = require('./apprelays.js').CreateWebRelaySession(obj, db, req, args, domain, userid, nodeid, addr, port, appid, xrelaySessionId);
|
||||
relaySession.onclose = function (sessionId) {
|
||||
// Remove the relay session
|
||||
delete webRelaySessions[sessionId];
|
||||
// If there are not more relay sessions, clear the cleanup timer
|
||||
if ((Object.keys(webRelaySessions).length == 0) && (obj.cleanupTimer != null)) { clearInterval(webRelayCleanupTimer); obj.cleanupTimer = null; }
|
||||
}
|
||||
|
||||
// Set the multi-tunnel session
|
||||
webRelaySessions[xrelaySessionId] = relaySession;
|
||||
|
||||
// Setup the cleanup timer if needed
|
||||
if (obj.cleanupTimer == null) { webRelayCleanupTimer = setInterval(checkWebRelaySessionsTimeout, 10000); }
|
||||
|
||||
// Redirect to root.
|
||||
res.redirect('/');
|
||||
} else {
|
||||
if (req.query.noredirect != null) {
|
||||
// No redirects allowed, fail here. This is important to make sure there is no redirect cascades
|
||||
res.sendStatus(404);
|
||||
// Check if there is a free relay DNS name we can use
|
||||
var selectedHost = null;
|
||||
if (freeRelayHost != null) {
|
||||
// There is a free one, use it.
|
||||
selectedHost = freeRelayHost;
|
||||
} else {
|
||||
// Request was made to a different host, redirect using the full URL so an HTTP cookie can be created on the other DNS name.
|
||||
const httpport = ((args.aliasport != null) ? args.aliasport : args.port);
|
||||
res.redirect('https://' + selectedHost + ((httpport != 443) ? (':' + httpport) : '') + req.url + '&noredirect=1');
|
||||
// No free ones, close the oldest one
|
||||
selectedHost = oldestRelayHost;
|
||||
}
|
||||
}
|
||||
xrelaySessionId = webSessionId + '/' + selectedHost;
|
||||
|
||||
if (selectedHost == req.hostname) {
|
||||
// If this web relay session id is not free, close it now
|
||||
xrelaySession = webRelaySessions[xrelaySessionId];
|
||||
if (xrelaySession != null) { xrelaySession.close(); delete webRelaySessions[xrelaySessionId]; }
|
||||
|
||||
// Create a web relay session
|
||||
const relaySession = require('./apprelays.js').CreateWebRelaySession(obj, db, req, args, domain, userid, nodeid, addr, port, appid, xrelaySessionId, expire);
|
||||
relaySession.onclose = function (sessionId) {
|
||||
// Remove the relay session
|
||||
delete webRelaySessions[sessionId];
|
||||
// If there are not more relay sessions, clear the cleanup timer
|
||||
if ((Object.keys(webRelaySessions).length == 0) && (obj.cleanupTimer != null)) { clearInterval(webRelayCleanupTimer); obj.cleanupTimer = null; }
|
||||
}
|
||||
|
||||
// Set the multi-tunnel session
|
||||
webRelaySessions[xrelaySessionId] = relaySession;
|
||||
|
||||
// Setup the cleanup timer if needed
|
||||
if (obj.cleanupTimer == null) { webRelayCleanupTimer = setInterval(checkWebRelaySessionsTimeout, 10000); }
|
||||
|
||||
// Redirect to root.
|
||||
res.redirect('/');
|
||||
} else {
|
||||
if (req.query.noredirect != null) {
|
||||
// No redirects allowed, fail here. This is important to make sure there is no redirect cascades
|
||||
res.sendStatus(404);
|
||||
} else {
|
||||
// Request was made to a different host, redirect using the full URL so an HTTP cookie can be created on the other DNS name.
|
||||
const httpport = ((args.aliasport != null) ? args.aliasport : args.port);
|
||||
res.redirect('https://' + selectedHost + ((httpport != 443) ? (':' + httpport) : '') + req.url + '&noredirect=1');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Handle all incoming requests as web relays
|
||||
@ -6956,8 +7007,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
|
||||
// Handle an incoming request as a web relay
|
||||
function handleWebRelayRequest(req, res) {
|
||||
if ((req.session.userid != null) && (req.session.x != null) && (obj.destroyedSessions[req.session.userid + '/' + req.session.x] == null)) {
|
||||
var relaySession = webRelaySessions[req.session.userid + '/' + req.session.x + '/' + req.hostname];
|
||||
var webRelaySessionId = null;
|
||||
if ((req.session.userid != null) && (req.session.x != null)) { webRelaySessionId = req.session.userid + '/' + req.session.x; }
|
||||
else if (req.session.z != null) { webRelaySessionId = req.session.z; }
|
||||
if ((webRelaySessionId != null) && (obj.destroyedSessions[webRelaySessionId] == null)) {
|
||||
var relaySession = webRelaySessions[webRelaySessionId + '/' + req.hostname];
|
||||
if (relaySession != null) {
|
||||
// The web relay session is valid, use it
|
||||
relaySession.handleRequest(req, res);
|
||||
@ -6973,8 +7027,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
|
||||
// Handle an incoming websocket connection as a web relay
|
||||
function handleWebRelayWebSocket(ws, req) {
|
||||
if ((req.session.userid != null) && (req.session.x != null) && (obj.destroyedSessions[req.session.userid + '/' + req.session.x] == null)) {
|
||||
var relaySession = webRelaySessions[req.session.userid + '/' + req.session.x + '/' + req.hostname];
|
||||
var webRelaySessionId = null;
|
||||
if ((req.session.userid != null) && (req.session.x != null)) { webRelaySessionId = req.session.userid + '/' + req.session.x; }
|
||||
else if (req.session.z != null) { webRelaySessionId = req.session.z; }
|
||||
if ((webRelaySessionId != null) && (obj.destroyedSessions[webRelaySessionId] == null)) {
|
||||
var relaySession = webRelaySessions[webRelaySessionId + '/' + req.hostname];
|
||||
if (relaySession != null) {
|
||||
// The multi-tunnel session is valid, use it
|
||||
relaySession.handleWebSocket(ws, req);
|
||||
|
Loading…
Reference in New Issue
Block a user