
Abgleichs-Audit für die Discord-Integration von Alliance Auth: findet Servermitglieder, die AA-verwaltete Rollen besitzen, die Auth nie gewährt hat, und entzieht oder entfernt sie unter einer vom Betreiber gesteuerten Richtlinie. Eigenständige Community-Django-App.
Abgleichsprüfung für die Discord-Integration von Alliance Auth. Vergleicht die tatsächlichen Rollenzuweisungen im konfigurierten Discord-Server mit dem in Alliance Auth ausgedrückten Zustand (Gruppen + Status pro Benutzer) und schließt die Lücke, durch die Moderatoren AA-benannte Rollen manuell an Benutzer vergeben können, von denen AA nichts weiß.
Status: Alpha (
0.1.x). Die öffentliche API und die Einstellungen können sich vor1.0noch ändern.
Die Prüfung ist standardmäßig sicher:
InitialAuditAcknowledgement (nur Admin oder Shell).report — zerstörerische Aktionen
sind Opt-in.AuditRun, AuditFinding, AuditInvocation,
ConfigChangeLog) sind auf Manager- und Instanzebene nur zum Anhängen;
bulk update() / bulk_update() sind blockiert.Jedes Mitglied des Servers wird in eine Kategorie eingeteilt:
| Kategorie | Bedeutung |
|---|---|
unknown_guest | Discord-Mitglied, von dem AA nichts weiß |
linked_no_perm | Identität bei AA bekannt, aber es fehlt discord.access_discord |
bot_filtered | Konfiguriertes Bot-Konto – wird nie bearbeitet |
Der Operator ordnet jeder Kategorie eine Aktion zu:
| Aktion | Verhalten |
|---|---|
report | Fund protokollieren; keine Änderung auf Discord-Seite |
strip | AA-verwaltete Rollen entfernen |
strip_kick | AA-verwaltete Rollen entfernen, dann vom Server kicken |
Die Zuordnung ist die Einstellung AA_DISCORD_AUDIT_POLICY; Überschreibungen pro Gruppe
und pro Status sind in jeder Kategorie verschachtelt.
allianceauth.services.modules.discord) installiert und konfiguriert
(Bot-Token + Server)pip install aa-discord-audit
In Ihrem 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",
]
Dann führen Sie Migrationen aus:```sh python manage.py migrate aa_discord_audit
The `CurrentUserMiddleware` ist zwingend erforderlich – `apps.ready()` löst `ImproperlyConfigured` aus, wenn sie fehlt. Sie ermöglicht es dem Signalhandler `ConfigChangeLog`, Admin-Bearbeitungen einem echten Benutzer zuzuordnen, anstatt `<system>`.
## Schnellstart
1. Erteilen Sie `aa_discord_audit.run_audit` an die Operator-Rolle, die Audits durchführt.
2. Führen Sie einen Dry-Run-Audit durch: ```sh
python manage.py audit_discord_roles --action report
InitialAuditAcknowledgement-Zeile über den Admin, oder durch Ausführen von
python manage.py audit_acknowledge_initial. Beide erfordern
aa_discord_audit.run_audit und
aa_discord_audit.acknowledge_initial_audit.Die manage_*-Codenamen sind nach Blast Radius aufgeteilt, damit ein Junior mit
manage_bot_account_uid das Audit nicht ebenfalls durch Bearbeiten von
ManagedRoleException entschärfen kann.
Alle Einstellungen sind optional. Die Standardeinstellungen sind sicher.```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
## Management commands
| Command | Zweck |
|--------------------------|----------------------------------------------------------------|
| `audit_discord_roles` | Primärer Einstiegspunkt. `--action {report,strip,strip_kick}`. |
| `audit_discord_roles --resume <run_id>` | Offene (PENDING) Ergebnisse eines vorhandenen Durchlaufs erneut durchgehen. |
| `audit_discord_roles --abandon <run_id>` | Einen feststeckenden Durchlauf als ABANDONED markieren. |
| `audit_discord_roles --diff <run_id>` | Aktuellen Zustand mit einem historischen Durchlauf vergleichen.|
| `audit_discord_roles --explain <member_id>` | Mitgliedsbezogene Klassifikation (schreibgeschützt). |
| `audit_discord_roles --policy-preview <json>` | Eine hypothetische Richtlinie projizieren. |
| `audit_discord_roles --from-fixture <path>` | Gegen einen JSON-Snapshot wiedergeben. |
| `audit_acknowledge_initial` | Den Erstlauf-Trockenlauf-Sperre von der Konsole freigeben. |
| `audit_benchmark` | Benchmark zur synthetischen Lastbemessung (siehe [`docs/performance.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/performance.md)). |
| `prune_audit_runs` | Aufbewahrungsbereinigung. |
| `audit_abandon_stuck_runs` | Feststeckende PENDING-Durchläufe, die von einem abgestürzten Worker hinterlassen wurden, bereinigen – setzt sie auf ABANDONED (siehe Runbook; ein feststeckender RUNNING-Durchlauf verwendet `audit_discord_roles --abandon`). |
Ein langlebiger ``audit_discord_roles``-Durchlauf reagiert sauber auf ``SIGTERM``
und ``SIGINT``: Der Durchlauf wird auf ``INTERRUPTED`` gesetzt und die Anwendungsschleife
verlässt den nächsten Ergebnisgrenzpunkt, sodass die Teilarbeit im Audit-Trail
dauerhaft erhalten bleibt. ``audit_discord_roles --resume <run_id>``
nimmt den Durchlauf ab den verbleibenden ``PENDING``-Ergebnissen wieder
auf.
## Periodische Überprüfung (Celery beat)
`audit_orphan_members` führt dieselbe Build-+Apply-Pipeline nach einem
Zeitplan aus, den Sie in Celery beat einbinden (standardmäßig nicht geplant).
Der unbeaufsichtigte Pfad ist aus Sicherheitsgründen abgesichert:
- **Nur Bericht, es sei denn, bewaffnet.** Ein Beat-Durchlauf wird unabhängig
von der Richtlinie zu `report` gezwungen, es sei denn,
`AA_DISCORD_AUDIT_BEAT_ALLOW_DESTRUCTIVE` ist gesetzt – das Freigeben der
Einmal-Erstlauf-Sperre für einen manuellen CLI-Durchlauf bewaffnet den Beat
nicht. Eine Startwarnung wird ausgegeben, wenn die Richtlinie destruktiv ist,
der Beat aber nicht bestätigt wurde.
- **Zugeordnet.** Jeder Beat-Durchlauf schreibt eine akzeptierte
System-Actor-`AuditInvocation` (`triggered_by=BEAT`), sodass die
Audit-the-Auditor-Spur auch unbeaufsichtigte Durchläufe abdeckt.
- **Selbstheilend.** Ein Durchlauf, der durch einen Soft-Time-Limit-Treffer
oder einen Worker-Absturz gestrandet ist, wird beim nächsten Takt in den
fortsetzbaren `INTERRUPTED`-Zustand versetzt.
`AA_DISCORD_AUDIT_BEAT_MIN_INTERVAL_MINUTES` ist eine grobe Untergrenze
gegen eine fehlkonfigurierte schnelle Zeitplanung.
## Celery-Aufgaben
Das Paket registriert fünf Aufgaben unter dem Namenspräfix
`aa_discord_audit.*`. Nur die Beat-Aufgaben benötigen einen Zeitplan;
die restlichen sind ereignisgesteuert oder werden auf Abruf ausgeführt. Keine
werden für Sie eingeplant.
| Aufgabe | Ausführung | Beschreibung |
|---------|------------|--------------|
| `aa_discord_audit.audit_orphan_members` | Celery beat — Sie planen es | Die oben beschriebene unbeaufsichtigte Build-+Apply-Prüfung. Nur Bericht, es sei denn, bewaffnet; teilt die `discord.user_actions.<uid>`-Sperre mit AA's `update_groups`. |
| `aa_discord_audit.retry_pending_kicks` | Celery beat — Sie planen es | Begrenzter Durchlauf, der durch einen vorübergehenden Discord-Fehler verzögerte Kicks erneut ausführt und eine zeilenweise Abklingzeit einhält, damit ein hart fehlschlagendes Mitglied nicht überlastet wird. |
| `aa_discord_audit.process_pending_run` | Ereignisgesteuert — durch einen Web-Start in die Warteschlange eingereiht | Nimmt den `PENDING`-Durchlauf auf, den ein Web-Start erstellt hat, setzt ihn auf `RUNNING` und treibt die Pipeline gegen das eingefrorene Bestätigungsflag des Durchlaufs an. |
| `aa_discord_audit.prune_audit_runs` | Celery beat / cron — Sie planen es (auch ein Management-Befehl) | Aufbewahrung: Ablauf des Idempotenzschlüssels, dann Löschen der Zeilen (siehe **Management-Befehle** und das Runbook). |
| `aa_discord_audit.sample_guild_presence` | Celery beat — Sie planen es | Leichte, REST-only-Stichprobe von Servermitglieder-/Online-/Boost-Anzahlen in den aktuellen Anwesenheits-Snapshot für die Prometheus-Anwesenheitsmesswerte. Geschützt durch `AA_DISCORD_AUDIT_PRESENCE_ENABLED`; eine Skip-Guard begrenzt eine fehlkonfigurierte schnelle Zeitplanung (Untergrenze `AA_DISCORD_AUDIT_PRESENCE_SAMPLE_INTERVAL_MINUTES`, Standard 10). Inaktiv, bis sowohl das `[metrics]`-Extra als auch ein Beat-Eintrag vorhanden sind (siehe [`docs/METRICS.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/METRICS.md)). |
Um die Beat-Aufgaben zu planen, fügen Sie sie zu `CELERYBEAT_SCHEDULE` in Ihrer
`local.py` hinzu, zum Beispiel:```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
}
Unter der Auth-Hauptnavigation als Discord Audit eingebunden. Schreibgeschützte Ansichten; jede Liste verfügt über eine serverseitige Suchbox, die über alle Zeilen hinweg sucht – nicht nur über die aktuelle Seite – sowie über Dropdown-Filter:
ProtectedDiscordMember, First-Run-Lock) das Ergebnis entschieden hat.Operatoren mit der Berechtigung aa_discord_audit.run_audit sehen auf der Seite Audit-Läufe einen Audit starten-Button. Ein Klick öffnet ein Bootstrap-Modal, das den konfigurierten Richtlinienmodus (nur Bericht oder destruktiv), den initialen Bestätigungsstatus und einen Starten bestätigen-Button anzeigt. Das Senden von POSTs an /run-launch/ erstellt einen PENDENTEN AuditRun, stellt process_pending_run über Celery in die Warteschlange und leitet zur Detailseite des Laufs weiter.
Der Webpfad spiegelt die Sicherheitsmechanismen der CLI wider:
audit_acknowledge_initial nicht ausgeführt wurde, zeigt das Modal einen Ablehnungsblock (kein Senden-Button) anstelle der Starten bestätigen-Aktion. Ein POST, der das Modal umgeht (z.B. curl), wird serverseitig abgelehnt, die Ablehnung wird in AuditInvocation protokolliert, und der Operator wird mit einer Flash-Nachricht weitergeleitet.was_confirmation_bypassed = user.has_perm(run_audit_destructive) and policy_has_destructive(policy). Die UI hat keinen Kippschalter — die destruktive Berechtigung allein bestimmt die Absicht. Ein Operator mit nur run_audit-Berechtigung, der einen Start gegen eine destruktive Richtlinie auslöst, erhält einen stillen REPORT-only-Lauf (derselbe Zwang, den die CLI ohne --yes anwendet).Während sich ein Lauf in einem nicht-terminalen Zustand befindet (PENDING, RUNNING oder INTERRUPTED), ruft die Detailseite des Laufs alle fünf Sekunden /runs/<pk>/state.json ab und aktualisiert die Statuskarte direkt. Polling pausiert auf versteckten Registerkarten und stoppt, sobald der Lauf einen terminalen Zustand erreicht.
Optionale Prometheus-Instrumentierung hinter dem [metrics]-Extra — wenn nicht vorhanden, löst sich jeder Metrikaufruf in einen No-op-Stub nahezu ohne Kosten auf. Das Modul liefert eine Kooperiere-statt-Abhängig-Schicht: Wenn django-prometheus installiert ist, registrieren sich die Zähler und Histogramme des Audits im Standard-prometheus_client.REGISTRY und die /metrics-Ansicht von django-prometheus exportiert sie zusammen mit ihren eigenen Reihen.```sh
pip install aa-discord-audit[metrics]
Die Snapshot-Gauges – Gildenpräsenz und die Aggregate pro Gruppe / pro Rolle – nehmen einen zweiten Pfad: Sie befinden sich in einem dedizierten Registry, die vom eigenen `/audit/discord/metrics`-Endpunkt des Moduls exportiert wird. Der Multiprozess-Collector von django-prometheus liest nur mmap-Dateien und überspringt benutzerdefinierte Collectoren, daher benötigen diese Gauges ihr eigenes Scrape-Ziel. Wie jedes `/metrics`-Ziel ist es nicht authentifiziert – schränken Sie es auf der Reverse-Proxy- oder Netzwerkebene ein. Beide Oberflächen bleiben ohne das `[metrics]`-Extra inaktiv.
Metrikkatalog, Label-Vokabular und Grafana-Rezepte befinden sich in [`docs/METRICS.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/METRICS.md) (russische Übersetzung: [`docs/METRICS.ru.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/METRICS.ru.md)).
## Einschränkungen
- **Einzelne Gilde.** Die Prüfung gleicht die eine Discord-Gilde ab, gegen die AA konfiguriert ist; sie erstreckt sich nicht über mehrere Gilden.
- **Nur AA-verwaltete Rollen.** Entfernen / Kicken wirkt auf AA-benannte Rollen und Gildenmitgliedschaften; Rollen, die AA nicht verwaltet, werden nie angetastet.
- **Keine automatische Nickname-Erkennung.** Bot-Konten werden nur über die explizite `BotAccountUid`-Tabelle / `AA_DISCORD_AUDIT_BOT_UIDS` erkannt – `AA_DISCORD_AUDIT_AUTO_DISCOVER_BY_NICKNAME` ist für v2 reserviert und nicht implementiert.
- **Alpha-Oberfläche.** Einstellungsnamen und die öffentliche API können sich vor `1.0` ändern.
## Dokumentation
- [`docs/runbook.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/runbook.md) — Betreiber-Runbook: Bot-Discord-Berechtigungen, Pre-Flight-Checkliste, Freigabe der Erstausführungssperre, Vorfall-Playbooks, Diagnose-Toggles.
- [`docs/performance.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/performance.md) — `audit_benchmark`-Referenzzahlen und Auswirkungen auf die Dimensionierung.
- [`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) — Prometheus-Metrikkatalog, Label-Vokabular, Grafana-Rezepte.
- Source code: <https://gitlab.com/eveo7/aa-discord-audit>
- Issue tracker: <https://gitlab.com/eveo7/aa-discord-audit/-/issues>
- Changelog: [`CHANGELOG.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/CHANGELOG.md)
## Entwicklung```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
Toolchain ist uv-only. Zeilenlänge ist 79 (Python) / 120 (Markdown).
MIT — siehe LICENSE.
| Codename | Zugänge |
|---|
aa_discord_audit.run_audit | Management-Befehl, Beat-Aufgabe, Löschvorgang ausführen |
aa_discord_audit.run_audit_destructive | Web-Start-Tor für strip / strip_kick (getrennt von run_audit) |
aa_discord_audit.acknowledge_initial_audit | Sperre für den ersten Trockentestlauf freigeben |
aa_discord_audit.manage_discord_identity | DiscordIdentity-Admin |
aa_discord_audit.manage_role_exception | ManagedRoleException-Admin |
aa_discord_audit.manage_protected_member | ProtectedDiscordMember-Admin |
aa_discord_audit.manage_bot_account_uid | BotAccountUid-Admin |
aa_discord_audit.manage_finding_override | FindingActionOverride-Admin |
aa_discord_audit.view_auditrun (und Verwandte) | schreibgeschützter Audit-Pfad im Auth-Dashboard |