import { decideChangeRequest, getChangeRequest, listChangeRequests } from '../models/changeRequestModel.js'; import { getConnectionGovernance, intuneReportingOverview, listGraphAudit } from '../models/graphModel.js'; import { loadIntuneDeployment } from '../models/psadtModel.js'; import { canApprove } from '../services/graphRbac.js'; export function changeRequests(req, res) { res.json(listChangeRequests(req.user.id, { status: req.query.status, isAdmin: req.user.role === 'admin' })); } // Admins can always decide; otherwise the user must be a named approver on the // request's Graph connection. function authorizedApprover(req, request) { if (req.user.role === 'admin') return true; const governance = request.connectionId ? getConnectionGovernance(request.connectionId) : null; return canApprove({ approverIds: governance?.approverIds || [] }, req.user); } function decide(req, res, status) { const request = getChangeRequest(req.params.id); if (!request || request.status !== 'pending') return res.status(404).json({ error: 'Pending change request not found' }); if (!authorizedApprover(req, request)) return res.status(403).json({ error: 'You are not an authorized approver for this connection.' }); const decided = decideChangeRequest(req.params.id, { status, approverUserId: req.user.id, approverEmail: req.user.email, reason: req.body?.reason || '' }); if (!decided) return res.status(404).json({ error: 'Pending change request not found' }); res.json(decided); } export function approveChangeRequest(req, res) { decide(req, res, 'approved'); } export function rejectChangeRequest(req, res) { decide(req, res, 'rejected'); } export function reportingOverview(req, res) { res.json(intuneReportingOverview(req.user.id, req.user.role === 'admin')); } function toCsv(rows) { const headers = ['createdAt', 'action', 'status', 'actorEmail', 'targetGraphId', 'message']; const escape = (value) => `"${String(value ?? '').replace(/"/g, '""')}"`; const lines = [headers.join(',')]; for (const row of rows) lines.push(headers.map((h) => escape(row[h])).join(',')); return lines.join('\n'); } // CSV export of the tenant write-back audit trail for one deployment. export function auditExport(req, res) { const deployment = loadIntuneDeployment(req.params.id, req.user.id); if (!deployment) return res.status(404).json({ error: 'Intune deployment not found' }); const rows = listGraphAudit({ deploymentId: deployment.id, limit: 1000 }); res.setHeader('Content-Type', 'text/csv; charset=utf-8'); res.setHeader('Content-Disposition', `attachment; filename="audit-${deployment.id}.csv"`); res.send(toCsv(rows)); }