
조정 감사: Alliance Auth의 Discord 통합을 위한 것으로, Auth가 부여하지 않은 AA 관리 역할을 보유한 길드 멤버를 찾아 운영자 제어 정책 하에 제거하거나 추방합니다. 독립형 커뮤니티 Django 앱.
Alliance Auth의 Discord 통합을 위한 정합성 감사 도구입니다. 설정된 Discord 길드의 실제 역할 할당을 Alliance Auth에 표현된 상태(사용자별 그룹 + 상태)와 비교하고, 운영자가 AA가 알지 못하는 사용자에게 AA 이름의 역할을 수동으로 부여할 수 있는 격차를 해소합니다.
상태: 알파(
0.1.x). 공개 API와 설정은1.0이전에 여전히 변경될 수 있습니다.
감사는 기본적으로 안전합니다:
InitialAuditAcknowledgement(관리자 또는 셸 전용)가 필요합니다.report입니다 — 파괴적 작업은 옵트인 방식입니다.AuditRun, AuditFinding, AuditInvocation, ConfigChangeLog)은 매니저 및 인스턴스 계층에서 추가 전용이며, 대량 update() / bulk_update()는 차단됩니다.각 길드 멤버는 하나의 카테고리로 분류됩니다:
| 카테고리 | 의미 |
|---|---|
unknown_guest | AA가 전혀 알지 못하는 Discord 멤버 |
linked_no_perm | AA에 알려진 신원이지만 discord.access_discord가 없음 |
bot_filtered | 설정된 봇 계정 — 절대 조치되지 않음 |
운영자는 각 카테고리를 하나의 작업에 매핑합니다:
| 작업 | 동작 |
|---|---|
report | 발견 사항을 기록하고 Discord 측 변경 없음 |
strip | AA 관리 역할 제거 |
strip_kick | AA 관리 역할 제거 후 길드에서 추방 |
매핑은 AA_DISCORD_AUDIT_POLICY 설정이며, 그룹별 및 상태별 재정의는 각 카테고리 내에 중첩됩니다.
allianceauth.services.modules.discord)이 설치 및 구성됨(봇 토큰 + 길드)pip install aa-discord-audit
Auth local.py에서:
# `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",
]
그런 다음 마이그레이션을 실행합니다:
python manage.py migrate aa_discord_audit
CurrentUserMiddleware는 필수입니다 — 이것이 없으면 apps.ready()가
ImproperlyConfigured를 발생시킵니다. 이 미들웨어가 있어야
ConfigChangeLog 시그널 핸들러가 관리자 편집을 <system> 대신
실제 사용자에게 귀속시킬 수 있습니다.
감사를 실행하는 운영자 역할에 aa_discord_audit.run_audit 권한을 부여합니다.
드라이런 감사를 실행합니다:
python manage.py audit_discord_roles --action report
Auth 대시보드의 Discord Audit → Audit runs에서 결과를 검토합니다.
최초 실행 잠금을 해제합니다. 관리자를 통해 InitialAuditAcknowledgement 행을 생성하거나 python manage.py audit_acknowledge_initial을 실행합니다. 두 방법 모두 aa_discord_audit.run_audit 및 aa_discord_audit.acknowledge_initial_audit 권한이 필요합니다.
준비가 되면 원하는 파괴적 작업으로 다시 실행합니다.
| 코드네임 | 제어 대상 |
|---|---|
aa_discord_audit.run_audit | 관리 명령, beat 작업, 실행 삭제 |
aa_discord_audit.run_audit_destructive | strip / strip_kick의 웹 실행 게이트 (run_audit과 별도) |
aa_discord_audit.acknowledge_initial_audit | 최초 실행 드라이런 잠금 해제 |
aa_discord_audit.manage_discord_identity | DiscordIdentity 관리자 |
aa_discord_audit.manage_role_exception | ManagedRoleException 관리자 |
aa_discord_audit.manage_protected_member | ProtectedDiscordMember 관리자 |
aa_discord_audit.manage_bot_account_uid | BotAccountUid 관리자 |
aa_discord_audit.manage_finding_override | FindingActionOverride 관리자 |
aa_discord_audit.view_auditrun (및 관련 권한) | Auth 대시보드의 읽기 전용 감사 추적 |
manage_* 코드네임은 영향 범위별로 분리되어 있어, manage_bot_account_uid 권한만 가진 주니어가 ManagedRoleException을 편집하여 감사를 무력화할 수 없습니다.
모든 설정은 선택 사항입니다. 기본값은 안전합니다.
# Action policy. Bare-string form below is shorthand for
# {"default": "<action>"}; use the nested form for per-group / per-state
# overrides keyed by AA group name and state name.
AA_DISCORD_AUDIT_POLICY = {
"unknown_guest": "report",
"linked_no_perm": "report",
# "strip" is rejected at startup for linked_no_perm (it conflicts with
# AA's update_groups task); use "strip_kick" instead.
# "linked_no_perm": {
# "default": "strip_kick",
# "by_state": {"Guest": "report"},
# "by_group": {"Directors": "report"},
# },
}
# AA-notify fan-out to permission holders.
AA_DISCORD_AUDIT_NOTIFY_ADMINS = True
# Discord webhook for run summaries. Treat as a credential.
AA_DISCORD_AUDIT_WEBHOOK_URL = None
# uids skipped as bot accounts (in addition to the BotAccountUid admin
# table).
AA_DISCORD_AUDIT_BOT_UIDS = []
# Auto-discover bot accounts by Discord nickname heuristics. Reserved
# for v2; not implemented in the MVP. The startup validator raises
# ImproperlyConfigured if set to True — keep this False and use the
# explicit BotAccountUid admin table instead.
AA_DISCORD_AUDIT_AUTO_DISCOVER_BY_NICKNAME = False
# Retention. 0 disables pruning; the validator refuses 0 unless the
# acknowledged flag below is also set.
AA_DISCORD_AUDIT_RUN_RETENTION_DAYS = 180
AA_DISCORD_AUDIT_RETENTION_OPT_OUT_ACKNOWLEDGED = False
# Idempotency-key TTL. When positive, prune_audit_runs releases
# AuditRun.idempotency_key on rows older than the cutoff while the
# row itself stays for RUN_RETENTION_DAYS. 0 (default) disables the
# expiry; the key dies with the row.
AA_DISCORD_AUDIT_IDEMPOTENCY_KEY_TTL_DAYS = 0
# Per-run deadline. The Celery task's soft_time_limit follows.
AA_DISCORD_AUDIT_RUN_DEADLINE_MINUTES = 60
# Rolling 24h rate limit on accepted audit triggers per user.
# DISABLED is an opt-out gate; refuses to take effect unless
# explicitly toggled.
AA_DISCORD_AUDIT_RUN_RATE_LIMIT_PER_DAY = 5
AA_DISCORD_AUDIT_RUN_RATE_LIMIT_DISABLED = False
# Discord webhook delivery tuning.
AA_DISCORD_AUDIT_WEBHOOK_TIMEOUT = 10
AA_DISCORD_AUDIT_WEBHOOK_MAX_RETRIES = 3
# Opt-in: bulk PATCH role strip. Faster on large guilds; off by default
# while we collect operator feedback on Discord-side rate-limit shape.
AA_DISCORD_AUDIT_USE_BULK_ROLE_STRIP = False
# Periodic (Celery beat) audit. Destructive actions on the unattended
# beat require this explicit opt-in, independent of the one-shot
# first-run lock: a beat run carrying strip/kick is coerced to report
# otherwise, with a startup warning on mismatch.
AA_DISCORD_AUDIT_BEAT_ALLOW_DESTRUCTIVE = False
# Coarse global floor between beat runs, in minutes. 0 (default)
# disables it — the Celery beat schedule is the primary cadence
# control. A backstop against a misconfigured fast schedule
# (QueueOnce only dedupes concurrent ticks, not back-to-back ones).
AA_DISCORD_AUDIT_BEAT_MIN_INTERVAL_MINUTES = 0
# Pin the CLI acting user to a Django username (the "service principal"
# pattern). The management command has no HTTP request, so the actor is
# normally derived from the OS login; set this for clean, stable
# ConfigChangeLog attribution from cron and manual CLI alike. Resolved
# and permission-checked per invocation.
AA_DISCORD_AUDIT_CLI_ACTOR = None