Add BU Lookup tool to Admin panel
Backend: - GET /api/ivanti/findings/bu-lookup?q=<hostname|ip> — queries the live Ivanti API to discover which BU a host is assigned to. Returns deduplicated results with hostName, ipAddress, BU, and hostId. Admin-only endpoint. Frontend: - Add 'BU Lookup' tab to Admin page with search input and results table - Shows BU assignment badge (blue for tagged, amber for untagged) - Supports Enter key to search, loading state, error display - Useful for verifying team assignments when onboarding new users
This commit is contained in:
@@ -1689,6 +1689,83 @@ function createIvantiFindingsRouter(db, requireAuth) {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /api/ivanti/findings/bu-lookup
|
||||
*
|
||||
* Look up the BU assignment for a hostname or IP in the Ivanti API.
|
||||
* Queries the live Ivanti platform (not the local cache) to discover
|
||||
* which BU a given host is tagged to. Useful for verifying user team
|
||||
* assignments before onboarding.
|
||||
* Requires Admin group.
|
||||
*
|
||||
* @query {string} q - Hostname or IP address to look up
|
||||
* @returns {Object} 200 - { query, findings: [{ hostId, hostName, ipAddress, bu, severity, title }] }
|
||||
* @returns {Object} 400 - { error } when query is missing
|
||||
* @returns {Object} 500 - { error } on API failure
|
||||
*/
|
||||
router.get('/bu-lookup', requireGroup('Admin'), async (req, res) => {
|
||||
const q = (req.query.q || '').trim();
|
||||
if (!q) return res.status(400).json({ error: 'q parameter is required (hostname or IP)' });
|
||||
|
||||
try {
|
||||
const clientId = process.env.IVANTI_CLIENT_ID || '1550';
|
||||
const apiKey = process.env.IVANTI_API_KEY;
|
||||
if (!apiKey) return res.status(503).json({ error: 'Ivanti API key not configured' });
|
||||
|
||||
// Search by hostname or IP
|
||||
const filters = [
|
||||
{
|
||||
field: q.match(/^\d+\./) ? 'host.ipAddress' : 'host.hostName',
|
||||
operator: 'WILDCARD',
|
||||
value: `*${q}*`,
|
||||
exclusive: false,
|
||||
orWithPrevious: false,
|
||||
implicitFilters: [],
|
||||
caseSensitive: false
|
||||
}
|
||||
];
|
||||
|
||||
const result = await ivantiPost(`/client/${clientId}/hostFinding/search`, {
|
||||
filters,
|
||||
projection: 'detail',
|
||||
sort: [{ field: 'severity', direction: 'DESC' }],
|
||||
page: 0,
|
||||
size: 20
|
||||
});
|
||||
|
||||
if (!result.ok) {
|
||||
return res.status(502).json({ error: 'Ivanti API request failed', status: result.status });
|
||||
}
|
||||
|
||||
const data = JSON.parse(result.body);
|
||||
const records = data._embedded?.records || [];
|
||||
|
||||
// Extract BU info from each finding
|
||||
const findings = records.map(r => ({
|
||||
hostId: r.host?.hostId || null,
|
||||
hostName: r.host?.hostName || null,
|
||||
ipAddress: r.host?.ipAddress || null,
|
||||
bu: r.assetCustomAttributes?.['1550_host_1']?.[0] || 'Untagged',
|
||||
severity: r.severity || null,
|
||||
title: r.title || null,
|
||||
}));
|
||||
|
||||
// Deduplicate by hostId+bu
|
||||
const seen = new Set();
|
||||
const unique = findings.filter(f => {
|
||||
const key = `${f.hostId}-${f.bu}`;
|
||||
if (seen.has(key)) return false;
|
||||
seen.add(key);
|
||||
return true;
|
||||
});
|
||||
|
||||
res.json({ query: q, total: data.page?.totalElements || 0, findings: unique });
|
||||
} catch (err) {
|
||||
console.error('[Ivanti] BU lookup error:', err.message);
|
||||
res.status(500).json({ error: 'BU lookup failed' });
|
||||
}
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user