This commit is contained in:
stephannn 2025-10-23 07:51:56 +01:00 committed by GitHub
commit d5e2cf9aa8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 67 additions and 2 deletions

11
db.js
View File

@ -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); };
@ -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(); } }); };
@ -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); };
@ -2473,6 +2481,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 JSON_EXTRACT(doc, "$.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); };
@ -2784,6 +2793,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) {
@ -3031,6 +3041,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); };

View File

@ -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

View File

@ -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",

View File

@ -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; }

View File

@ -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;