Script Python que genera un informe mensual HTML autocontenido con accesos bloqueados, detecciones de Identity Protection, alertas Microsoft Defender y usuarios en riesgo. Ejecutado desde security-monitor.
Script Python standalone ejecutado mensualmente desde security-monitor. Sin Flask, sin base de datos. Consulta 4 endpoints Graph API, genera un HTML con Chart.js y lo envía por SMTP.
4 fuentes: auditLogs/signIns (bloqueos CA), riskDetections (Identity Protection), alerts_v2 (Defender), riskyUsers (usuarios en riesgo). Paginación automática.
HTML autocontenido: hero + KPIs, gráficos Chart.js (donut, stacked bar, horizontal bars), mapa mundial choropleth con distribución geográfica, tablas de detalle. Dark/light toggle.
Descarta en Entra ID usuarios en riesgo cuyo riskLastUpdatedDateTime supera N días. Batches de 60, manejo 429 con Retry-After, 403 graceful. Configurable con auto_dismiss_stale_days: 0 para deshabilitar.
Cuatro endpoints de Graph API v1.0, todos con permisos Application (sin usuario interactivo).
| Sección | Endpoint | Permiso requerido |
|---|---|---|
| Accesos bloqueados | /auditLogs/signIns |
AuditLog.Read.All |
| Detecciones de riesgo | /identityProtection/riskDetections |
IdentityRiskEvent.Read.All |
| Alertas Defender | /security/alerts_v2 |
SecurityAlert.Read.All |
| Usuarios en riesgo + dismiss | /identityProtection/riskyUsers |
IdentityRiskyUser.ReadWrite.All |
| Client ID | 250c7cb4-c8c7-4f9c-a393-5aa0b057dcc4 |
| Tenant | d78b7929-c2a3-4897-ae9a-7d8f8dc1a1cf |
| Thumbprint | FC402921656C6BFE606980EB654C7A507883239E |
| Tipo permisos | Application (sin usuario interactivo) |
HTML autocontenido con Chart.js, chartjs-chart-geo y topojson-client incrustados. Funciona sin conexión a internet una vez generado.
Los usuarios en riesgo se colorean según días desde último evento:
| Función | Descripción |
|---|---|
fetch_blocked_signins() |
Consulta auditLogs/signIns con filtros de códigos de error CA/riesgo. Paginación automática hasta 1000/página |
fetch_security_alerts() |
Chunking diario en alerts_v2 para evitar hard cap de 1000/query. Sleep 3s entre días (20 req/min). Avisa si algún día alcanza el límite |
dismiss_stale_risky_users() |
Descarta usuarios con riesgo antiguo. Batches de 60 IDs, manejo 429 con Retry-After, 403 graceful sin error fatal |
render_html() |
Genera el HTML completo autocontenido con Chart.js y topojson incrustados. Dark/light mode con prefers-color-scheme |
{
"graph": {
"tenant_id": "d78b7929-...",
"client_id": "250c7cb4-...",
"cert_thumbprint": "FC402921..."
},
"auto_dismiss_stale_days": 30,
"email": {
"smtp_host": "mail-s1.pro.es.sys.idealista",
"smtp_port": 25,
"from": "[email protected]",
"to": ["[email protected]"]
}
}Los assets JS (Chart.js, chartjs-chart-geo, topojson) se descargan desde jsDelivr/unpkg la primera vez y se cachean en assets/. Las ejecuciones posteriores los reutilizan sin acceso a internet.
assets/ — JS embebidos (ignorado en git)reports/ — informes generados (ignorado en git)config/app-config.json — credenciales (NO en git)security/alerts_v2 tiene un hard cap de 1000 alertas por query sin nextLink. El chunking diario lo mitiga. Si un día supera 1000, se avisa por stderr pero no se aborta la ejecución.
Requiere acceso al cert store CurrentUser\My del usuario que ejecuta el script. Si el script corre como servicio de Windows, debe hacerlo bajo la misma cuenta donde está instalado el certificado.
Para el dismiss de usuarios en riesgo se requiere IdentityRiskyUser.ReadWrite.All, no el Read.All básico. Sin este permiso, el dismiss se omite con aviso graceful.
En entornos sin acceso a internet directo, copiar manualmente los assets JS a la carpeta assets/ antes de la primera ejecución: Chart.js, chartjs-chart-geo, topojson-client.