feat: add multi-BU tenancy with per-user team scoping (Option B)
- Add bu_teams column to users table (migration + fresh schema) - Create shared KNOWN_TEAMS constant and validateTeams helper - Expose user teams in auth middleware, login, and /me responses - Add bu_teams CRUD to user management routes with audit logging - Make Ivanti FINDINGS_FILTERS configurable via IVANTI_BU_FILTER env var - Add query-time team filtering to GET /findings and /findings/counts - Update AuthContext with teams helpers and admin scope toggle - Create AdminScopeToggle component (My Teams / All BUs) - Scope ReportingPage findings fetch by user teams - Scope CompliancePage team selector by user teams - Scope ExportsPage findings exports by user teams - Add BU teams multi-select to UserManagement create/edit forms - Display team badges in user list table
This commit is contained in:
@@ -510,6 +510,36 @@ export default function AtlasSlideOutPanel({ hostId, hostName, findingId, qualys
|
||||
}
|
||||
const data = await res.json();
|
||||
const remotePlans = parseAtlasPlans(data);
|
||||
|
||||
// If Atlas returns no plans, check local cache for optimistic bulk-create stubs
|
||||
if (remotePlans.length === 0) {
|
||||
try {
|
||||
const cacheRes = await fetch(`${API_BASE}/atlas/status`, { credentials: 'include' });
|
||||
if (cacheRes.ok) {
|
||||
const cacheData = await cacheRes.json();
|
||||
const hostCache = cacheData.find(r => r.host_id === hostId);
|
||||
if (hostCache && hostCache.has_action_plan === 1 && hostCache.plans_json) {
|
||||
let cachedPlans = [];
|
||||
try { cachedPlans = typeof hostCache.plans_json === 'string' ? JSON.parse(hostCache.plans_json) : hostCache.plans_json; } catch (_) {}
|
||||
const stubs = cachedPlans
|
||||
.filter(p => p.source === 'bulk-create')
|
||||
.map((p, i) => ({
|
||||
action_plan_id: 'pending-' + hostId + '-' + i,
|
||||
plan_type: p.plan_type || 'unknown',
|
||||
commit_date: p.commit_date || '',
|
||||
status: 'pending',
|
||||
_localPending: true,
|
||||
created_at: p.created_at || '',
|
||||
}));
|
||||
if (stubs.length > 0) {
|
||||
setPlans(stubs);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (_) { /* ignore cache fallback errors */ }
|
||||
}
|
||||
|
||||
// Merge: keep local pending plans that aren't yet confirmed by Atlas
|
||||
setPlans(prev => {
|
||||
const localPending = prev.filter(p => p._localPending);
|
||||
|
||||
Reference in New Issue
Block a user