
Audit de réconciliation pour l'intégration Discord d'Alliance Auth : détecte les membres de la guilde qui détiennent des rôles gérés par AA qu'Auth n'a jamais accordés et les retire ou les exclut selon une politique contrôlée par l'opérateur. Application Django autonome de la communauté.
Audit de réconciliation pour l'intégration Discord d'Alliance Auth. Compare les attributions de rôles réelles dans le serveur Discord configuré avec l'état exprimé dans Alliance Auth (Groupes + État par utilisateur) et comble l'écart par lequel les modérateurs peuvent attribuer manuellement des rôles nommés AA à des utilisateurs qu'AA ne connaît pas.
Statut : alpha (
0.1.x). L'API publique et les paramètres peuvent encore changer avant la version1.0.
L'audit est sûr par défaut :
InitialAuditAcknowledgement explicite (admin ou shell uniquement).report pour chaque catégorie — les actions destructrices sont sur option.AuditRun, AuditFinding, AuditInvocation, ConfigChangeLog) sont en mode append-only au niveau des managers et des instances ; les update() / bulk_update() en masse sont bloqués.Chaque membre du serveur est classé dans une catégorie :
| Catégorie | Signification |
|---|---|
unknown_guest | Membre Discord dont AA ne sait rien |
linked_no_perm | Identité connue d'AA mais sans discord.access_discord |
bot_filtered | Compte bot configuré — jamais traité |
L'opérateur associe chaque catégorie à une action :
| Action | Comportement |
|---|---|
report | Enregistrer la constatation ; aucun changement côté Discord |
strip | Supprimer les rôles gérés par AA |
strip_kick | Supprimer les rôles gérés par AA, puis exclure du serveur |
Le mappage est le paramètre AA_DISCORD_AUDIT_POLICY ; les surcharges par groupe et par état s'imbriquent dans chaque catégorie.
allianceauth.services.modules.discord) installé et configuré (jeton du bot + serveur)pip install aa-discord-audit
Dans votre 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",
]
Ensuite, exécutez les migrations :
python manage.py migrate
``````sh
python manage.py migrate aa_discord_audit
Le CurrentUserMiddleware est obligatoire — apps.ready() lève ImproperlyConfigured s'il manque. C'est ce qui permet au gestionnaire de signal ConfigChangeLog d'attribuer les modifications d'administration à un véritable utilisateur au lieu de <system>.
aa_discord_audit.run_audit au rôle d'opérateur qui exécute les audits.InitialAuditAcknowledgement via l'admin, soit en exécutant python manage.py audit_acknowledge_initial. Les deux nécessitent aa_discord_audit.run_audit et aa_discord_audit.acknowledge_initial_audit.Les codes manage_* sont répartis par rayon d'impact afin qu'un junior disposant de manage_bot_account_uid ne puisse pas désarmer l'audit en modifiant ManagedRoleException.
Tous les paramètres sont optionnels. Les valeurs par défaut sont sûres.```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
## Commandes de gestion
| Commande | Objectif |
|---------------------------|------------------------------------------------------------------|
| `audit_discord_roles` | Point d'entrée principal. `--action {report,strip,strip_kick}`. |
| `audit_discord_roles --resume <run_id>` | Reparcourir les résultats PENDING d'une exécution existante. |
| `audit_discord_roles --abandon <run_id>` | Marquer une exécution bloquée comme ABANDONNÉE. |
| `audit_discord_roles --diff <run_id>` | Comparer l'état actuel à une exécution historique. |
| `audit_discord_roles --explain <member_id>` | Classification par membre (lecture seule). |
| `audit_discord_roles --policy-preview <json>` | Projeter une politique hypothétique. |
| `audit_discord_roles --from-fixture <path>` | Rejouer à partir d'un instantané JSON. |
| `audit_acknowledge_initial` | Libérer le verrou du premier essai à sec depuis la console. |
| `audit_benchmark` | Benchmark de dimensionnement de charge synthétique (voir [`docs/performance.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/performance.md)). |
| `prune_audit_runs` | Purge de rétention. |
| `audit_abandon_stuck_runs` | Réconcilier les exécutions PENDING bloquées laissées par un worker planté — les bascule vers ABANDONNÉ (voir le runbook ; une exécution RUNNING bloquée utilise `audit_discord_roles --abandon`). |
Une exécution longue de ``audit_discord_roles`` réagit proprement à ``SIGTERM``
et ``SIGINT`` : l'exécution est basculée en ``INTERRUPTED`` et la boucle
d'application s'arrête à la prochaine limite de résultat, de sorte que le travail
partiel soit durable dans la piste d'audit. ``audit_discord_roles --resume <run_id>``
reprend l'exécution à partir des résultats ``PENDING`` restants.
## Audit périodique (Celery beat)
`audit_orphan_members` exécute le même pipeline de construction + application
selon un planning que vous intégrez dans Celery beat (il n'est pas planifié par défaut).
Le chemin non supervisé est verrouillé pour des raisons de sécurité :
- **Rapport uniquement sauf si armé.** Une exécution beat est forcée en `report`
quelle que soit la politique, sauf si `AA_DISCORD_AUDIT_BEAT_ALLOW_DESTRUCTIVE`
est défini — libérer le verrou unique du premier essai pour une exécution CLI manuelle
n'arme pas le beat. Un avertissement au démarrage s'affiche lorsque la politique est
destructive mais que le beat n'est pas activé.
- **Attribué.** Chaque exécution beat écrit une `AuditInvocation` acceptée par un acteur
système (`triggered_by=BEAT`), de sorte que la piste d'audit des auditeurs couvre
également les exécutions non supervisées.
- **Auto-réparation.** Une exécution échouée suite à une limite de temps douce ou à un
plantage de worker est réconciliée à l'état `INTERRUPTED` (reprenable) lors du prochain
tick. `AA_DISCORD_AUDIT_BEAT_MIN_INTERVAL_MINUTES` est un plancher approximatif contre
une planification rapide mal configurée.
## Tâches Celery
Le paquet enregistre cinq tâches sous le préfixe de nom `aa_discord_audit.*`.
Seules les tâches beat ont besoin d'un planning ; les autres sont pilotées par
événement ou exécutées à la demande. Aucune n'est planifiée pour vous.
| Tâche | Mode d'exécution | Description |
|------|-------------|--------------|
| `aa_discord_audit.audit_orphan_members` | Celery beat — vous le planifiez | L'audit non supervisé de construction + application décrit ci-dessus. Rapport uniquement sauf si armé ; partage le verrou `discord.user_actions.<uid>` avec `update_groups` d'AA. |
| `aa_discord_audit.retry_pending_kicks` | Celery beat — vous le planifiez | Balayage limité qui ré-expédie les expulsions différées par un échec transitoire de Discord, respectant un délai de refroidissement par ligne afin qu'un membre en échec persistant ne soit pas sollicité de manière répétée. |
| `aa_discord_audit.process_pending_run` | Piloté par événement — mis en file d'attente par un lancement web | Récupère l'exécution `PENDING` créée par un lancement web, la bascule en `RUNNING`, et pilote le pipeline selon le drapeau de confirmation figé de l'exécution. |
| `aa_discord_audit.prune_audit_runs` | Celery beat / cron — vous le planifiez (également une commande de gestion) | Rétention : expiration de la clé d'idempotence puis suppression de lignes (voir **Commandes de gestion** et le runbook). |
| `aa_discord_audit.sample_guild_presence` | Celery beat — vous le planifiez | Échantillon léger, uniquement REST, des comptes de membres / en ligne / boosts du serveur dans l'instantané de présence le plus récent pour les jauges Prometheus de présence. Conditionné par `AA_DISCORD_AUDIT_PRESENCE_ENABLED` ; un garde-fou de saut limite une planification rapide mal configurée (plancher `AA_DISCORD_AUDIT_PRESENCE_SAMPLE_INTERVAL_MINUTES`, défaut 10). Inerte tant que l'extra `[metrics]` et une entrée beat ne sont pas présents (voir [`docs/METRICS.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/METRICS.md)). |
Pour planifier les tâches beat, ajoutez-les à `CELERYBEAT_SCHEDULE` dans votre
`local.py`, par exemple :```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
}
Monté sous la navigation principale Auth en tant que Discord Audit. Vues en lecture seule ; chaque liste comporte une boîte de recherche côté serveur qui correspond à chaque ligne — pas seulement la page en cours — ainsi que ses filtres déroulants :
ProtectedDiscordMember, verrou de première exécution) a décidé du résultat.Les opérateurs disposant de la permission aa_discord_audit.run_audit voient un
bouton Lancer un audit sur la page Audit runs. Cliquer dessus ouvre une
modale Bootstrap qui affiche le mode de politique configuré (rapport uniquement
ou destructif), l'état d'accusé de réception initial, et un bouton Confirmer le lancement.
Soumettre un POST vers /run-launch/, qui crée un AuditRun en état
PENDING, met en file d'attente process_pending_run via Celery, et
redirige vers la page de détail de l'exécution.
Le chemin web reflète les barrières de sécurité de la CLI :
audit_acknowledge_initial n'a pas été
exécuté, la modale affiche un bloc de refus (pas de bouton de soumission) au lieu de
l'action Confirmer le lancement. Un POST qui contourne la modale (par ex.
curl) est refusé côté serveur, le refus est enregistré dans
AuditInvocation, et l'opérateur est redirigé avec un message flash.was_confirmation_bypassed = user.has_perm(run_audit_destructive) and policy_has_destructive(policy). L'interface n'a aucune bascule — la
permission destructive seule détermine l'intention. Un opérateur avec seulement run_audit
qui déclenche un lancement contre une politique destructive obtient une
exécution silencieuse en mode RAPPORT uniquement (la même coercition que la CLI applique sans
--yes).Tant qu'une exécution est dans un état non terminal (PENDING, RUNNING, ou
INTERRUPTED), la page de détail de l'exécution interroge /runs/<pk>/state.json
toutes les cinq secondes et met à jour la carte d'état sur place. L'interrogation
se met en pause sur les onglets cachés et s'arrête dès que l'exécution atteint un état
terminal.
Instrumentation Prometheus optionnelle derrière l'extra [metrics] —
en son absence, chaque appel de métrique se résout en un stub inopérant à un coût quasi nul.
Le module intègre une couche « coopérez, ne dépendez pas » : lorsque django-prometheus
est installé, les compteurs et histogrammes de l'audit s'enregistrent dans le
prometheus_client.REGISTRY par défaut et la vue /metrics de django-prometheus
les exporte aux côtés de ses propres séries.```sh
pip install aa-discord-audit[metrics]
Les jauges instantanées — présence du serveur et agrégats d'appartenance par groupe / par rôle — empruntent une deuxième voie : elles résident dans un registre dédié exporté par le propre point de terminaison `/audit/discord/metrics` du module. Le collecteur multiprocess de django-prometheus ne lit que les fichiers mmap et ignore les collecteurs personnalisés, donc ces jauges nécessitent leur propre cible de collecte. Comme toute cible `/metrics`, elle n'est pas authentifiée — restreignez-la au niveau du proxy inverse ou du réseau. Les deux surfaces restent inactives sans l'extension `[metrics]`.
Le catalogue de métriques, le vocabulaire des étiquettes et les recettes Grafana se trouvent dans
[`docs/METRICS.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/METRICS.md)
(Traduction russe :
[`docs/METRICS.ru.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/METRICS.ru.md)).
## Limitations
- **Serveur unique.** L'audit concilie le seul serveur Discord pour lequel AA est configuré ; il ne couvre pas plusieurs serveurs.
- **Rôles gérés par AA uniquement.** Les actions de retrait / exclusion agissent sur les rôles nommés par AA et l'appartenance au serveur ; les rôles qu'AA ne gère pas ne sont jamais touchés.
- **Pas d'auto-découverte par pseudo.** Les comptes robots sont reconnus uniquement via la table explicite `BotAccountUid` / `AA_DISCORD_AUDIT_BOT_UIDS` — `AA_DISCORD_AUDIT_AUTO_DISCOVER_BY_NICKNAME` est réservé pour v2 et non implémenté.
- **Surface alpha.** Les noms des paramètres et l'API publique peuvent changer avant la version `1.0`.
## Documentation
- [`docs/runbook.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/runbook.md)
— manuel d'exploitation : permissions du bot Discord, liste de vérification avant vol,
déverrouillage de la première exécution, playbooks d'incidents, interrupteurs de diagnostic.
- [`docs/performance.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/performance.md)
— chiffres de référence `audit_benchmark` et implications de dimensionnement.
- [`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)
— catalogue de métriques Prometheus, vocabulaire des étiquettes, recettes Grafana.
- Code source : <https://gitlab.com/eveo7/aa-discord-audit>
- Suivi des problèmes : <https://gitlab.com/eveo7/aa-discord-audit/-/issues>
- Journal des modifications :
[`CHANGELOG.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/CHANGELOG.md)
## Développement```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 chaîne d'outils est uniquement uv. La longueur de ligne est de 79 (Python) / 120 (Markdown).
MIT — voir LICENSE.
| Codename | Gates |
|---|
aa_discord_audit.run_audit | commande de gestion, tâche beat, exécution delete |
aa_discord_audit.run_audit_destructive | porte de lancement web pour strip / strip_kick (séparée de run_audit) |
aa_discord_audit.acknowledge_initial_audit | libère le verrou de première exécution (simulation) |
aa_discord_audit.manage_discord_identity | admin DiscordIdentity |
aa_discord_audit.manage_role_exception | admin ManagedRoleException |
aa_discord_audit.manage_protected_member | admin ProtectedDiscordMember |
aa_discord_audit.manage_bot_account_uid | admin BotAccountUid |
aa_discord_audit.manage_finding_override | admin FindingActionOverride |
aa_discord_audit.view_auditrun (and friends) | audit trail en lecture seule dans le tableau de bord d'Auth |