requireTeam() now respects an optional ?teams= query param from Admin
users as a voluntary scope filter. When the Admin Scope Toggle is set
to 'My Teams', the frontend sends ?teams=STEAM,ACCESS-ENG and the
backend applies the filter. When set to 'All BUs' (no param), Admin
gets the full unfiltered view.
Non-admin users continue to be enforced by their bu_teams assignment
regardless of any query param.
Allow Admin users to temporarily view the app as another user to verify
permissions and team scoping without switching accounts.
Backend:
- Migration: add impersonate_user_id column to sessions table
- requireAuth(): when impersonation is active, override req.user with
target user's identity; store real admin identity in req.realUser
- POST /api/auth/impersonate: start impersonation (Admin only, cannot
impersonate self or other Admins)
- POST /api/auth/stop-impersonate: end impersonation, revert to real user
- GET /api/auth/me: returns impersonating flag and realUser when active
- Audit logging on impersonate start/stop
Frontend:
- AuthContext: add impersonating, realUser state; startImpersonation()
and stopImpersonation() helpers
- ImpersonationBanner: fixed amber banner showing target user identity
with Exit button
- UserManagement: Eye icon button on each non-Admin user row to start
View As (visible only to Admin, hidden for self and other Admins)
- App.js: mount ImpersonationBanner at top of authenticated view
Introduce server-side team-scoped data access enforcement:
- Add TEAM_TO_IVANTI/IVANTI_TO_TEAM mapping to helpers/teams.js
- Add requireTeam() middleware to middleware/auth.js
- Admin bypass (req.teamScope = null)
- 403 for users with no team assignment
- Populates req.teamScope with short and ivanti name arrays
- Ivanti findings: replace client ?teams= param with req.teamScope filtering
on GET /, /counts, /counts/history, /fp-workflow-counts, POST /sync
- Override and note endpoints verify finding is in team scope
- Compliance: add requireTeam() router-level, validate ?team= param against scope
on GET /items and GET /summary
- CARD: validate teamName param on GET /teams/:teamName/assets
- Todo queue: verify findings belong to user's teams on POST /batch
- Clarify IVANTI_BU_FILTER comment (sync-level vs query-time filtering)
- Update 14 test files to include requireTeam in auth middleware mocks
- All 16 route files now import pool from ../db directly
- Removed db parameter from all factory functions
- All callbacks replaced with async/await pool.query()
- All ? placeholders converted to $1, $2... numbered params
- datetime('now') → NOW(), INSERT OR IGNORE → ON CONFLICT DO NOTHING
- LIKE → ILIKE for case-insensitive searches
- Error detection: err.code === '23505' for unique violations
- server.js no longer passes pool/db/requireAuth to route factories
- Only ivantiFindings.js still receives pool (pending task 8 rewrite)
- 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