diff --git a/common.js b/common.js index 456e0292..a7e0aa5a 100644 --- a/common.js +++ b/common.js @@ -147,6 +147,7 @@ module.exports.escapeFieldName = function (name) { if ((name.indexOf('%') == -1) module.exports.unEscapeFieldName = function (name) { if (name.indexOf('%') == -1) return name; return name.split('%2E').join('.').split('%24').join('$').split('%25').join('%'); }; // Escape all links +module.exports.escapeLinksFieldNameEx = function (docx) { if (docx.links == null) { return docx; } var doc = Object.assign({}, docx); doc.links = Object.assign({}, doc.links); for (var i in doc.links) { var ue = module.exports.escapeFieldName(i); if (ue !== i) { doc.links[ue] = doc.links[i]; delete doc.links[i]; } } return doc; }; module.exports.escapeLinksFieldName = function (docx) { var doc = Object.assign({}, docx); if (doc.links != null) { doc.links = Object.assign({}, doc.links); for (var i in doc.links) { var ue = module.exports.escapeFieldName(i); if (ue !== i) { doc.links[ue] = doc.links[i]; delete doc.links[i]; } } } return doc; }; module.exports.unEscapeLinksFieldName = function (doc) { if (doc.links != null) { for (var j in doc.links) { var ue = module.exports.unEscapeFieldName(j); if (ue !== j) { doc.links[ue] = doc.links[j]; delete doc.links[j]; } } } return doc; }; //module.exports.escapeAllLinksFieldName = function (docs) { for (var i in docs) { module.exports.escapeLinksFieldName(docs[i]); } return docs; }; diff --git a/db.js b/db.js index ea3fb25d..1940ebd1 100644 --- a/db.js +++ b/db.js @@ -789,11 +789,17 @@ module.exports.CreateDB = function (parent, func) { // Database actions on the main collection (MariaDB or MySQL) obj.Set = function (value, func) { var extra = null, extraex = null; + value = common.escapeLinksFieldNameEx(value); if (value.meshid) { extra = value.meshid; } else if (value.email) { extra = 'email/' + value.email; } if ((value.type == 'node') && (value.intelamt != null) && (value.intelamt.uuid != null)) { extraex = 'uuid/' + value.intelamt.uuid; } sqlDbQuery('REPLACE INTO meshcentral.main VALUE (?, ?, ?, ?, ?, ?)', [value._id, (value.type ? value.type : null), ((value.domain != null) ? value.domain : null), extra, extraex, JSON.stringify(performTypedRecordEncrypt(value))], func); } - obj.Get = function (_id, func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE id = ?', [_id], func); } + obj.Get = function (_id, func) { + sqlDbQuery('SELECT doc FROM meshcentral.main WHERE id = ?', [_id], function (err, docs) { + if ((docs != null) && (docs.length > 0) && (docs[0].links != null)) { docs[0] = common.unEscapeLinksFieldName(docs[0]); } + func(_id, func); + }); + } obj.GetAll = function (func) { sqlDbQuery('SELECT domain, doc FROM meshcentral.main', null, func); } obj.GetHash = function (id, func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE id = ?', [id], func); } obj.GetAllTypeNoTypeField = function (type, domain, func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE type = ? AND domain = ?', [type, domain], function (err, docs) { if (err == null) { for (var i in docs) { delete docs[i].type } } func(err, docs); }); }; @@ -920,7 +926,7 @@ module.exports.CreateDB = function (parent, func) { } } else if (obj.databaseType == 3) { // Database actions on the main collection (MongoDB) - obj.Set = function (data, func) { obj.file.replaceOne({ _id: data._id }, performTypedRecordEncrypt(data), { upsert: true }, func); }; + obj.Set = function (data, func) { data = common.escapeLinksFieldNameEx(data); obj.file.replaceOne({ _id: data._id }, performTypedRecordEncrypt(data), { upsert: true }, func); }; obj.Get = function (id, func) { if (arguments.length > 2) { var parms = [func]; @@ -932,9 +938,15 @@ module.exports.CreateDB = function (parent, func) { userCallback.apply(obj, _func2.userArgs); }; func2.userArgs = parms; - obj.file.find({ _id: id }).toArray(function (err, docs) { func2(err, performTypedRecordDecrypt(docs)); }); + obj.file.find({ _id: id }).toArray(function (err, docs) { + if ((docs != null) && (docs.length > 0) && (docs[0].links != null)) { docs[0] = common.unEscapeLinksFieldName(docs[0]); } + func2(err, performTypedRecordDecrypt(docs)); + }); } else { - obj.file.find({ _id: id }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); + obj.file.find({ _id: id }).toArray(function (err, docs) { + if ((docs != null) && (docs.length > 0) && (docs[0].links != null)) { docs[0] = common.unEscapeLinksFieldName(docs[0]); } + func(err, performTypedRecordDecrypt(docs)); + }); } }; obj.GetAll = function (func) { obj.file.find({}).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); }; @@ -1052,7 +1064,7 @@ module.exports.CreateDB = function (parent, func) { } else { // Database actions on the main collection (NeDB and MongoJS) - obj.Set = function (data, func) { var xdata = performTypedRecordEncrypt(data); obj.file.update({ _id: xdata._id }, xdata, { upsert: true }, func); }; + obj.Set = function (data, func) { data = common.escapeLinksFieldNameEx(data); var xdata = performTypedRecordEncrypt(data); obj.file.update({ _id: xdata._id }, xdata, { upsert: true }, func); }; obj.Get = function (id, func) { if (arguments.length > 2) { var parms = [func]; @@ -1064,9 +1076,15 @@ module.exports.CreateDB = function (parent, func) { userCallback.apply(obj, _func2.userArgs); }; func2.userArgs = parms; - obj.file.find({ _id: id }, function (err, docs) { func2(err, performTypedRecordDecrypt(docs)); }); + obj.file.find({ _id: id }, function (err, docs) { + if ((docs != null) && (docs.length > 0) && (docs[0].links != null)) { docs[0] = common.unEscapeLinksFieldName(docs[0]); } + func2(err, performTypedRecordDecrypt(docs)); + }); } else { - obj.file.find({ _id: id }, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); + obj.file.find({ _id: id }, function (err, docs) { + if ((docs != null) && (docs.length > 0) && (docs[0].links != null)) { docs[0] = common.unEscapeLinksFieldName(docs[0]); } + func(err, performTypedRecordDecrypt(docs)); + }); } }; obj.GetAll = function (func) { obj.file.find({}, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); }; @@ -1323,6 +1341,7 @@ module.exports.CreateDB = function (parent, func) { // Called when a node has changed function dbNodeChange(nodeChange, added) { + common.unEscapeLinksFieldName(nodeChange.fullDocument); const node = nodeChange.fullDocument; if (node.intelamt && node.intelamt.pass) { delete node.intelamt.pass; } // Remove the Intel AMT password before eventing this. parent.DispatchEvent(['*', node.meshid], obj, { etype: 'node', action: (added ? 'addnode' : 'changenode'), node: node, nodeid: node._id, domain: node.domain, nolog: 1 }); diff --git a/meshagent.js b/meshagent.js index e062d491..5841b644 100644 --- a/meshagent.js +++ b/meshagent.js @@ -549,7 +549,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { const links = {}; links[adminUser._id] = { name: adminUser.name, rights: 0xFFFFFFFF }; mesh = { type: 'mesh', _id: obj.dbMeshKey, name: meshname, mtype: 2, desc: '', domain: domain.id, links: links }; - db.Set(common.escapeLinksFieldName(mesh)); + db.Set(mesh); parent.meshes[obj.dbMeshKey] = mesh; if (adminUser.links == null) adminUser.links = {}; @@ -580,7 +580,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { // Mark the mesh as active delete mesh.deleted; - db.Set(common.escapeLinksFieldName(mesh)); + db.Set(mesh); } } return mesh; @@ -638,7 +638,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { var links = {}; links[user._id] = { name: adminUser.name, rights: 0xFFFFFFFF }; mesh = { type: 'mesh', _id: obj.dbMeshKey, name: obj.meshid, mtype: 2, desc: '', domain: domain.id, links: links }; - db.Set(common.escapeLinksFieldName(mesh)); + db.Set(mesh); parent.meshes[obj.meshid] = mesh; parent.parent.AddEventDispatch(parent.CreateMeshDispatchTargets(obj.meshid, [obj.dbNodeKey]), ws); diff --git a/meshcentral.js b/meshcentral.js index 63e6fe1a..4c01aca0 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -815,8 +815,8 @@ function CreateMeshCentralServer(config, args) { objectToAdd.push(newobj); // Add this user } } else if (newobj.type == 'mesh') { - // Add this object after escaping - objectToAdd.push(obj.common.escapeLinksFieldName(newobj)); + // Add this object + objectToAdd.push(newobj); } // Don't add nodes. } console.log('Importing ' + objectToAdd.length + ' object(s)...'); diff --git a/meshuser.js b/meshuser.js index f51982cf..638a8244 100644 --- a/meshuser.js +++ b/meshuser.js @@ -1268,7 +1268,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use mesh = parent.meshes[meshid]; if (mesh) { // Remove user from the mesh - if (mesh.links[deluser._id] != null) { delete mesh.links[deluser._id]; parent.db.Set(common.escapeLinksFieldName(mesh)); } + if (mesh.links[deluser._id] != null) { delete mesh.links[deluser._id]; parent.db.Set(mesh); } // Notify mesh change change = 'Removed user ' + deluser.name + ' from group ' + mesh.name; var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id, invite: mesh.invite }; @@ -1664,7 +1664,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if (xmesh && xmesh.links) { ugrp.links[i] = { rights: cgroup.links[i].rights }; xmesh.links[ugrpid] = { rights: cgroup.links[i].rights }; - db.Set(common.escapeLinksFieldName(xmesh)); + db.Set(xmesh); // Notify mesh change var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: xmesh._id, name: xmesh.name, mtype: xmesh.mtype, desc: xmesh.desc, action: 'meshchange', links: xmesh.links, msg: 'Added group ' + ugrp.name + ' to mesh ' + xmesh.name, domain: domain.id, invite: mesh.invite }; @@ -1678,7 +1678,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use } // Save the new group - db.Set(common.escapeLinksFieldName(ugrp)); + db.Set(ugrp); if (db.changeStream == false) { parent.userGroups[ugrpid] = ugrp; } // Event the device group creation @@ -1703,7 +1703,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use db.Get(command.ugrpid, function (err, groups) { if ((err != null) || (groups.length != 1)) return; - var group = common.unEscapeLinksFieldName(groups[0]); + var group = groups[0]; // Unlink any user and meshes that have a link to this group if (group.links) { @@ -1725,7 +1725,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use var xmesh = parent.meshes[i]; if (xmesh && xmesh.links) { delete xmesh.links[group._id]; - db.Set(common.escapeLinksFieldName(xmesh)); + db.Set(xmesh); // Notify mesh change var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: xmesh._id, name: xmesh.name, mtype: xmesh.mtype, desc: xmesh.desc, action: 'meshchange', links: xmesh.links, msg: 'Removed group ' + group.name + ' from mesh ' + xmesh.name, domain: domain.id, invite: mesh.invite }; @@ -1762,7 +1762,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if ((common.validateString(command.name, 1, 64) == true) && (command.name != group.name) && (command.name.indexOf(' ') == -1)) { change = 'User group name changed from "' + group.name + '" to "' + command.name + '"'; group.name = command.name; } if ((common.validateString(command.desc, 0, 1024) == true) && (command.desc != group.desc)) { if (change != '') change += ' and description changed'; else change += 'User group "' + group.name + '" description changed'; group.desc = command.desc; } if (change != '') { - db.Set(common.escapeLinksFieldName(group)); + db.Set(group); var event = { etype: 'ugrp', userid: user._id, username: user.name, ugrpid: group._id, name: group.name, desc: group.desc, action: 'usergroupchange', links: group.links, msg: change, domain: domain.id }; if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the mesh. Another event will come. parent.parent.DispatchEvent(['*', group._id, user._id], obj, event); @@ -1822,7 +1822,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if (addedCount > 0) { // Save the new group to the database - db.Set(common.escapeLinksFieldName(group)); + db.Set(group); // Notify user group change var event = { etype: 'ugrp', userid: user._id, username: user.name, ugrpid: group._id, name: group.name, desc: group.desc, action: 'usergroupchange', links: group.links, msg: 'Added user ' + chguser.name + ' to user group ' + group.name, domain: domain.id }; @@ -1884,7 +1884,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if ((group.links != null) && (group.links[command.userid] != null)) { change = true; delete group.links[command.userid]; - db.Set(common.escapeLinksFieldName(group)); + db.Set(group); // Notify user group change if (change) { @@ -2158,7 +2158,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use var links = {}; links[user._id] = { name: user.name, rights: 4294967295 }; mesh = { type: 'mesh', _id: meshid, name: command.meshname, mtype: command.meshtype, desc: command.desc, domain: domain.id, links: links }; - db.Set(common.escapeLinksFieldName(mesh)); + db.Set(mesh); parent.meshes[meshid] = mesh; parent.parent.AddEventDispatch([meshid], ws); @@ -2230,7 +2230,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use var xgroup = parent.userGroups[j]; if (xgroup && xgroup.links) { delete xgroup.links[mesh._id]; - db.Set(common.escapeLinksFieldName(xgroup)); + db.Set(xgroup); // Notify user group change var targets = ['*', 'server-ugroups', user._id, xgroup._id]; @@ -2251,7 +2251,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Mark the mesh as deleted mesh.deleted = new Date(); // Mark the time this mesh was deleted, we can expire it at some point. - db.Set(common.escapeLinksFieldName(mesh)); // We don't really delete meshes because if a device connects to is again, we will un-delete it. + db.Set(mesh); // We don't really delete meshes because if a device connects to is again, we will un-delete it. // Delete all devices attached to this mesh in the database db.RemoveMeshDocuments(command.meshid); @@ -2304,7 +2304,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use } if (change != '') { - db.Set(common.escapeLinksFieldName(mesh)); + db.Set(mesh); var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, flags: mesh.flags, consent: mesh.consent, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id, invite: mesh.invite }; if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the mesh. Another event will come. parent.parent.DispatchEvent(['*', mesh._id, user._id], obj, event); @@ -2356,7 +2356,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if (newuser.links == null) { newuser.links = {}; } if (newuser.links[command.meshid]) { newuser.links[command.meshid].rights = command.meshadmin; } else { newuser.links[command.meshid] = { rights: command.meshadmin }; } if (newuserid.startsWith('user/')) { db.SetUser(newuser); } - else if (newuserid.startsWith('ugrp/')) { db.Set(common.escapeLinksFieldName(newuser)); } + else if (newuserid.startsWith('ugrp/')) { db.Set(newuser); } parent.parent.DispatchEvent([newuser._id], obj, 'resubscribe'); if (newuserid.startsWith('user/')) { @@ -2375,7 +2375,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Add userid to the mesh mesh.links[newuserid] = { name: newuser.name, rights: command.meshadmin }; - db.Set(common.escapeLinksFieldName(mesh)); + db.Set(mesh); // Notify mesh change var event = { etype: 'mesh', username: newuser.name, userid: user._id, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: 'Added user ' + newuser.name + ' to mesh ' + mesh.name, domain: domain.id, invite: mesh.invite }; @@ -2402,6 +2402,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use try { if (common.validateString(command.nodeid, 1, 1024) == false) { err = 'Invalid nodeid'; } // Check the nodeid else if (common.validateInt(command.rights) == false) { err = 'Invalid rights'; } // Device rights must be an integer + else if ((command.rights & 7) != 0) { err = 'Invalid rights'; } // EDITMESH, MANAGEUSERS or MANAGECOMPUTERS rights can't be assigned to a user to device link else if ((common.validateStrArray(command.usernames, 1, 64) == false) && (common.validateStrArray(command.userids, 1, 128) == false)) { err = 'Invalid usernames'; } // Username is between 1 and 64 characters else { if (command.nodeid.indexOf('/') == -1) { command.nodeid = 'node/' + domain.id + '/' + command.meshid; } @@ -2421,8 +2422,70 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use for (var i in command.usernames) { command.userids.push('user/' + domain.id + '/' + command.usernames[i].toLowerCase()); } } - // TODO - //console.log(command); + // Get the node and the rights for this node + parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) { + // Check if already in the right mesh + if ((node == null) || (node.meshid == command.meshid)) return; + var dispatchTargets = ['*', node.meshid, node._id]; + + // Check that we have rights to manage users on this device + if ((rights & MESHRIGHT_MANAGEUSERS) == 0) return; + + // Add the new link to the users + var nodeChanged = false; + for (var i in command.userids) { + var newuserid = command.userids[i]; + var newuser = parent.users[newuserid]; + if (newuser != null) { + // Add this user to the dispatch target list + dispatchTargets.push(newuser._id); + + if (command.rights == 0) { + // Remove link to this user + if (newuser.links != null) { + delete newuser.links[command.nodeid]; + if (Object.keys(newuser.links).length == 0) { delete newuser.links; } + } + + // Remove link to this device + if (node.links != null) { + delete node.links[newuserid]; + nodeChanged = true; + if (Object.keys(node.links).length == 0) { delete node.links; } + } + } else { + // Add the new link to this user + if (newuser.links == null) { newuser.links = {}; } + newuser.links[command.nodeid] = { rights: command.rights }; + + // Add the new link to the device + if (node.links == null) { node.links = {}; } + node.links[newuserid] = { rights: command.rights } + nodeChanged = true; + } + + // Save the user to the database + db.SetUser(newuser); + + // Notify user change + var targets = ['*', 'server-users', newuserid._id]; + var event = { etype: 'user', userid: newuserid._id, username: newuserid.name, action: 'accountchange', msg: (command.rights == 0) ? ('Removed user device rights for ' + user.name) : ('Changed user device rights for ' + user.name), domain: domain.id, account: parent.CloneSafeUser(newuser) }; + if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come. + parent.parent.DispatchEvent(targets, obj, event); + } + } + + // Save the device + if (nodeChanged == true) { + // Save the node to the database + db.Set(node); + + // Event the node change + var event = { etype: 'node', userid: user._id, username: user.name, action: 'changenode', nodeid: node._id, domain: domain.id, msg: (command.rights == 0) ? ('Removed user device rights for ' + node.name) : ('Changed user device rights for ' + node.name), node: parent.CloneSafeNode(node) } + if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the mesh. Another event will come. + parent.parent.DispatchEvent(dispatchTargets, obj, event); + } + }); break; } @@ -2461,7 +2524,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if ((delmeshrights == 0xFFFFFFFF) && (mesh.links[deluserid].rights != 0xFFFFFFFF)) return; // A non-admin can't kick out an admin delete deluser.links[command.meshid]; if (deluserid.startsWith('user/')) { db.SetUser(deluser); } - else if (deluserid.startsWith('ugrp/')) { db.Set(common.escapeLinksFieldName(deluser)); } + else if (deluserid.startsWith('ugrp/')) { db.Set(deluser); } parent.parent.DispatchEvent([deluser._id], obj, 'resubscribe'); if (deluserid.startsWith('user/')) { @@ -2483,7 +2546,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Remove user from the mesh if (mesh.links[command.userid] != null) { delete mesh.links[command.userid]; - db.Set(common.escapeLinksFieldName(mesh)); + db.Set(mesh); // Notify mesh change var event; @@ -2528,7 +2591,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if (command.amtpolicy.type === 2) { amtpolicy = { type: command.amtpolicy.type, password: command.amtpolicy.password, badpass: command.amtpolicy.badpass, cirasetup: command.amtpolicy.cirasetup }; } else if (command.amtpolicy.type === 3) { amtpolicy = { type: command.amtpolicy.type, password: command.amtpolicy.password, cirasetup: command.amtpolicy.cirasetup }; } mesh.amt = amtpolicy; - db.Set(common.escapeLinksFieldName(mesh)); + db.Set(mesh); var amtpolicy2 = Object.assign({}, amtpolicy); // Shallow clone delete amtpolicy2.password; var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: mesh._id, amt: amtpolicy2, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id, invite: mesh.invite }; diff --git a/translate/translate.json b/translate/translate.json index 1bd85633..e6539308 100644 --- a/translate/translate.json +++ b/translate/translate.json @@ -653,7 +653,7 @@ "default-mobile.handlebars->9->246", "default-mobile.handlebars->9->70", "default.handlebars->27->1237", - "default.handlebars->27->1483", + "default.handlebars->27->1485", "default.handlebars->27->653" ] }, @@ -2361,7 +2361,7 @@ "ru": "Добавить участие", "zh-chs": "添加會員", "xloc": [ - "default.handlebars->27->1479" + "default.handlebars->27->1481" ] }, { @@ -2847,7 +2847,7 @@ "ru": "Счетчик ошибок агента", "zh-chs": "座席錯誤計數器", "xloc": [ - "default.handlebars->27->1492" + "default.handlebars->27->1494" ] }, { @@ -2913,7 +2913,7 @@ "ru": "Сессии агентов", "zh-chs": "座席會議", "xloc": [ - "default.handlebars->27->1508" + "default.handlebars->27->1510" ] }, { @@ -3035,7 +3035,7 @@ "ru": "Агенты", "zh-chs": "代理商", "xloc": [ - "default.handlebars->27->1521" + "default.handlebars->27->1523" ] }, { @@ -3766,7 +3766,7 @@ "ru": "Вы уверенны, что {0} плагин: {1}", "zh-chs": "您確定要{0}插件嗎:{1}", "xloc": [ - "default.handlebars->27->1558" + "default.handlebars->27->1560" ] }, { @@ -4158,7 +4158,7 @@ "ru": "Плохой ключ", "zh-chs": "錯誤的簽名", "xloc": [ - "default.handlebars->27->1499" + "default.handlebars->27->1501" ] }, { @@ -4175,7 +4175,7 @@ "ru": "Плохой веб-сертификат", "zh-chs": "錯誤的網絡證書", "xloc": [ - "default.handlebars->27->1498" + "default.handlebars->27->1500" ] }, { @@ -4410,7 +4410,7 @@ "ru": "CIRA Сервер", "zh-chs": "CIRA服務器", "xloc": [ - "default.handlebars->27->1549" + "default.handlebars->27->1551" ] }, { @@ -4427,7 +4427,7 @@ "ru": "CIRA Сервер команды", "zh-chs": "CIRA服務器命令", "xloc": [ - "default.handlebars->27->1550" + "default.handlebars->27->1552" ] }, { @@ -4444,7 +4444,7 @@ "ru": "Загрузка CPU", "zh-chs": "CPU負載", "xloc": [ - "default.handlebars->27->1513" + "default.handlebars->27->1515" ] }, { @@ -4461,7 +4461,7 @@ "ru": "Загрузка CPU за последние 15 минут", "zh-chs": "最近15分鐘的CPU負載", "xloc": [ - "default.handlebars->27->1516" + "default.handlebars->27->1518" ] }, { @@ -4478,7 +4478,7 @@ "ru": "Загрузка CPU за последние 5 минут", "zh-chs": "最近5分鐘的CPU負載", "xloc": [ - "default.handlebars->27->1515" + "default.handlebars->27->1517" ] }, { @@ -4495,7 +4495,7 @@ "ru": "Загрузка CPU за последнюю минуту", "zh-chs": "最後一分鐘的CPU負載", "xloc": [ - "default.handlebars->27->1514" + "default.handlebars->27->1516" ] }, { @@ -4550,7 +4550,7 @@ "ru": "Ошибка вызова", "zh-chs": "通話錯誤", "xloc": [ - "default.handlebars->27->1559" + "default.handlebars->27->1561" ] }, { @@ -4968,7 +4968,7 @@ "ru": "Проверка...", "zh-chs": "檢查...", "xloc": [ - "default.handlebars->27->1555", + "default.handlebars->27->1557", "default.handlebars->27->778" ] }, @@ -5197,7 +5197,7 @@ "en": "Clear this notification", "nl": "Wis deze melding", "xloc": [ - "default.handlebars->27->1486" + "default.handlebars->27->1488" ] }, { @@ -5560,7 +5560,13 @@ "zh-chs": "確認刪除設備組{0}?", "xloc": [ "default.handlebars->27->1384", - "default.handlebars->27->1481" + "default.handlebars->27->1483" + ] + }, + { + "en": "Confirm removal of device {0}?", + "xloc": [ + "default.handlebars->27->1476" ] }, { @@ -5577,7 +5583,7 @@ "ru": "Подтвердить удаление группы {0}?", "zh-chs": "確認刪除組{0}?", "xloc": [ - "default.handlebars->27->1477" + "default.handlebars->27->1479" ] }, { @@ -5728,7 +5734,7 @@ "ru": "Подключено Intel® AMT", "zh-chs": "連接的英特爾®AMT", "xloc": [ - "default.handlebars->27->1504" + "default.handlebars->27->1506" ] }, { @@ -5745,7 +5751,7 @@ "ru": "Подключенные пользователи", "zh-chs": "關聯用戶", "xloc": [ - "default.handlebars->27->1509" + "default.handlebars->27->1511" ] }, { @@ -5817,7 +5823,7 @@ "ru": "Подключений ", "zh-chs": "連接數", "xloc": [ - "default.handlebars->27->1520" + "default.handlebars->27->1522" ] }, { @@ -5834,7 +5840,7 @@ "ru": "Ретранслятор подключения", "zh-chs": "連接繼電器", "xloc": [ - "default.handlebars->27->1548" + "default.handlebars->27->1550" ] }, { @@ -5941,7 +5947,7 @@ "ru": "Cookie-кодировщик", "zh-chs": "Cookie編碼器", "xloc": [ - "default.handlebars->27->1534" + "default.handlebars->27->1536" ] }, { @@ -6201,7 +6207,7 @@ "ru": "Основной сервер", "zh-chs": "核心服務器", "xloc": [ - "default.handlebars->27->1533" + "default.handlebars->27->1535" ] }, { @@ -7235,6 +7241,7 @@ "zh-chs": "設備", "xloc": [ "default.handlebars->27->1150", + "default.handlebars->27->1458", "default.handlebars->container->column_l->p1->devListToolbarSpan->1->0->9->devListToolbarSort->sortselect->5" ] }, @@ -7273,7 +7280,6 @@ "default.handlebars->27->1148", "default.handlebars->27->1149", "default.handlebars->27->1380", - "default.handlebars->27->1458", "default.handlebars->27->1467", "default.handlebars->27->1473" ] @@ -7314,7 +7320,7 @@ "default.handlebars->27->1352", "default.handlebars->27->1365", "default.handlebars->27->1420", - "default.handlebars->27->1507", + "default.handlebars->27->1509", "default.handlebars->container->column_l->p2->9" ] }, @@ -8309,7 +8315,7 @@ "ru": "Скопировать агент", "zh-chs": "代理重複", "xloc": [ - "default.handlebars->27->1503" + "default.handlebars->27->1505" ] }, { @@ -8738,7 +8744,7 @@ "en": "Email Traffic", "nl": "E-mailverkeer", "xloc": [ - "default.handlebars->27->1542" + "default.handlebars->27->1544" ] }, { @@ -9450,7 +9456,7 @@ "ru": "Внешний", "zh-chs": "外部", "xloc": [ - "default.handlebars->27->1527" + "default.handlebars->27->1529" ] }, { @@ -9882,8 +9888,8 @@ "ru": "Свободно", "zh-chs": "自由", "xloc": [ - "default.handlebars->27->1488", - "default.handlebars->27->1490" + "default.handlebars->27->1490", + "default.handlebars->27->1492" ] }, { @@ -10731,7 +10737,7 @@ "pt": "Total da pilha", "zh-chs": "堆總數", "xloc": [ - "default.handlebars->27->1529" + "default.handlebars->27->1531" ] }, { @@ -10746,7 +10752,7 @@ "pt": "Pilha usada", "zh-chs": "堆使用", "xloc": [ - "default.handlebars->27->1528" + "default.handlebars->27->1530" ] }, { @@ -11404,8 +11410,8 @@ "xloc": [ "default.handlebars->27->1226", "default.handlebars->27->1232", - "default.handlebars->27->1525", - "default.handlebars->27->1547" + "default.handlebars->27->1527", + "default.handlebars->27->1549" ] }, { @@ -12098,7 +12104,7 @@ "ru": "Некорректный тип группы устройств", "zh-chs": "無效的設備組類型", "xloc": [ - "default.handlebars->27->1502" + "default.handlebars->27->1504" ] }, { @@ -12115,7 +12121,7 @@ "ru": "Некорректный JSON", "zh-chs": "無效的JSON", "xloc": [ - "default.handlebars->27->1496" + "default.handlebars->27->1498" ] }, { @@ -12167,7 +12173,7 @@ "ru": "Некорректная сигнатура PKCS", "zh-chs": "無效的PKCS簽名", "xloc": [ - "default.handlebars->27->1494" + "default.handlebars->27->1496" ] }, { @@ -12184,7 +12190,7 @@ "ru": "Некорректная сигнатура RSA", "zh-chs": "無效的RSA密碼", "xloc": [ - "default.handlebars->27->1495" + "default.handlebars->27->1497" ] }, { @@ -13205,7 +13211,7 @@ "ru": "Меньше", "zh-chs": "減", "xloc": [ - "default.handlebars->27->1561" + "default.handlebars->27->1563" ] }, { @@ -14222,7 +14228,7 @@ "ru": "Сообщения главного сервера", "zh-chs": "主服務器消息", "xloc": [ - "default.handlebars->27->1536" + "default.handlebars->27->1538" ] }, { @@ -14596,7 +14602,7 @@ "ru": "Достигнуто максимальное число сессий", "zh-chs": "達到的會話數上限", "xloc": [ - "default.handlebars->27->1500" + "default.handlebars->27->1502" ] }, { @@ -14650,7 +14656,7 @@ "ru": "Мегабайт", "zh-chs": "兆字節", "xloc": [ - "default.handlebars->27->1526" + "default.handlebars->27->1528" ] }, { @@ -14667,7 +14673,7 @@ "ru": "ОЗУ", "zh-chs": "記憶", "xloc": [ - "default.handlebars->27->1517", + "default.handlebars->27->1519", "default.handlebars->27->743", "default.handlebars->container->column_l->p40->3->1->p40type->3" ] @@ -14800,7 +14806,7 @@ "ru": "Трафик MeshAgent", "zh-chs": "MeshAgent流量", "xloc": [ - "default.handlebars->27->1538" + "default.handlebars->27->1540" ] }, { @@ -14817,7 +14823,7 @@ "ru": "Обновление MeshAgent", "zh-chs": "MeshAgent更新", "xloc": [ - "default.handlebars->27->1539" + "default.handlebars->27->1541" ] }, { @@ -14916,7 +14922,7 @@ "ru": "Соединения сервера MeshCentral", "zh-chs": "MeshCentral服務器對等", "xloc": [ - "default.handlebars->27->1537" + "default.handlebars->27->1539" ] }, { @@ -15170,7 +15176,7 @@ "ru": "Диспетчер сообщения", "zh-chs": "郵件調度程序", "xloc": [ - "default.handlebars->27->1535" + "default.handlebars->27->1537" ] }, { @@ -15271,7 +15277,7 @@ "ru": "Еще", "zh-chs": "更多", "xloc": [ - "default.handlebars->27->1560" + "default.handlebars->27->1562" ] }, { @@ -15956,7 +15962,7 @@ "zh-chs": "找不到活動", "xloc": [ "default.handlebars->27->1273", - "default.handlebars->27->1482", + "default.handlebars->27->1484", "default.handlebars->27->691" ] }, @@ -17000,7 +17006,7 @@ "ru": "Произошло в {0}", "zh-chs": "發生在{0}", "xloc": [ - "default.handlebars->27->1485" + "default.handlebars->27->1487" ] }, { @@ -18015,7 +18021,7 @@ "ru": "Действие плагина", "zh-chs": "插件動作", "xloc": [ - "default.handlebars->27->1557", + "default.handlebars->27->1559", "default.handlebars->27->159" ] }, @@ -18707,7 +18713,7 @@ "ru": "RSS", "zh-chs": "的RSS", "xloc": [ - "default.handlebars->27->1530" + "default.handlebars->27->1532" ] }, { @@ -18886,7 +18892,7 @@ "ru": "Число ретрансляций", "zh-chs": "中繼計數", "xloc": [ - "default.handlebars->27->1512" + "default.handlebars->27->1514" ] }, { @@ -18902,7 +18908,7 @@ "ru": "Ошибки ретранслятора", "zh-chs": "中繼錯誤", "xloc": [ - "default.handlebars->27->1505" + "default.handlebars->27->1507" ] }, { @@ -18918,8 +18924,8 @@ "ru": "Сессии ретранслятора", "zh-chs": "接力會議", "xloc": [ - "default.handlebars->27->1511", - "default.handlebars->27->1524" + "default.handlebars->27->1513", + "default.handlebars->27->1526" ] }, { @@ -19193,7 +19199,7 @@ "zh-chs": "刪除設備組", "xloc": [ "default.handlebars->27->1383", - "default.handlebars->27->1480" + "default.handlebars->27->1482" ] }, { @@ -19210,7 +19216,8 @@ "ru": "Удалить пользователя", "zh-chs": "刪除用戶", "xloc": [ - "default.handlebars->27->1476" + "default.handlebars->27->1475", + "default.handlebars->27->1478" ] }, { @@ -20603,7 +20610,7 @@ "ru": "Сертификат сервера", "zh-chs": "服務器證書", "xloc": [ - "default.handlebars->27->1540" + "default.handlebars->27->1542" ] }, { @@ -20616,7 +20623,7 @@ "pt": "Banco de Dados do Servidor", "zh-chs": "服務器數據庫", "xloc": [ - "default.handlebars->27->1541" + "default.handlebars->27->1543" ] }, { @@ -20723,7 +20730,7 @@ "ru": "Состояние сервера", "zh-chs": "服務器狀態", "xloc": [ - "default.handlebars->27->1491" + "default.handlebars->27->1493" ] }, { @@ -20757,7 +20764,7 @@ "ru": "Трассировка сервера", "zh-chs": "服務器跟踪", "xloc": [ - "default.handlebars->27->1551" + "default.handlebars->27->1553" ] }, { @@ -20893,7 +20900,7 @@ "ru": "ServerStats.csv", "zh-chs": "ServerStats.csv", "xloc": [ - "default.handlebars->27->1532" + "default.handlebars->27->1534" ] }, { @@ -22869,7 +22876,7 @@ "ru": "На данный момент уведомлений нет", "zh-chs": "目前沒有任何通知", "xloc": [ - "default.handlebars->27->1484" + "default.handlebars->27->1486" ] }, { @@ -23995,7 +24002,7 @@ "default-mobile.handlebars->9->174", "default-mobile.handlebars->9->175", "default.handlebars->27->13", - "default.handlebars->27->1475", + "default.handlebars->27->1477", "default.handlebars->27->364", "default.handlebars->27->41", "default.handlebars->27->42", @@ -24037,7 +24044,7 @@ "ru": "Неизвестное действие", "zh-chs": "未知動作", "xloc": [ - "default.handlebars->27->1497" + "default.handlebars->27->1499" ] }, { @@ -24069,7 +24076,7 @@ "xloc": [ "default.handlebars->27->1376", "default.handlebars->27->1463", - "default.handlebars->27->1501" + "default.handlebars->27->1503" ] }, { @@ -24086,7 +24093,7 @@ "ru": "Неизвестная группа", "zh-chs": "未知群組", "xloc": [ - "default.handlebars->27->1493" + "default.handlebars->27->1495" ] }, { @@ -24196,7 +24203,7 @@ "ru": "Актуально", "zh-chs": "最新", "xloc": [ - "default.handlebars->27->1556" + "default.handlebars->27->1558" ] }, { @@ -24436,8 +24443,8 @@ "ru": "Использовано", "zh-chs": "用過的", "xloc": [ - "default.handlebars->27->1487", - "default.handlebars->27->1489" + "default.handlebars->27->1489", + "default.handlebars->27->1491" ] }, { @@ -24511,7 +24518,7 @@ "ru": "Учетные записи пользователей", "zh-chs": "用戶帳號", "xloc": [ - "default.handlebars->27->1506" + "default.handlebars->27->1508" ] }, { @@ -24565,7 +24572,7 @@ "xloc": [ "default.handlebars->27->1147", "default.handlebars->27->1354", - "default.handlebars->27->1478" + "default.handlebars->27->1480" ] }, { @@ -24703,7 +24710,7 @@ "ru": "Сессии пользователя", "zh-chs": "用戶會話", "xloc": [ - "default.handlebars->27->1523" + "default.handlebars->27->1525" ] }, { @@ -24858,7 +24865,7 @@ "xloc": [ "default.handlebars->27->1353", "default.handlebars->27->1364", - "default.handlebars->27->1522", + "default.handlebars->27->1524", "default.handlebars->container->topbar->1->1->UsersSubMenuSpan->UsersSubMenu->1->0->UsersGeneral" ] }, @@ -24876,7 +24883,7 @@ "ru": "Сессии пользователей", "zh-chs": "用戶會話", "xloc": [ - "default.handlebars->27->1510" + "default.handlebars->27->1512" ] }, { @@ -25270,8 +25277,8 @@ "ru": "Веб-сервер", "zh-chs": "網絡服務器", "xloc": [ - "default.handlebars->27->1543", - "default.handlebars->27->1544" + "default.handlebars->27->1545", + "default.handlebars->27->1546" ] }, { @@ -25288,7 +25295,7 @@ "ru": "Запросы веб-сервера", "zh-chs": "Web服務器請求", "xloc": [ - "default.handlebars->27->1545" + "default.handlebars->27->1547" ] }, { @@ -25305,7 +25312,7 @@ "ru": "Ретранслятор Web Socket", "zh-chs": "Web套接字中繼", "xloc": [ - "default.handlebars->27->1546" + "default.handlebars->27->1548" ] }, { @@ -26142,7 +26149,7 @@ "ru": "\\\\'", "zh-chs": "\\\\'", "xloc": [ - "default.handlebars->27->1554" + "default.handlebars->27->1556" ] }, { @@ -26381,7 +26388,7 @@ "ru": "свободно", "zh-chs": "自由", "xloc": [ - "default.handlebars->27->1518" + "default.handlebars->27->1520" ] }, { @@ -26675,7 +26682,7 @@ "ru": "servertrace.csv", "zh-chs": "servertrace.csv", "xloc": [ - "default.handlebars->27->1553" + "default.handlebars->27->1555" ] }, { @@ -26725,7 +26732,7 @@ "ru": "time, conn.agent, conn.users, conn.usersessions, conn.relaysession, conn.intelamt, mem.external, mem.heapused, mem.heaptotal, mem.rss", "zh-chs": "時間,conn.agent,conn.users,conn.usersessions,conn.relaysession,conn.intelamt,mem.external,mem.heapused,mem.heaptotal,mem.rss", "xloc": [ - "default.handlebars->27->1531" + "default.handlebars->27->1533" ] }, { @@ -26741,7 +26748,7 @@ "ru": "time, source, message", "zh-chs": "時間,來源,訊息", "xloc": [ - "default.handlebars->27->1552" + "default.handlebars->27->1554" ] }, { @@ -26771,7 +26778,7 @@ "ru": "всего", "zh-chs": "總", "xloc": [ - "default.handlebars->27->1519" + "default.handlebars->27->1521" ] }, { diff --git a/views/default.handlebars b/views/default.handlebars index 853235d5..52bb4e46 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -2419,6 +2419,7 @@ node.tags = message.event.node.tags; node.userloc = message.event.node.userloc; node.rdpport = message.event.node.rdpport; + if (message.event.node.links != null) { node.links = message.event.node.links; } else { delete node.links; } if (message.event.node.agent != null) { if (node.agent == null) node.agent = {}; if (message.event.node.agent.ver != null) { node.agent.ver = message.event.node.agent.ver; } @@ -8433,10 +8434,10 @@ } else if (userid === 4) { var y = '', firstMeshId = null; for (var i in meshes) { y += ''; if (firstMeshId == null) { firstMeshId = meshes[i]._id; } } - x += addHtmlValue("Device Group", '
'); + x += addHtmlValue("Device Group", ''); y = ''; for (var i in nodes) { if (nodes[i].meshid == firstMeshId) { y += ''; } } - x += addHtmlValue("Device", ''); + x += addHtmlValue("Device", ''); } else { userid = decodeURIComponent(userid); var uname = userid.split('/')[2]; @@ -8513,7 +8514,7 @@ if (meshrights & 32768) { Q('p20uninstall').checked = true; } } } - p20validateAddMeshUserDialog(); + p20validateAddMeshUserDialog(userid); return false; } @@ -8521,6 +8522,7 @@ var y = '', meshid = decodeURIComponent(Q('dp2meshid').value); for (var i in nodes) { if (nodes[i].meshid == meshid) { y += ''; } } QH('dp2nodeid', y); + p20validateAddMeshUserDialog(4); } function p20setname(name) { @@ -8533,14 +8535,36 @@ return false; } - function p20validateAddMeshUserDialog() { + function p20validateAddMeshUserDialog(updateId) { + var ok = true; + + if (updateId === 4) { + // Update user device rights + var devrights = 0, nodeid = decodeURIComponent(Q('dp2nodeid').value); + if ((nodeid != '') && (currentUser.links != null) && (currentUser.links[nodeid] != null)) { devrights = currentUser.links[nodeid].rights; } + Q('p20remotecontrol').checked = ((devrights & 8) != 0); + Q('p20meshagentconsole').checked = ((devrights & 16) != 0); + Q('p20meshserverfiles').checked = ((devrights & 32) != 0); + Q('p20wakedevices').checked = ((devrights & 64) != 0); + Q('p20editnotes').checked = ((devrights & 128) != 0); + Q('p20remoteview').checked = ((devrights & 256) != 0); + Q('p20noterminal').checked = ((devrights & 512) != 0); + Q('p20nofiles').checked = ((devrights & 1024) != 0); + Q('p20noamt').checked = ((devrights & 2048) != 0); + Q('p20remotelimitedinput').checked = ((devrights & 4096) != 0); + Q('p20limitevents').checked = ((devrights & 8192) != 0); + Q('p20chatnotify').checked = ((devrights & 16384) != 0); + Q('p20uninstall').checked = ((devrights & 32768) != 0); + Q('p20nodesktop').checked = ((devrights & 65536) != 0); + ok = (nodeid != ''); + } + var meshrights = null; if ((xxdialogTag === 1) || (xxdialogTag === 3)) { meshrights = GetMeshRights(decodeURIComponent(Q('dp2groupid').value)); } else { meshrights = GetMeshRights(currentMesh); } - var ok = true; if (Q('dp20username')) { var xusers = Q('dp20username').value.split(','); for (var i in xusers) { @@ -8569,14 +8593,15 @@ } QE('idx_dlgOkButton', ok); + var nc; if (Q('p20fulladmin') != null) { - var nc = !Q('p20fulladmin').checked; + nc = !Q('p20fulladmin').checked; QE('p20fulladmin', meshrights == 0xFFFFFFFF); QE('p20editmesh', nc && (meshrights == 0xFFFFFFFF)); QE('p20manageusers', nc); QE('p20managecomputers', nc); } else { - nc = true; + nc = (nodeid != ''); } QE('p20remotecontrol', nc); QE('p20meshagentconsole', nc); @@ -10205,13 +10230,13 @@ if (currentUser.links) { for (var i in currentUser.links) { if (i.startsWith('node/')) { - var cr = 0, r = currentUser.links[i].rights, node = getNodeFromId(i), trash = '', rights = "Partial Device Rights"; + var r = currentUser.links[i].rights, node = getNodeFromId(i), trash = '', rights = "Partial Device Rights", cr = GetNodeRights(node); if (node == null) { continue; } if ((userinfo.links) && (userinfo.links[i] != null) && (userinfo.links[i].rights != null)) { cr = userinfo.links[i].rights; } var nodename = node?EscapeHtml(node.name):('' + "Unknown Device" + ''); if (r == 0xFFFFFFFF) rights = "Full Device Rights"; else if (r == 0) rights = "No Rights"; if ((currentUser._id != userinfo._id) && ((cr & 2) != 0)) { trash = '