Added agent tag processing.

Ylian Saint-Hilaire committed Sep 23, 2020 at 15:34 UTC 61cc2399cc173fe16680d1fc64849050aedf65e5
2 files changed +93 -9
meshagent.js
+82 -8
@@ -1474,13 +1474,17 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
1474 if (obj.agentInfo.capabilities & 0x40) return;
1475 if ((command == null) || (command == null)) return; // Safety, should never happen.
1476
1477 + // If the device is pending a change, hold.
1478 + if (obj.deviceChanging === true) { setTimeout(function () { ChangeAgentCoreInfo(command); }, 100); return; }
1479 + obj.deviceChanging = true;
1480 +
1481 // Check that the mesh exists
1482 const mesh = parent.meshes[obj.dbMeshKey];
1479 - if (mesh == null) return;
1483 + if (mesh == null) { delete obj.deviceChanging; return; }
1484
1485 // Get the node and change it if needed
1486 db.Get(obj.dbNodeKey, function (err, nodes) { // TODO: THIS IS A BIG RACE CONDITION HERE, WE NEED TO FIX THAT. If this call is made twice at the same time on the same device, data will be missed.
1483 - if ((nodes == null) || (nodes.length != 1)) return;
1487 + if ((nodes == null) || (nodes.length != 1)) { delete obj.deviceChanging; return; }
1488 const device = nodes[0];
1489 if (device.agent) {
1490 var changes = [], change = 0, log = 0;
@@ -1538,6 +1542,9 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
1542 if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come.
1543 parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(device.meshid, [obj.dbNodeKey]), obj, event);
1544 }
1545 +
1546 + // Device change is done.
1547 + delete obj.deviceChanging;
1548 }
1549 });
1550 }
@@ -1551,9 +1558,13 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
1558 const mesh = parent.meshes[obj.dbMeshKey];
1559 if (mesh == null) return;
1560
1561 + // If the device is pending a change, hold.
1562 + if (obj.deviceChanging === true) { setTimeout(function () { ChangeAgentLocationInfo(command); }, 100); return; }
1563 + obj.deviceChanging = true;
1564 +
1565 // Get the node and change it if needed
1566 db.Get(obj.dbNodeKey, function (err, nodes) {
1556 - if ((nodes == null) || (nodes.length != 1)) { return; }
1567 + if ((nodes == null) || (nodes.length != 1)) { delete obj.deviceChanging; return; }
1568 const device = nodes[0];
1569 if (device.agent) {
1570 var changes = [], change = 0;
@@ -1580,27 +1591,87 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
1591 parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(device.meshid, [obj.dbNodeKey]), obj, event);
1592 }
1593 }
1594 +
1595 + // Done changing the device
1596 + delete obj.deviceChanging;
1597 });
1598 }
1599
1600 // Update the mesh agent tab in the database
1601 function ChangeAgentTag(tag) {
1602 if (obj.agentInfo.capabilities & 0x40) return;
1589 - if (tag.length == 0) { tag = null; }
1603 + if ((tag != null) && (tag.length == 0)) { tag = null; }
1604 +
1605 + // If the device is pending a change, hold.
1606 + if (obj.deviceChanging === true) { setTimeout(function () { ChangeAgentCoreInfo(command); }, 100); return; }
1607 + obj.deviceChanging = true;
1608 +
1609 // Get the node and change it if needed
1610 db.Get(obj.dbNodeKey, function (err, nodes) {
1592 - if ((nodes == null) || (nodes.length != 1)) return;
1611 + if ((nodes == null) || (nodes.length != 1)) { delete obj.deviceChanging; return; }
1612 const device = nodes[0];
1613 if (device.agent) {
1595 - if (device.agent.tag != tag) {
1614 + // Parse the agent tag
1615 + var agentTag = null, serverName = null, serverDesc = null, serverTags = null;
1616 + if (tag != null) {
1617 + var taglines = tag.split('\r\n').join('\n').split('\r').join('\n').split('\n');
1618 + for (var i in taglines) {
1619 + var tagline = taglines[i].trim();
1620 + if (tagline.length > 0) {
1621 + if (tagline.startsWith('~')) {
1622 + if (tagline.startsWith('~ServerName:') && (tagline.length > 12) && (serverName == null)) { serverName = tagline.substring(12).trim(); }
1623 + if (tagline.startsWith('~ServerDesc:') && (tagline.length > 12) && (serverDesc == null)) { serverDesc = tagline.substring(12).trim(); }
1624 + if (tagline.startsWith('~ServerTags:') && (tagline.length > 12) && (serverTags == null)) { serverTags = tagline.substring(12).split(','); for (var j in serverTags) { serverTags[j] = serverTags[j].trim(); } }
1625 + } else { if (agentTag == null) { agentTag = tagline; } }
1626 + }
1627 + }
1628 + }
1629 +
1630 + // Set the agent tag
1631 + var changes = false;
1632 + if (device.agent.tag != agentTag) { device.agent.tag = agentTag; if ((device.agent.tag == null) || (device.agent.tag == '')) { delete device.agent.tag; } changes = true; }
1633 + if (domain.agenttag != null) {
1634 + // Set the device's server name
1635 + if ((serverName != null) && (domain.agenttag.servername === 1) && (device.name != serverName)) { device.name = serverName; changes = true; }
1636 +
1637 + // Set the device's server description
1638 + if ((serverDesc != null) && (domain.agenttag.serverdesc === 1) && (device.desc != serverDesc)) { device.desc = serverDesc; changes = true; }
1639 +
1640 + // Set the device's server description if there is no description
1641 + if ((serverDesc != null) && (domain.agenttag.serverdesc === 2) && (device.desc != serverDesc) && ((device.desc == null) || (device.desc == ''))) { device.desc = serverDesc; changes = true; }
1642 +
1643 + if ((serverTags != null) && (domain.agenttag.servertags != null) && (domain.agenttag.servertags != 0)) {
1644 + // Sort the tags
1645 + serverTags.sort();
1646 +
1647 + // Stringify the tags
1648 + var st2 = '', st1 = serverTags.join(',');
1649 + if (device.tags != null) { st2 = device.tags.join(','); }
1650 +
1651 + // Set the device's server tags
1652 + if ((domain.agenttag.servertags === 1) && (st1 != st2)) { device.tags = serverTags; changes = true; }
1653 +
1654 + // Set the device's server tags if there are not tags
1655 + if ((domain.agenttag.servertags === 2) && (st2 == '')) { device.tags = serverTags; changes = true; }
1656 +
1657 + // Append to device's server tags
1658 + if ((domain.agenttag.servertags === 3) && (st1 != st2)) {
1659 + if (device.tags == null) { device.tags = []; }
1660 + for (var i in serverTags) { if (device.tags.indexOf(serverTags[i]) == -1) { device.tags.push(serverTags[i]); } }
1661 + device.tags.sort();
1662 + changes = true;
1663 + }
1664 + }
1665 + }
1666 +
1667 + if (changes == true) {
1668 // Do some clean up if needed, these values should not be in the database.
1669 if (device.conn != null) { delete device.conn; }
1670 if (device.pwr != null) { delete device.pwr; }
1671 if (device.agct != null) { delete device.agct; }
1672 if (device.cict != null) { delete device.cict; }
1673
1602 - // Set the new tag
1603 - device.agent.tag = tag;
1674 + // Update the device
1675 db.Set(device);
1676
1677 // Event the node change
@@ -1609,6 +1680,9 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
1680 parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(device.meshid, [obj.dbNodeKey]), obj, event);
1681 }
1682 }
1683 +
1684 + // Done changing the device
1685 + delete obj.deviceChanging;
1686 });
1687 }
1688
meshcentral-config-schema.json
+11 -1
@@ -196,6 +196,16 @@
196 "ldapOptions": { "type": "object", "description": "LDAP options passed to ldapauth-fork" },
197 "agentInviteCodes": { "type": "boolean", "default": false, "description": "Enabled a feature where you can set one or more invitation codes in a device group. You can then give a invitation link to users who can use it to download the agent." },
198 "agentNoProxy": { "type": "boolean", "default": false, "description": "When enabled, all newly installed MeshAgents will be instructed to no use a HTTP/HTTPS proxy even if one is configured on the remote system" },
199 + "agentTag": {
200 + "type": "object",
201 + "description": "This section is used to indicate if parts of the meshagent.tag file should be used to set server-side device properties.",
202 + "additionalProperties": false,
203 + "properties": {
204 + "ServerName": { "type": "integer", "default": 0, "description": "Action taken if one of the lines in meshagent.tag contains ~ServerName:name. 0=Ignore, 1=Set." },
205 + "ServerDesc": { "type": "integer", "default": 0, "description": "Action taken if one of the lines in meshagent.tag contains ~ServerDesc:desc. 0=Ignore, 1=Set, 2=SetIfEmpty." },
206 + "ServerTags": { "type": "integer", "default": 0, "description": "Action taken if one of the lines in meshagent.tag contains ~ServerTags:tag1,tag2,tag3. 0=Ignore, 1=Set, 2=SetIfEmpty, 3=Append." }
207 + }
208 + },
209 "geoLocation": { "type": "boolean", "default": false, "description": "Enables the geo-location feature and device location map in the user interface, this feature is not being worked on." },
210 "novnc": { "type": "boolean", "default": true, "description": "When enabled, activates the built-in web-based noVNC client." },
211 "mstsc": { "type": "boolean", "default": false, "description": "When enabled, activates the built-in web-based RDP client." },
@@ -204,7 +214,7 @@
214 "customUI": { "type": "object" },
215 "consentMessages": {
216 "type": "object",
207 - "description": "This section is user to customize user consent prompts, these show up when asking if a remote session is allowed or not.",
217 + "description": "This section is used to customize user consent prompts, these show up when asking if a remote session is allowed or not.",
218 "additionalProperties": false,
219 "properties": {
220 "Title": { "type": "string" },