152 lines
5.2 KiB
JavaScript
152 lines
5.2 KiB
JavaScript
const { DeviceStat, Flow } = require('../../../models/Schemas');
|
|
const { getTimeFilter, getBaseFilter, getCustomLabelsMap } = require('../helpers');
|
|
const { resolveDeviceTypeFromIp, resolveOSFromIp, resolveVendorFromIp, generateAutoLabel } = require('../../../deviceResolver');
|
|
const User = require('../../../models/User');
|
|
|
|
async function getLabelingHandler(req, res) {
|
|
try {
|
|
const isAuthorized = req.user?.role === 'SUPER_ADMIN' ||
|
|
req.user?.role === 'EXECUTIVE' ||
|
|
req.user?.role === 'TENANT_ADMIN' ||
|
|
req.user?.role === 'COMPANY_ADMIN' ||
|
|
req.user?.role === 'COMPANY_OPERATOR' ||
|
|
req.user?._originalRole === 'SUPER_ADMIN' ||
|
|
req.user?._originalRole === 'TENANT_ADMIN';
|
|
|
|
if (!isAuthorized) {
|
|
return res.status(403).json({ ok: false, error: 'Unauthorized: Only administrators can view device labeling directory.' });
|
|
}
|
|
|
|
const timeFilter = getTimeFilter(req);
|
|
const query = getBaseFilter(req, timeFilter);
|
|
|
|
// Enforce tenant site isolation
|
|
const isGlobalUser = req.user?.role === 'SUPER_ADMIN' ||
|
|
req.user?.role === 'EXECUTIVE' ||
|
|
((!req.user?.site_uuid || req.user?.site_uuid === 'default') && ['SOC_ANALYST', 'ENGINEER'].includes(req.user?.role)) ||
|
|
req.user?._originalRole === 'SUPER_ADMIN';
|
|
|
|
if (!isGlobalUser && req.user?.site_uuid) {
|
|
query.site_uuid = req.user.site_uuid;
|
|
}
|
|
|
|
// 1. Group by mac_address to find the latest record for each MAC in DeviceStat
|
|
const pipeline = [
|
|
{ $match: { ...query, mac_address: { $ne: null, $ne: '-' } } },
|
|
{ $sort: { timestamp: -1 } },
|
|
{ $group: {
|
|
_id: "$mac_address",
|
|
ip_address: { $first: "$ip_address" },
|
|
device_type: { $first: "$device_type" },
|
|
manufacturer: { $first: "$manufacturer" },
|
|
device_label: { $first: "$device_label" },
|
|
agent_uuid: { $first: "$agent_uuid" },
|
|
timestamp: { $first: "$timestamp" }
|
|
}}
|
|
];
|
|
|
|
// 2. Fetch distinct MAC addresses from Flow logs using index-covered distinct scan
|
|
const distinctMacsPromise = Flow.distinct('src_mac', {
|
|
...query,
|
|
src_mac: { $ne: null, $ne: '-' }
|
|
});
|
|
|
|
const [deviceData, distinctMacs] = await Promise.all([
|
|
DeviceStat.aggregate(pipeline),
|
|
distinctMacsPromise
|
|
]);
|
|
|
|
// 3. Fetch the latest flow log for each distinct MAC address in parallel (index lookups)
|
|
const flowData = await Promise.all(
|
|
distinctMacs.map(async (mac) => {
|
|
const latest = await Flow.findOne({
|
|
...query,
|
|
src_mac: mac
|
|
})
|
|
.sort({ timestamp: -1 })
|
|
.select('src_ip agent_uuid timestamp')
|
|
.lean();
|
|
|
|
if (!latest) return null;
|
|
return {
|
|
_id: mac,
|
|
ip_address: latest.src_ip,
|
|
agent_uuid: latest.agent_uuid,
|
|
timestamp: latest.timestamp
|
|
};
|
|
})
|
|
).then(results => results.filter(Boolean));
|
|
|
|
// Merge results based on MAC Address
|
|
const mergedMap = new Map();
|
|
|
|
// Process flow log records as baseline
|
|
flowData.forEach(f => {
|
|
const mac = f._id;
|
|
mergedMap.set(mac, {
|
|
_id: mac,
|
|
ip_address: f.ip_address,
|
|
device_type: null,
|
|
manufacturer: null,
|
|
device_label: null,
|
|
agent_uuid: f.agent_uuid,
|
|
timestamp: f.timestamp
|
|
});
|
|
});
|
|
|
|
// Overwrite/merge with DeviceStat records
|
|
deviceData.forEach(d => {
|
|
const mac = d._id;
|
|
mergedMap.set(mac, d);
|
|
});
|
|
|
|
const data = Array.from(mergedMap.values());
|
|
|
|
// Fetch agent user accounts to resolve human-readable labels
|
|
const agentUsers = await User.find({ role: 'AGENT_VIEWER' }).lean();
|
|
const agentMap = {};
|
|
agentUsers.forEach(u => {
|
|
if (u.agent_uuid) {
|
|
agentMap[u.agent_uuid] = u.account_name || u.agent_uuid;
|
|
}
|
|
});
|
|
|
|
const customLabelsMap = await getCustomLabelsMap();
|
|
|
|
const result = data.map(item => {
|
|
const mac = item._id;
|
|
const customLabel = customLabelsMap[mac] || null;
|
|
const ip = item.ip_address || '-';
|
|
const type = item.device_type && item.device_type !== '-' && item.device_type !== 'Unknown' ? item.device_type : resolveDeviceTypeFromIp(ip);
|
|
const os = item.os_label && item.os_label !== '-' && item.os_label !== 'Unknown' ? item.os_label : resolveOSFromIp(ip);
|
|
const man = item.manufacturer && item.manufacturer !== '-' && item.manufacturer !== 'Unknown' ? item.manufacturer : resolveVendorFromIp(ip);
|
|
|
|
const baseLabel = item.device_label;
|
|
const defaultLabel = baseLabel && baseLabel !== '-' && baseLabel !== 'Unknown' && baseLabel !== 'Generic Client'
|
|
? baseLabel
|
|
: generateAutoLabel(ip, mac, man, type);
|
|
|
|
const agentUuid = item.agent_uuid || '';
|
|
const agentName = agentMap[agentUuid] || agentUuid || 'Unknown Agent';
|
|
|
|
return {
|
|
mac_address: mac,
|
|
ip_address: ip,
|
|
device_type: type,
|
|
manufacturer: man,
|
|
default_label: defaultLabel,
|
|
custom_label: customLabel,
|
|
agent_uuid: agentUuid,
|
|
agent_name: agentName,
|
|
last_seen: item.timestamp || new Date()
|
|
};
|
|
});
|
|
|
|
res.json({ ok: true, data: result });
|
|
} catch (err) {
|
|
res.status(500).json({ ok: false, error: err.message });
|
|
}
|
|
}
|
|
|
|
module.exports = getLabelingHandler;
|