Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions db.js
Original file line number Diff line number Diff line change
Expand Up @@ -1656,6 +1656,12 @@ module.exports.CreateDB = function (parent, func) {
func(err, performTypedRecordDecrypt(docs));
});
}
obj.GetNodeByComputerName = function (domain, rname, func) {
sqlDbQuery('SELECT doc FROM main WHERE type = $1 AND domain = $2 AND JSON_EXTRACT(doc, "$.rname") = $3',
['node', domain, rname], function (err, docs) {
func(err, performTypedRecordDecrypt(docs));
});
};
obj.Remove = function (id, func) { sqlDbQuery('DELETE FROM main WHERE id = $1', [id], func); };
obj.RemoveAll = function (func) { sqlDbQuery('DELETE FROM main', null, func); };
obj.RemoveAllOfType = function (type, func) { sqlDbQuery('DELETE FROM main WHERE type = $1', [type], func); };
Expand Down Expand Up @@ -1926,6 +1932,7 @@ module.exports.CreateDB = function (parent, func) {
obj.GetAllIdsOfType = function (ids, domain, type, func) { obj.file.query('meshcentral').filter('_id', 'in', ids).filter('domain', '==', domain).filter('type', '==', type).get(function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, performTypedRecordDecrypt(docs)); }); };
obj.GetUserWithEmail = function (domain, email, func) { obj.file.query('meshcentral').filter('type', '==', 'user').filter('domain', '==', domain).filter('email', '==', email).get({ exclude: ['type'] }, function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, performTypedRecordDecrypt(docs)); }); };
obj.GetUserWithVerifiedEmail = function (domain, email, func) { obj.file.query('meshcentral').filter('type', '==', 'user').filter('domain', '==', domain).filter('email', '==', email).filter('emailVerified', '==', true).get({ exclude: ['type'] }, function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, performTypedRecordDecrypt(docs)); }); };
obj.GetNodeByComputerName = function (domain, rname, func) { obj.file.query('meshcentral').filter('type', '==', 'node').filter('domain', '==', domain).filter('rname', '==', rname).get(function (snapshots) { const docs = []; for (var i in snapshots) { docs.push(snapshots[i].val()); } func(null, performTypedRecordDecrypt(docs)); }); };
obj.Remove = function (id, func) { obj.file.ref('meshcentral').child(encodeURIComponent(id)).remove().then(function () { if (func) { func(); } }); };
obj.RemoveAll = function (func) { obj.file.query('meshcentral').remove().then(function () { if (func) { func(); } }); };
obj.RemoveAllOfType = function (type, func) { obj.file.query('meshcentral').filter('type', '==', type).remove().then(function () { if (func) { func(); } }); };
Expand Down Expand Up @@ -2213,6 +2220,7 @@ module.exports.CreateDB = function (parent, func) {
obj.GetAllIdsOfType = function (ids, domain, type, func) { sqlDbQuery('SELECT doc FROM main WHERE (id = ANY ($1)) AND domain = $2 AND type = $3', [ids, domain, type], function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); }
obj.GetUserWithEmail = function (domain, email, func) { sqlDbQuery('SELECT doc FROM main WHERE domain = $1 AND extra = $2', [domain, 'email/' + email], function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); }
obj.GetUserWithVerifiedEmail = function (domain, email, func) { sqlDbQuery('SELECT doc FROM main WHERE domain = $1 AND extra = $2', [domain, 'email/' + email], function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); }
obj.GetNodeByComputerName = function (domain, rname, func) { sqlDbQuery('SELECT doc FROM main WHERE type = $1 AND domain = $2 AND doc->>\'rname\' = $3', ['node', domain, rname], function (err, docs) { func(err, performTypedRecordDecrypt(docs)); });};
obj.Remove = function (id, func) { sqlDbQuery('DELETE FROM main WHERE id = $1', [id], func); };
obj.RemoveAll = function (func) { sqlDbQuery('DELETE FROM main', null, func); };
obj.RemoveAllOfType = function (type, func) { sqlDbQuery('DELETE FROM main WHERE type = $1', [type], func); };
Expand Down Expand Up @@ -2378,6 +2386,7 @@ module.exports.CreateDB = function (parent, func) {
obj.getPowerTimeline = function (nodeid, func) { sqlDbQuery('SELECT doc FROM power WHERE ((nodeid = $1) OR (nodeid = \'*\')) ORDER BY time ASC', [nodeid], func); };
obj.removeAllPowerEvents = function () { sqlDbQuery('DELETE FROM power', null, function (err, docs) { }); };
obj.removeAllPowerEventsForNode = function (nodeid) { if (nodeid == null) return; sqlDbQuery('DELETE FROM power WHERE nodeid = $1', [nodeid], function (err, docs) { }); };
obj.moveAllPowerEventsToNode = function (nodeid, oldnodeid) { if (nodeid == null) return; sqlDbQuery('UPDATE SET nodeid = $1 FROM power WHERE nodeid = $2', [nodeid, oldnodeid], function (err, docs) { }); };

// Database actions on the SMBIOS collection
obj.GetAllSMBIOS = function (func) { sqlDbQuery('SELECT doc FROM smbios', null, func); };
Expand Down Expand Up @@ -2473,6 +2482,7 @@ module.exports.CreateDB = function (parent, func) {
}
obj.GetUserWithEmail = function (domain, email, func) { sqlDbQuery('SELECT doc FROM main WHERE domain = ? AND extra = ?', [domain, 'email/' + email], function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); }
obj.GetUserWithVerifiedEmail = function (domain, email, func) { sqlDbQuery('SELECT doc FROM main WHERE domain = ? AND extra = ?', [domain, 'email/' + email], function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); }
obj.GetNodeByComputerName = function (domain, rname, func) { sqlDbQuery('SELECT doc FROM main WHERE type = ? AND domain = ? AND rname = ?', ['node', domain, rname], function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
obj.Remove = function (id, func) { sqlDbQuery('DELETE FROM main WHERE id = ?', [id], func); };
obj.RemoveAll = function (func) { sqlDbQuery('DELETE FROM main', null, func); };
obj.RemoveAllOfType = function (type, func) { sqlDbQuery('DELETE FROM main WHERE type = ?', [type], func); };
Expand Down Expand Up @@ -2784,6 +2794,7 @@ module.exports.CreateDB = function (parent, func) {
obj.GetAllIdsOfType = function (ids, domain, type, func) { obj.file.find({ type: type, domain: domain, _id: { $in: ids } }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
obj.GetUserWithEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
obj.GetUserWithVerifiedEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email, emailVerified: true }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
obj.GetNodeByComputerName = function (domain, rname, func) { obj.file.find({ type: 'node', domain: domain, rname: rname }).sort({ lastbootuptime: -1 }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };

// Bulk operations
if (parent.config.settings.mongodbbulkoperations) {
Expand Down Expand Up @@ -3031,6 +3042,7 @@ module.exports.CreateDB = function (parent, func) {
obj.GetAllIdsOfType = function (ids, domain, type, func) { obj.file.find({ type: type, domain: domain, _id: { $in: ids } }, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
obj.GetUserWithEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email }, { type: 0 }, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
obj.GetUserWithVerifiedEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email, emailVerified: true }, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
obj.GetNodeByComputerName = function (domain, rname, func) { obj.file.find({ type: 'node', domain: domain, rname: rname }, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
obj.Remove = function (id, func) { obj.file.remove({ _id: id }, func); };
obj.RemoveAll = function (func) { obj.file.remove({}, { multi: true }, func); };
obj.RemoveAllOfType = function (type, func) { obj.file.remove({ type: type }, { multi: true }, func); };
Expand Down
50 changes: 49 additions & 1 deletion meshagent.js
Original file line number Diff line number Diff line change
Expand Up @@ -741,12 +741,60 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
*/

// Check that the node exists
db.Get(obj.dbNodeKey, function (err, nodes) {
db.Get(obj.dbNodeKey, async function (err, nodes) {
if (obj.agentInfo == null) { return; }
var device, mesh;
var nodeExists = Boolean(false);

// See if this node exists in the database
if ((nodes == null) || (nodes.length == 0)) {
if(domain.preventduplicatedevices){

const existingNodes = await new Promise((resolve, reject) => {
db.GetNodeByComputerName(domain.id, obj.agentInfo.computerName, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});

if (!existingNodes || existingNodes.length === 0) {
// Device does not exist with the name
nodeExists = false;
} else {
// Device exists with the name
// Remove nodes with the same name
existingNodes.forEach((eNode) => {

parent.parent.debug('agent', 'Removing old dublicated node (' + eNode.rname + ', ' + eNode._id + ').');

db.Remove(eNode._id); // Remove node with that id
db.Remove('if' + eNode._id); // Remove interface information
db.Remove('nt' + eNode._id); // Remove notes
db.Remove('lc' + eNode._id); // Remove last connect time
db.Remove('si' + eNode._id); // Remove system information
db.Remove('al' + eNode._id); // Remove error log last time
if (db.RemoveSMBIOS) { db.RemoveSMBIOS(eNode._id); } // Remove SMBios data
db.RemoveAllNodeEvents(eNode._id); // Remove all events for this node
db.removeAllPowerEventsForNode(eNode._id); // Remove all power events for this node

parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(eNode.meshid, [eNode._id]), obj, { etype: 'node', action: 'removenode', nodeid: eNode._id, domain: eNode.domain, nolog: 1 });
});

// Set mesh from previous node
obj.dbMeshKey = existingNodes[0].meshid

nodeExists = false;

}
} else {
nodeExists = false;
}

} else {
nodeExists = true;
}

if (nodeExists == false) {
// This device does not exist, use the meshid given by the device

// Check if we already have too many devices for this domain
Expand Down
5 changes: 5 additions & 0 deletions meshcentral-config-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1249,6 +1249,11 @@
"type": "integer",
"default": null,
"description": "The maximum number of devices a user can see on the devices page at the same time. By default all devices will show, but this may need to be limited on servers with large number of devices."
},
"preventDuplicateDevices": {
"type": "boolean",
"default": false,
"description": "If true, devices with the same name gets removed before a new device with the identical name gets joined. MeshID gets transfered."
},
"unknownUserRootRedirect": {
"type": "string",
Expand Down
1 change: 1 addition & 0 deletions meshcentral.js
Original file line number Diff line number Diff line change
Expand Up @@ -1464,6 +1464,7 @@ function CreateMeshCentralServer(config, args) {
if ((i.length > 0) && (i[0] == '_')) { delete obj.config.domains[i]; continue; } // Remove any domains with names that start with _
if (typeof config.domains[i].auth == 'string') { config.domains[i].auth = config.domains[i].auth.toLowerCase(); }
if (obj.config.domains[i].limits == null) { obj.config.domains[i].limits = {}; }
if (obj.config.domains[i].preventduplicatedevices == null) { obj.config.domains[i].preventduplicatedevices = false; }
if (obj.config.domains[i].dns == null) { obj.config.domains[i].url = (i == '') ? '/' : ('/' + i + '/'); } else { obj.config.domains[i].url = '/'; }
obj.config.domains[i].id = i;
if ((typeof obj.config.domains[i].maxdeviceview != 'number') || (obj.config.domains[i].maxdeviceview < 1)) { delete obj.config.domains[i].maxdeviceview; }
Expand Down
2 changes: 1 addition & 1 deletion meshctrl.js
Original file line number Diff line number Diff line change
Expand Up @@ -1195,7 +1195,7 @@ function displayConfigHelp() {
}

function performConfigOperations(args) {
var domainValues = ['title', 'title2', 'titlepicture', 'trustedcert', 'welcomepicture', 'welcometext', 'userquota', 'meshquota', 'newaccounts', 'usernameisemail', 'newaccountemaildomains', 'newaccountspass', 'newaccountsrights', 'geolocation', 'lockagentdownload', 'userconsentflags', 'Usersessionidletimeout', 'auth', 'ldapoptions', 'ldapusername', 'ldapuserbinarykey', 'ldapuseremail', 'footer', 'certurl', 'loginKey', 'userallowedip', 'agentallowedip', 'agentnoproxy', 'agentconfig', 'orphanagentuser', 'httpheaders', 'yubikey', 'passwordrequirements', 'limits', 'amtacmactivation', 'redirects', 'sessionrecording', 'hide'];
var domainValues = ['title', 'title2', 'titlepicture', 'trustedcert', 'welcomepicture', 'welcometext', 'userquota', 'meshquota', 'newaccounts', 'usernameisemail', 'newaccountemaildomains', 'newaccountspass', 'newaccountsrights', 'geolocation', 'lockagentdownload', 'userconsentflags', 'Usersessionidletimeout', 'auth', 'ldapoptions', 'ldapusername', 'ldapuserbinarykey', 'ldapuseremail', 'footer', 'certurl', 'loginKey', 'userallowedip', 'agentallowedip', 'agentnoproxy', 'agentconfig', 'orphanagentuser', 'httpheaders', 'yubikey', 'passwordrequirements', 'limits', 'amtacmactivation', 'redirects', 'sessionrecording', 'hide', 'preventduplicatedevices'];
var domainObjectValues = ['ldapoptions', 'httpheaders', 'yubikey', 'passwordrequirements', 'limits', 'amtacmactivation', 'redirects', 'sessionrecording'];
var domainArrayValues = ['newaccountemaildomains', 'newaccountsrights', 'loginkey', 'agentconfig'];
var configChange = false;
Expand Down