
Auditoría de conciliación para la integración de Discord de Alliance Auth: encuentra miembros del gremio que tienen roles gestionados por AA que Auth nunca concedió y les quita los roles o los expulsa bajo una política controlada por el operador. Aplicación Django comunitaria independiente.
Auditoría de conciliación para la integración de Discord de Alliance Auth. Compara las asignaciones reales de roles en el gremio de Discord configurado con el estado expresado en Alliance Auth (Grupos + Estado por usuario) y cierra la brecha mediante la cual los moderadores pueden asignar manualmente roles nombrados por AA a usuarios que AA no conoce.
Estado: alpha (
0.1.x). La API pública y la configuración pueden aún cambiar antes de1.0.
La auditoría es segura por defecto:
InitialAuditAcknowledgement (solo administrador o shell).report para cada categoría — las acciones
destructivas son opt-in.AuditRun, AuditFinding, AuditInvocation,
ConfigChangeLog) son de solo añadido (append-only) a nivel de manager e instancia;
las operaciones masivas update() / bulk_update() están bloqueadas.Cada miembro del gremio se clasifica en una categoría:
| Categoría | Significado |
|---|---|
unknown_guest | Miembro de Discord que AA no conoce |
linked_no_perm | Identidad conocida por AA pero carece de discord.access_discord |
bot_filtered | Cuenta de bot configurada — nunca se actúa sobre ella |
El operador asigna cada categoría a una acción:
| Acción | Comportamiento |
|---|---|
report | Registrar el hallazgo; sin cambios del lado de Discord |
strip | Eliminar roles gestionados por AA |
strip_kick | Eliminar roles gestionados por AA y luego expulsar del gremio |
El mapeo es la configuración AA_DISCORD_AUDIT_POLICY; las anulaciones por grupo y
por estado se anidan dentro de cada categoría.
allianceauth.services.modules.discord) instalado y configurado
(token del bot + gremio)pip install aa-discord-audit
En tu Auth `local.py`:```python
# `aa_discord_audit` must appear AFTER
# `allianceauth.services.modules.discord` so the discord module's
# models load first; `apps.ready()` raises `ImproperlyConfigured`
# otherwise.
INSTALLED_APPS += ["aa_discord_audit"]
MIDDLEWARE += [
"aa_discord_audit.current_user.CurrentUserMiddleware",
]
Luego ejecuta las migraciones:```sh python manage.py migrate aa_discord_audit
El `CurrentUserMiddleware` es obligatorio: `apps.ready()` lanza `ImproperlyConfigured` si falta. Es lo que permite que el manejador de señales `ConfigChangeLog` atribuya las ediciones de administración a un usuario real en lugar de `<system>`.
## Inicio rápido
1. Otorga `aa_discord_audit.run_audit` al rol de operador que ejecuta las auditorías.
2. Ejecuta una auditoría de prueba en seco: ```sh
python manage.py audit_discord_roles --action report
InitialAuditAcknowledgement a través del admin, o ejecute
python manage.py audit_acknowledge_initial. Ambos requieren
aa_discord_audit.run_audit y
aa_discord_audit.acknowledge_initial_audit.| Nombre clave | Puertas |
|---|---|
aa_discord_audit.run_audit | comando de gestión, tarea beat, ejecutar eliminación |
aa_discord_audit.run_audit_destructive | puerta de lanzamiento web para strip / strip_kick (separada de run_audit) |
aa_discord_audit.acknowledge_initial_audit | libera el bloqueo de primera ejecución en seco |
aa_discord_audit.manage_discord_identity | admin de DiscordIdentity |
aa_discord_audit.manage_role_exception | admin de ManagedRoleException |
aa_discord_audit.manage_protected_member | admin de ProtectedDiscordMember |
aa_discord_audit.manage_bot_account_uid | admin de BotAccountUid |
aa_discord_audit.manage_finding_override | admin de FindingActionOverride |
aa_discord_audit.view_auditrun (y amigos) | registro de auditoría de solo lectura en el panel de Auth |
Los nombres clave manage_* están divididos por radio de explosión, de modo que un junior con
manage_bot_account_uid no pueda también desarmar la auditoría editando
ManagedRoleException.
Todas las configuraciones son opcionales. Los valores predeterminados son seguros.```python
AA_DISCORD_AUDIT_POLICY = { "unknown_guest": "report", "linked_no_perm": "report", # "linked_no_perm": { # "default": "strip", # "by_state": {"Guest": "report"}, # "by_group": {"Directors": "report"}, # }, }
AA_DISCORD_AUDIT_NOTIFY_ADMINS = True
AA_DISCORD_AUDIT_WEBHOOK_URL = None
AA_DISCORD_AUDIT_BOT_UIDS = []
AA_DISCORD_AUDIT_AUTO_DISCOVER_BY_NICKNAME = False
AA_DISCORD_AUDIT_RUN_RETENTION_DAYS = 180 AA_DISCORD_AUDIT_RETENTION_OPT_OUT_ACKNOWLEDGED = False
AA_DISCORD_AUDIT_IDEMPOTENCY_KEY_TTL_DAYS = 0
AA_DISCORD_AUDIT_RUN_DEADLINE_MINUTES = 60
AA_DISCORD_AUDIT_RUN_RATE_LIMIT_PER_DAY = 5 AA_DISCORD_AUDIT_RUN_RATE_LIMIT_DISABLED = False
AA_DISCORD_AUDIT_WEBHOOK_TIMEOUT = 10 AA_DISCORD_AUDIT_WEBHOOK_MAX_RETRIES = 3
AA_DISCORD_AUDIT_USE_BULK_ROLE_STRIP = False
AA_DISCORD_AUDIT_BEAT_ALLOW_DESTRUCTIVE = False
AA_DISCORD_AUDIT_BEAT_MIN_INTERVAL_MINUTES = 0
AA_DISCORD_AUDIT_CLI_ACTOR = None
AA_DISCORD_AUDIT_PRESENCE_ENABLED = True
AA_DISCORD_AUDIT_PRESENCE_SAMPLE_INTERVAL_MINUTES = 10
AA_DISCORD_AUDIT_METRICS_MEMBERS_BY_GROUP = True
AA_DISCORD_AUDIT_METRICS_MEMBERS_BY_ROLE = False
## Comandos de gestión
| Comando | Propósito |
|--------------------------|------------------------------------------------------------------|
| `audit_discord_roles` | Punto de entrada principal. `--action {report,strip,strip_kick}`. |
| `audit_discord_roles --resume <run_id>` | Re-examinar hallazgos PENDIENTES de una ejecución existente. |
| `audit_discord_roles --abandon <run_id>` | Marcar una ejecución atascada como ABANDONADA. |
| `audit_discord_roles --diff <run_id>` | Comparar el estado actual con una ejecución histórica. |
| `audit_discord_roles --explain <member_id>` | Clasificación por miembro (solo lectura). |
| `audit_discord_roles --policy-preview <json>` | Proyectar una política hipotética. |
| `audit_discord_roles --from-fixture <path>` | Reproducir contra una instantánea JSON. |
| `audit_acknowledge_initial` | Liberar el bloqueo de primera ejecución en seco desde la consola. |
| `audit_benchmark` | Benchmark de dimensionamiento de carga sintética (ver [`docs/performance.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/performance.md)). |
| `prune_audit_runs` | Poda de retención. |
| `audit_abandon_stuck_runs` | Reconciliar ejecuciones PENDIENTES atascadas filtradas por un trabajador bloqueado — las cambia a ABANDONADAS (ver el runbook; una ejecución RUNNING atascada usa `audit_discord_roles --abandon`). |
Una ejecución prolongada de ``audit_discord_roles`` reacciona limpiamente a ``SIGTERM``
y ``SIGINT``: la ejecución se cambia a ``INTERRUPTED`` y el bucle de aplicación
sale en el siguiente límite de hallazgo para que el trabajo parcial sea
duradero en el rastro de auditoría. ``audit_discord_roles --resume <run_id>``
retoma la ejecución desde los hallazgos ``PENDING`` restantes.
## Auditoría periódica (Celery beat)
`audit_orphan_members` ejecuta el mismo pipeline de construcción + aplicación en una
programación que usted conecta en Celery beat (no está programado por defecto).
La ruta no supervisada está controlada por seguridad:
- **Solo informe a menos que esté armado.** Una ejecución beat se fuerza a `report`
independientemente de la política a menos que `AA_DISCORD_AUDIT_BEAT_ALLOW_DESTRUCTIVE`
esté establecido — liberar el bloqueo de primera ejecución única para una ejecución CLI manual
no arma el beat. Una advertencia de inicio se dispara cuando la política es
destructiva pero el beat no ha optado por ello.
- **Atribuido.** Cada ejecución beat escribe un `AuditInvocation` aceptado de actor del sistema
(`triggered_by=BEAT`), por lo que el rastro de auditoría del auditor
cubre también las ejecuciones no supervisadas.
- **Auto-reparación.** Una ejecución varada por un límite de tiempo suave o un
bloqueo del trabajador se reconcilia al estado `INTERRUPTED` reanudable en el siguiente
tick. `AA_DISCORD_AUDIT_BEAT_MIN_INTERVAL_MINUTES` es un piso grueso
contra una programación rápida mal configurada.
## Tareas de Celery
El paquete registra cinco tareas bajo el prefijo de nombre `aa_discord_audit.*`.
Solo las tareas beat necesitan una programación; el resto son controladas por eventos
o se ejecutan bajo demanda. Ninguna está programada para usted.
| Tarea | Cómo se ejecuta | Qué hace |
|------|-------------|--------------|
| `aa_discord_audit.audit_orphan_members` | Celery beat — usted lo programa | La auditoría de construcción + aplicación no supervisada descrita anteriormente. Solo informe a menos que esté armado; comparte el bloqueo `discord.user_actions.<uid>` con `update_groups` de AA. |
| `aa_discord_audit.retry_pending_kicks` | Celery beat — usted lo programa | Barrido acotado que re-despacha expulsiones diferidas por una falla transitoria de Discord, respetando un enfriamiento por fila para que un miembro con fallos persistentes no sea golpeado repetidamente. |
| `aa_discord_audit.process_pending_run` | Controlado por eventos — encolado por un lanzamiento web | Toma la ejecución `PENDING` que creó un lanzamiento web, la cambia a `RUNNING` y conduce el pipeline contra la bandera de confirmación congelada de la ejecución. |
| `aa_discord_audit.prune_audit_runs` | Celery beat / cron — usted lo programa (también un comando de gestión) | Retención: expiración de clave de idempotencia luego eliminación de filas (ver **Comandos de gestión** y el runbook). |
| `aa_discord_audit.sample_guild_presence` | Celery beat — usted lo programa | Muestra ligera, solo REST, de recuentos de miembros / en línea / impulsos del gremio en la instantánea de presencia más reciente para los indicadores de presencia de Prometheus. Controlado por `AA_DISCORD_AUDIT_PRESENCE_ENABLED`; un guardia de salto limita una programación rápida mal configurada (piso `AA_DISCORD_AUDIT_PRESENCE_SAMPLE_INTERVAL_MINUTES`, predeterminado 10). Inerte hasta que tanto el extra `[metrics]` como una entrada beat estén presentes (ver [`docs/METRICS.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/METRICS.md)). |
Para programar las tareas beat, agréguelas a `CELERYBEAT_SCHEDULE` en su
`local.py`, por ejemplo:```python
CELERYBEAT_SCHEDULE["aa_discord_audit_sample_guild_presence"] = {
"task": "aa_discord_audit.sample_guild_presence",
"schedule": 600, # seconds; honoured no finer than the sampler floor
}
Montado bajo la navegación principal de Auth como Discord Audit. Vistas de solo lectura; cada lista tiene un cuadro de búsqueda del lado del servidor que coincide en todas las filas — no solo en la página visible — junto con sus filtros desplegables:
ProtectedDiscordMember, bloqueo de primera ejecución) decidió el resultado.Los operadores con el permiso aa_discord_audit.run_audit ven un botón Launch Audit en la página Audit runs. Al hacer clic se abre un modal de Bootstrap que muestra el modo de política configurado (solo informe o destructivo), el estado de reconocimiento inicial y un botón Confirm Launch. El envío de POST a /run-launch/, que crea un AuditRun PENDIENTE, encola process_pending_run a través de Celery y redirige a la página de detalle de la ejecución.
La ruta web refleja las puertas de seguridad de la CLI:
audit_acknowledge_initial, el modal muestra un bloque de rechazo (sin botón de envío) en lugar de la acción Confirm Launch. Un POST que evita el modal (por ejemplo, curl) es rechazado en el servidor, el rechazo se registra en AuditInvocation, y el operador es redirigido con un mensaje flash.was_confirmation_bypassed = user.has_perm(run_audit_destructive) and policy_has_destructive(policy). La interfaz no tiene interruptor — el permiso destructivo solo determina la intención. Un operador solo con run_audit que activa un lanzamiento contra una política destructiva obtiene una ejecución silenciosa de solo INFORME (la misma coerción que aplica la CLI sin --yes).Mientras una ejecución está en un estado no terminal (PENDING, RUNNING o INTERRUPTED), la página de detalle de la ejecución consulta /runs/<pk>/state.json cada cinco segundos y actualiza la tarjeta de Estado en su lugar. La consulta se pausa en pestañas ocultas y se detiene tan pronto como la ejecución alcanza un estado terminal.
Instrumentación opcional de Prometheus detrás del extra [metrics] — si está ausente, cada llamada de métrica se resuelve en un stub sin operación a un costo casi nulo. El módulo incluye una capa de cooperación sin dependencia: cuando django-prometheus está instalado, los contadores e histogramas de la auditoría se registran en el prometheus_client.REGISTRY predeterminado y la vista /metrics de django-prometheus los exporta junto con sus propias series.```sh
pip install aa-discord-audit[metrics]
Los gauges de instantáneas — presencia del gremio y los agregados de membresía por grupo/por rol — siguen un segundo camino: residen en un registro dedicado exportado por el endpoint `/audit/discord/metrics` del propio módulo. El recolector multiproceso de django-prometheus solo lee archivos mmap y omite los colectores personalizados, por lo que estos gauges necesitan su propio objetivo de recolección. Como cualquier objetivo `/metrics`, no está autenticado — restrínjalo en el proxy inverso o en la capa de red. Ambas superficies permanecen inactivas sin el extra `[metrics]`.
El catálogo de métricas, el vocabulario de etiquetas y las recetas de Grafana se encuentran en
[`docs/METRICS.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/METRICS.md)
(traducción al ruso:
[`docs/METRICS.ru.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/METRICS.ru.md)).
## Limitaciones
- **Un solo gremio.** La auditoría reconcilia el único gremio de Discord contra el que está configurado AA; no abarca múltiples gremios.
- **Solo roles gestionados por AA.** Las acciones de despojar/expulsar actúan sobre roles con nombre AA y la membresía del gremio; los roles que AA no gestiona nunca se tocan.
- **Sin autodescubrimiento de apodos.** Las cuentas de bot se reconocen solo a través de la tabla explícita `BotAccountUid` / `AA_DISCORD_AUDIT_BOT_UIDS` — `AA_DISCORD_AUDIT_AUTO_DISCOVER_BY_NICKNAME` está reservado para v2 y no implementado.
- **Superficie alfa.** Los nombres de configuración y la API pública pueden cambiar antes de `1.0`.
## Documentación
- [`docs/runbook.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/runbook.md) — manual del operador: permisos del bot de Discord, lista de verificación previa al vuelo, liberación del bloqueo de primera ejecución, guías de incidentes, interruptores de diagnóstico.
- [`docs/performance.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/performance.md) — números de referencia de `audit_benchmark` e implicaciones de dimensionamiento.
- [`docs/METRICS.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/METRICS.md) / [`docs/METRICS.ru.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/METRICS.ru.md) — catálogo de métricas de Prometheus, vocabulario de etiquetas, recetas de Grafana.
- Código fuente: <https://gitlab.com/eveo7/aa-discord-audit>
- Seguimiento de incidencias: <https://gitlab.com/eveo7/aa-discord-audit/-/issues>
- Registro de cambios: [`CHANGELOG.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/CHANGELOG.md)
## Desarrollo```sh
make dev # uv sync --all-groups + pre-commit install
make tests # uv run nox -s tests
make lint # uv run nox -s lint
make typecheck # mypy + basedpyright
make coverage # term + html + xml report
make package # uv build
La cadena de herramientas es solo uv. La longitud de línea es 79 (Python) / 120 (Markdown).
MIT — consulte LICENSE.