
Audit di riconciliazione per l'integrazione Discord di Alliance Auth: trova i membri della gilda che detengono ruoli gestiti da AA che Auth non ha mai concesso e li rimuove o espelle secondo una politica controllata dall'operatore. App Django standalone della comunità.
Audit di riconciliazione per l'integrazione Discord di Alliance Auth. Confronta le assegnazioni di ruolo effettive nel guild Discord configurato con lo stato espresso in Alliance Auth (Gruppi + State per utente) e colma il divario attraverso il quale i moderatori possono assegnare manualmente ruoli con nome AA a utenti che AA non conosce.
Stato: alpha (
0.1.x). L'API pubblica e le impostazioni potrebbero ancora cambiare prima di1.0.
L'audit è sicuro per impostazione predefinita:
InitialAuditAcknowledgement (solo admin o shell).report per ogni categoria — le azioni
distruttive sono opt-in.AuditRun, AuditFinding, AuditInvocation,
ConfigChangeLog) sono append-only a livello di manager e istanza;
le operazioni bulk update() / bulk_update() sono bloccate.Ogni membro del guild è classificato in una categoria:
| Categoria | Significato |
|---|---|
unknown_guest | Membro Discord di cui AA non sa nulla |
linked_no_perm | Identità nota ad AA ma priva di discord.access_discord |
bot_filtered | Account bot configurato — mai soggetto ad azioni |
L'operatore associa ogni categoria a un'azione:
| Azione | Comportamento |
|---|---|
report | Registra il risultato; nessuna modifica su Discord |
strip | Rimuovi i ruoli gestiti da AA |
strip_kick | Rimuovi i ruoli gestiti da AA, poi espelli dal guild |
La mappatura è l'impostazione AA_DISCORD_AUDIT_POLICY; le sovrascritture
per gruppo e per stato sono annidate all'interno di ogni categoria.
allianceauth.services.modules.discord) installato e configurato
(bot token + guild)pip install aa-discord-audit
Nel tuo 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",
]
Quindi esegui le migrazioni:```sh python manage.py migrate aa_discord_audit
Il `CurrentUserMiddleware` è obbligatorio — `apps.ready()` solleva `ImproperlyConfigured` se manca. È ciò che permette al gestore del segnale `ConfigChangeLog` di attribuire le modifiche dell'amministratore a un utente reale invece che a `<system>`.
## Avvio rapido
1. Concedi `aa_discord_audit.run_audit` al ruolo operatore che esegue gli audit.
2. Esegui un audit dry-run: ```sh
python manage.py audit_discord_roles --action report
InitialAuditAcknowledgement tramite l'amministratore, oppure esegui
python manage.py audit_acknowledge_initial. Entrambi richiedono
aa_discord_audit.run_audit e
aa_discord_audit.acknowledge_initial_audit.I codename manage_* sono suddivisi per raggio d'esplosione in modo che un junior con
manage_bot_account_uid non possa anche disattivare l'audit modificando
ManagedRoleException.
Tutte le impostazioni sono opzionali. I valori predefiniti sono sicuri.```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
## Comandi di gestione
| Comando | Scopo |
|--------------------------|----------------------------------------------------------------|
| `audit_discord_roles` | Punto di ingresso principale. `--action {report,strip,strip_kick}`. |
| `audit_discord_roles --resume <run_id>` | Ripercorre i risultati PENDING di un'esecuzione esistente. |
| `audit_discord_roles --abandon <run_id>` | Contrassegna un'esecuzione bloccata come ABANDONED. |
| `audit_discord_roles --diff <run_id>` | Confronta lo stato attuale con un'esecuzione storica. |
| `audit_discord_roles --explain <member_id>` | Classificazione per membro (sola lettura). |
| `audit_discord_roles --policy-preview <json>` | Proietta una politica ipotetica. |
| `audit_discord_roles --from-fixture <path>` | Riproduce contro un'istantanea JSON. |
| `audit_acknowledge_initial` | Rilascia il blocco di dry-run della prima esecuzione dalla console. |
| `audit_benchmark` | Benchmark di dimensionamento del carico sintetico (vedi [`docs/performance.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/performance.md)). |
| `prune_audit_runs` | Pulizia della retention. |
| `audit_abandon_stuck_runs` | Riconcilia le esecuzioni PENDING bloccate perse da un worker crashato — le trasforma in ABANDONED (vedi il runbook; per un'esecuzione RUNNING bloccata usa `audit_discord_roles --abandon`). |
Un'esecuzione di lunga durata di ``audit_discord_roles`` reagisce correttamente a ``SIGTERM``
e ``SIGINT``: l'esecuzione viene trasformata in ``INTERRUPTED`` e il ciclo
di applicazione termina al prossimo confine di risultato, in modo che il lavoro
parziale sia durevole nella traccia di audit. ``audit_discord_roles --resume <run_id>``
riprende l'esecuzione dai risultati ``PENDING`` rimanenti.
## Audit periodico (Celery beat)
`audit_orphan_members` esegue la stessa pipeline di build + apply su una
pianificazione che inserisci in Celery beat (non è pianificata per impostazione
predefinita). Il percorso senza supervisione è protetto per sicurezza:
- **Solo report se non armato.** Un'esecuzione beat viene forzata a `report`
indipendentemente dalla politica a meno che `AA_DISCORD_AUDIT_BEAT_ALLOW_DESTRUCTIVE`
non sia impostato — rilasciare il blocco one-shot della prima esecuzione per
un'esecuzione CLI manuale non arma il beat. Un avviso all'avvio viene attivato
quando la politica è distruttiva ma il beat non è stato attivato.
- **Attribuito.** Ogni esecuzione beat scrive un `AuditInvocation` accettato
di sistema-attore (`triggered_by=BEAT`), così la traccia di audit-the-auditor
copre anche le esecuzioni senza supervisione.
- **Auto-riparazione.** Un'esecuzione bloccata da un soft-time-limit o da un
crash del worker viene riconciliata allo stato `INTERRUPTED` riprendibile al
prossimo tick. `AA_DISCORD_AUDIT_BEAT_MIN_INTERVAL_MINUTES` è un limite
minimo approssimativo contro una pianificazione veloce configurata male.
## Attività Celery
Il pacchetto registra cinque attività sotto il prefisso `aa_discord_audit.*`.
Solo le attività beat necessitano di una pianificazione; le altre sono guidate
da eventi o eseguite su richiesta. Nessuna è pianificata per te.
| Attività | Come viene eseguita | Cosa fa |
|----------|---------------------|---------|
| `aa_discord_audit.audit_orphan_members` | Celery beat — tu la pianifichi | L'audit di build + apply senza supervisione descritto sopra. Solo report se non armato; condivide il lock `discord.user_actions.<uid>` con `update_groups` di AA. |
| `aa_discord_audit.retry_pending_kicks` | Celery beat — tu la pianifichi | Sweep limitato che reinvia i kick differiti da un guasto transitorio di Discord, rispettando un cooldown per riga in modo che un membro con fallimento grave non venga martellato. |
| `aa_discord_audit.process_pending_run` | Guidata da eventi — accodata da un avvio web | Preleva l'esecuzione `PENDING` creata da un avvio web, la trasforma in `RUNNING` e guida la pipeline contro il flag di conferma congelato dell'esecuzione. |
| `aa_discord_audit.prune_audit_runs` | Celery beat / cron — tu la pianifichi (anche un comando di gestione) | Retention: scadenza della chiave di idempotenza poi eliminazione delle righe (vedi **Comandi di gestione** e il runbook). |
| `aa_discord_audit.sample_guild_presence` | Celery beat — tu la pianifichi | Campionamento leggero, solo REST, dei conteggi di membri della gilda / online / boost nell'istantanea di presenza più recente per i gauge di presenza di Prometheus. Gated su `AA_DISCORD_AUDIT_PRESENCE_ENABLED`; un skip-guard limita una pianificazione veloce configurata male (floor `AA_DISCORD_AUDIT_PRESENCE_SAMPLE_INTERVAL_MINUTES`, default 10). Inerte finché sia l'extra `[metrics]` che una voce beat non sono presenti (vedi [`docs/METRICS.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/METRICS.md)). |
Per pianificare le attività beat, aggiungile a `CELERYBEAT_SCHEDULE` nel tuo
`local.py`, per esempio:```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
}
Montato sotto la navigazione principale di Auth come Discord Audit. Viste di sola lettura; ogni elenco ha una casella di ricerca lato server che cerca in ogni riga — non solo nella pagina visualizzata — insieme ai suoi filtri a discesa:
ProtectedDiscordMember, blocco del primo avvio) ha determinato l'esito.Gli operatori con il permesso aa_discord_audit.run_audit vedono un pulsante Launch Audit sulla pagina Audit runs. Cliccandolo si apre un modal Bootstrap che mostra la modalità di policy configurata (solo report o distruttiva), lo stato di riconoscimento iniziale e un pulsante Confirm Launch. L'invio di POST a /run-launch/, che crea un AuditRun in stato PENDING, accoda process_pending_run tramite Celery e reindirizza alla pagina dei dettagli dell'esecuzione.
Il percorso web rispecchia i cancelli di sicurezza della CLI:
audit_acknowledge_initial non è stato eseguito, il modal mostra un blocco di rifiuto (nessun pulsante di invio) invece dell'azione Confirm Launch. Un POST che bypassa il modal (es. curl) viene rifiutato lato server, il rifiuto viene registrato in AuditInvocation e l'operatore viene reindirizzato con un messaggio flash.was_confirmation_bypassed = user.has_perm(run_audit_destructive) and policy_has_destructive(policy). L'interfaccia non ha toggle — il solo permesso distruttivo determina l'intento. Un operatore con solo run_audit che attiva un avvio contro una policy distruttiva ottiene un'esecuzione silenziosa di solo REPORT (la stessa coercizione che la CLI applica senza --yes).Mentre un'esecuzione è in uno stato non terminale (PENDING, RUNNING o INTERRUPTED), la pagina dei dettagli dell'esecuzione interroga /runs/<pk>/state.json ogni cinque secondi e aggiorna la scheda State sul posto. Il polling si interrompe sulle schede nascoste e si ferma non appena l'esecuzione raggiunge uno stato terminale.
Strumentazione Prometheus opzionale dietro l'extra [metrics] — in assenza, ogni chiamata metrica si risolve in uno stub no-op a costo quasi zero. Il modulo fornisce un livello di cooperazione senza dipendenza: quando django-prometheus è installato, i contatori e gli istogrammi dell'audit si registrano nel prometheus_client.REGISTRY predefinito e la vista /metrics di django-prometheus li esporta insieme alle proprie serie.```sh
pip install aa-discord-audit[metrics]
I gauges snapshot — presenza della gilda e aggregati di appartenenza per gruppo/ruolo — seguono un secondo percorso: risiedono in un registro dedicato esportato dall'endpoint `/audit/discord/metrics` del modulo stesso. Il collector multiprocesso di django-prometheus legge solo file mmap e salta i collector personalizzati, quindi questi gauges necessitano di un proprio target di scrape. Come qualsiasi target `/metrics`, non è autenticato — limitane l'accesso a livello di reverse-proxy o di rete. Entrambe le superfici restano inattive senza l'extra `[metrics]`.
Il catalogo delle metriche, il vocabolario delle etichette e le ricette Grafana si trovano in
[`docs/METRICS.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/METRICS.md)
(Traduzione russa:
[`docs/METRICS.ru.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/METRICS.ru.md)).
## Limitazioni
- **Singola gilda.** L'audit riconcilia l'unica gilda Discord con cui AA è configurato; non si estende a più gilde.
- **Solo ruoli gestiti da AA.** Le azioni Strip/Kick agiscono sui ruoli con nome AA e sull'appartenenza alla gilda; i ruoli che AA non gestisce non vengono mai toccati.
- **Nessun auto-rilevamento dei nickname.** I bot vengono riconosciuti solo tramite la tabella esplicita `BotAccountUid` / `AA_DISCORD_AUDIT_BOT_UIDS` — `AA_DISCORD_AUDIT_AUTO_DISCOVER_BY_NICKNAME` è riservato per v2 e non implementato.
- **Superficie alpha.** I nomi delle impostazioni e l'API pubblica potrebbero cambiare prima della versione `1.0`.
## Documentazione
- [`docs/runbook.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/runbook.md) — runbook operativo: permessi del bot Discord, checklist pre-volo, rilascio del lock di prima esecuzione, playbook per incidenti, interruttori diagnostici.
- [`docs/performance.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/performance.md) — numeri di riferimento di `audit_benchmark` e implicazioni di dimensionamento.
- [`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) — catalogo delle metriche Prometheus, vocabolario delle etichette, ricette Grafana.
- Codice sorgente: <https://gitlab.com/eveo7/aa-discord-audit>
- Tracker delle issue: <https://gitlab.com/eveo7/aa-discord-audit/-/issues>
- Changelog:
[`CHANGELOG.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/CHANGELOG.md)
## Sviluppo```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 è solo uv. La lunghezza della riga è 79 (Python) / 120 (Markdown).
MIT — vedi LICENSE.
| Codename | Gate |
|---|
aa_discord_audit.run_audit | comando di gestione, attività beat, esecuzione di eliminazione |
aa_discord_audit.run_audit_destructive | gate di avvio web per strip / strip_kick (separato da run_audit) |
aa_discord_audit.acknowledge_initial_audit | rilascia il blocco della prima esecuzione in modalità dry-run |
aa_discord_audit.manage_discord_identity | amministrazione DiscordIdentity |
aa_discord_audit.manage_role_exception | amministrazione ManagedRoleException |
aa_discord_audit.manage_protected_member | amministrazione ProtectedDiscordMember |
aa_discord_audit.manage_bot_account_uid | amministrazione BotAccountUid |
aa_discord_audit.manage_finding_override | amministrazione FindingActionOverride |
aa_discord_audit.view_auditrun (e simili) | audit-trail in sola lettura nel pannello di controllo Auth |