Added Firebase Cloud Messaging support.

Ylian Saint-Hilaire committed Jan 30, 2021 at 16:15 UTC 3b2abf8498ba4eee1557ba4fee17014bdc88cc19
5 files changed +62 -9
meshagent.js
+3
@@ -1584,6 +1584,9 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
1584 if (JSON.stringify(device.wsc) != JSON.stringify(command.wsc)) { /*changes.push('Windows Security Center status');*/ device.wsc = command.wsc; change = 1; log = 1; }
1585 }
1586
1587 + // Push Messaging Token
1588 + if ((command.pmt != null) && (typeof command.pmt == 'string') && (device.pmt != command.pmt)) { device.pmt = command.pmt; change = 1; } // Don't save this as an event to the db.
1589 +
1590 if ((command.users != null) && (Array.isArray(command.users)) && (device.users != command.users)) { device.users = command.users; change = 1; } // Don't save this to the db.
1591 if ((mesh.mtype == 2) && (!args.wanonly)) {
1592 // In WAN mode, the hostname of a computer is not important. Don't log hostname changes.
meshcentral.js
+16 -1
@@ -678,7 +678,7 @@ function CreateMeshCentralServer(config, args) {
678 }
679
680 // Check top level configuration for any unreconized values
681 - if (config) { for (var i in config) { if ((typeof i == 'string') && (i.length > 0) && (i[0] != '_') && (['settings', 'domaindefaults', 'domains', 'configfiles', 'smtp', 'letsencrypt', 'peers', 'sms', 'sendgrid', '$schema'].indexOf(i) == -1)) { addServerWarning('Unrecognized configuration option \"' + i + '\".'); } } }
681 + if (config) { for (var i in config) { if ((typeof i == 'string') && (i.length > 0) && (i[0] != '_') && (['settings', 'domaindefaults', 'domains', 'configfiles', 'smtp', 'letsencrypt', 'peers', 'sms', 'sendgrid', 'firebase', '$schema'].indexOf(i) == -1)) { addServerWarning('Unrecognized configuration option \"' + i + '\".'); } } }
682
683 if (typeof obj.args.userallowedip == 'string') { if (obj.args.userallowedip == '') { config.settings.userallowedip = obj.args.userallowedip = null; } else { config.settings.userallowedip = obj.args.userallowedip = obj.args.userallowedip.split(','); } }
684 if (typeof obj.args.userblockedip == 'string') { if (obj.args.userblockedip == '') { config.settings.userblockedip = obj.args.userblockedip = null; } else { config.settings.userblockedip = obj.args.userblockedip = obj.args.userblockedip.split(','); } }
@@ -1544,6 +1544,18 @@ function CreateMeshCentralServer(config, args) {
1544 if ((obj.smsserver != null) && (obj.args.lanonly == true)) { addServerWarning("SMS gateway has limited use in LAN mode."); }
1545 }
1546
1547 + // Setup Firebase
1548 + if (config.firebase != null) {
1549 + try {
1550 + obj.firebase = require('firebase-admin');
1551 + var firebaseServiceAccount = require(obj.path.join(obj.datapath, config.firebase.serviceaccountkeyfile));
1552 + obj.firebase.initializeApp({ credential: obj.firebase.credential.cert(firebaseServiceAccount) });
1553 + } catch (ex) {
1554 + console.log('Unable to setup Firebase: ' + ex);
1555 + delete obj.firebase;
1556 + }
1557 + }
1558 +
1559 // Start periodic maintenance
1560 obj.maintenanceTimer = setInterval(obj.maintenanceActions, 1000 * 60 * 60); // Run this every hour
1561
@@ -3053,6 +3065,9 @@ function mainStart() {
3065 if (NodeJSVer < 8) { console.log("SMS Plivo support requires Node v8 or above, current version is " + process.version + "."); } else { modules.push('plivo'); }
3066 }
3067
3068 + // Firebase Support
3069 + if (config.firebase != null) { modules.push('firebase-admin'); }
3070 +
3071 // Syslog support
3072 if ((require('os').platform() != 'win32') && (config.settings.syslog || config.settings.syslogjson)) { modules.push('modern-syslog'); }
3073
meshuser.js
+29 -1
@@ -686,6 +686,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
686 if (!r[meshid]) { r[meshid] = []; }
687 delete docs[i].meshid;
688
689 + // Remove push messaging token if present
690 + if (docs[i].pmt != null) { docs[i].pmt = 1; }
691 +
692 // Remove Intel AMT credential if present
693 if (docs[i].intelamt != null) {
694 if (docs[i].intelamt.pass != null) { docs[i].intelamt.pass = 1; }
@@ -4060,7 +4063,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
4063 // Check if this user has rights on this nodeid
4064 if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
4065 db.Get(command.nodeid, function (err, nodes) { // TODO: Make a NodeRights(user) method that also does not do a db call if agent is connected (???)
4063 - if ((nodes == null) || (nodes.length == 1)) {
4066 + if ((err == null) && (nodes.length == 1)) {
4067 if ((parent.GetMeshRights(user, nodes[0].meshid) & MESHRIGHT_REMOTECONTROL) != 0) {
4068 // Add a user authentication cookie to a url
4069 var cookieContent = { userid: user._id, domainid: user.domain };
@@ -5191,6 +5194,31 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
5194 try { ws.send(JSON.stringify(responseCmd)); } catch (ex) { }
5195 break;
5196 }
5197 + case 'pushmessage': {
5198 + // Check if this user has rights on this nodeid
5199 + if (parent.parent.firebase == null) return;
5200 + if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
5201 + if (common.validateString(command.title, 1, 1024) == false) break; // Check title
5202 + if (common.validateString(command.msg, 1, 1024) == false) break; // Check message
5203 + db.Get(command.nodeid, function (err, nodes) { // TODO: Make a NodeRights(user) method that also does not do a db call if agent is connected (???)
5204 + if ((err == null) && (nodes.length == 1)) {
5205 + const node = nodes[0];
5206 + if (((parent.GetMeshRights(user, node.meshid) & MESHRIGHT_REMOTECONTROL) != 0) && (typeof node.pmt == 'string')) {
5207 + // Send out a push message to the device
5208 + var payload = { notification: { title: command.title, body: command.msg } };
5209 + var options = { priority: "Normal", timeToLive: 5 * 60 }; // TTL: 5 minutes
5210 + parent.parent.firebase.messaging().sendToDevice(node.pmt, payload, options)
5211 + .then(function (response) {
5212 + parent.parent.debug('email', 'Successfully send push message to device ' + node.name + ', title: ' + command.title + ', msg: ' + command.msg);
5213 + })
5214 + .catch(function (error) {
5215 + parent.parent.debug('email', 'Failed to send push message to device ' + node.name + ', title: ' + command.title + ', msg: ' + command.msg + ', error: ' + error);
5216 + });
5217 + }
5218 + }
5219 + });
5220 + break;
5221 + }
5222 case 'print': {
5223 console.log(command.value);
5224 break;
views/default.handlebars
+7 -3
@@ -5974,7 +5974,7 @@
5974 }
5975 x += '<input type=button value="' + "Notes" + '" title="' + "View notes about this device" + '" onclick=showNotes(' + ((meshrights & 128) == 0) + ',"' + encodeURIComponentEx(node._id) + '") />';
5976 x += '<input type=button value="' + "Log Event" + '" title="' + "Write an event for this device" + '" onclick=writeDeviceEvent("' + encodeURIComponentEx(node._id) + '") />';
5977 - if ((connectivity & 1) && (meshrights & 8) && (node.agent.id != 14)) { x += '<input type=button value="' + "Message" + '" title="' + "Display a text message on the remote device" + '" onclick=deviceMessageFunction() />'; }
5977 + if ((meshrights & 8) && ((connectivity & 1) || (node.pmt == 1))) { x += '<input type=button value="' + "Message" + '" title="' + "Display a text message on the remote device" + '" onclick=deviceMessageFunction() />'; }
5978 //if ((connectivity & 1) && (meshrights & 8) && (node.agent.id < 5)) { x += '<input type=button value=Toast title="' + "Display a text message of the remote device" + '" onclick=deviceToastFunction() />'; }
5979 if ((connectivity & 1) && (meshrights & 8) && (node.agent.id == 14)) { x += '<input type=button value="' + "Chat" + '" title="' + "Open chat window to this computer" + '" onclick=deviceChat(event) />'; }
5980 if ((serverinfo.guestdevicesharing !== false) && (node.agent != null) && (node.agent.caps & 3) && (connectivity & 1) && (meshrights & 0x80008) && ((meshrights == 0xFFFFFFFF) || ((meshrights & 0x1000) == 0))) { x += '<input type=button value="' + "Share" + '" title="' + "Create a link to share this device with a guest" + '" onclick=showShareDevice() />'; }
@@ -6338,7 +6338,11 @@
6338 }
6339
6340 function deviceMessageFunctionEx() {
6341 - meshserver.send({ action: 'msg', type: 'messagebox', nodeid: currentNode._id, title: decodeURIComponent('{{{extitle}}}'), msg: Q('d2devMessage').value });
6341 + if (currentNode.pmt == 1) {
6342 + meshserver.send({ action: 'pushmessage', nodeid: currentNode._id, title: decodeURIComponent('{{{extitle}}}'), msg: Q('d2devMessage').value });
6343 + } else {
6344 + meshserver.send({ action: 'msg', type: 'messagebox', nodeid: currentNode._id, title: decodeURIComponent('{{{extitle}}}'), msg: Q('d2devMessage').value });
6345 + }
6346 }
6347
6348 function deskClipboardInFunction() {
@@ -13752,7 +13756,7 @@
13756 x += '<div><label><input type=checkbox id=p41c14 ' + ((serverTraceSources.indexOf('agentupdate') >= 0) ? 'checked' : '') + '>' + "MeshAgent update" + '</label></div>';
13757 x += '<div><label><input type=checkbox id=p41c16 ' + ((serverTraceSources.indexOf('cert') >= 0) ? 'checked' : '') + '>' + "Server Certificate" + '</label></div>';
13758 x += '<div><label><input type=checkbox id=p41c17 ' + ((serverTraceSources.indexOf('db') >= 0) ? 'checked' : '') + '>' + "Server Database" + '</label></div>';
13755 - x += '<div><label><input type=checkbox id=p41c18 ' + ((serverTraceSources.indexOf('email') >= 0) ? 'checked' : '') + '>' + "Email/SMS Traffic" + '</label></div>';
13759 + x += '<div><label><input type=checkbox id=p41c18 ' + ((serverTraceSources.indexOf('email') >= 0) ? 'checked' : '') + '>' + "Email/SMS/Push Traffic" + '</label></div>';
13760 x += '<div style="width:100%;border-bottom:1px solid gray;margin-bottom:5px;margin-top:5px"><b>' + "Web Server" + '</b></div>';
13761 x += '<div><label><input type=checkbox id=p41c5 ' + ((serverTraceSources.indexOf('web') >= 0) ? 'checked' : '') + '>' + "Web Server" + '</label></div>';
13762 x += '<div><label><input type=checkbox id=p41c6 ' + ((serverTraceSources.indexOf('webrequest') >= 0) ? 'checked' : '') + '>' + "Web Server Requests" + '</label></div>';
webserver.js
+7 -4
@@ -6293,11 +6293,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
6293 obj.CloneSafeNode = function (node) {
6294 if (typeof node != 'object') { return node; }
6295 var r = node;
6296 - if ((r.intelamt != null) && ((r.intelamt.pass != null) || (r.intelamt.mpspass != null))) {
6296 + if ((r.pmt != null) || ((r.intelamt != null) && ((r.intelamt.pass != null) || (r.intelamt.mpspass != null)))) {
6297 r = Object.assign({}, r); // Shallow clone
6298 - r.intelamt = Object.assign({}, r.intelamt); // Shallow clone
6299 - if (r.intelamt.pass != null) { r.intelamt.pass = 1; }; // Remove the Intel AMT administrator password from the node
6300 - if (r.intelamt.mpspass != null) { r.intelamt.mpspass = 1; }; // Remove the Intel AMT MPS password from the node
6298 + if (r.pmt != null) { r.pmt = 1; }
6299 + if ((r.intelamt != null) && ((r.intelamt.pass != null) || (r.intelamt.mpspass != null))) {
6300 + r.intelamt = Object.assign({}, r.intelamt); // Shallow clone
6301 + if (r.intelamt.pass != null) { r.intelamt.pass = 1; }; // Remove the Intel AMT administrator password from the node
6302 + if (r.intelamt.mpspass != null) { r.intelamt.mpspass = 1; }; // Remove the Intel AMT MPS password from the node
6303 + }
6304 }
6305 return r;
6306 }