
تدقيق المصالحة لتكامل Alliance Auth مع Discord: يجد أعضاء الخادم الذين يحملون أدوارًا مُدارة بواسطة AA لم يمنحها Auth، ويقوم بإزالتها أو طردهم وفق سياسة يتحكم بها المشغل. تطبيق Django مستقل للمجتمع.
تدقيق تسوية (Reconciliation audit) لتكامل Alliance Auth مع Discord. يقارن تعيينات الأدوار الفعلية في سيرفر (guild) Discord المُهيأ بحسب الحالة المعبر عنها في Alliance Auth (المجموعات + الحالة لكل مستخدم) ويُسد الفجوة التي يمكن من خلالها للمشرفين تعيين أدوار بأسماء مألوفة لـ AA لمستخدمين لا تعرفهم AA عنهم.
الحالة: ألفا (
0.1.x). قد تتغير واجهة API العامة والإعدادات قبل الإصدار1.0.
التدقيق آمن بشكل افتراضي:
InitialAuditAcknowledgement
(إداري أو عبر شل فقط).report لكل فئة — الإجراءات المدمرة تتطلب
اشتراكًا صريحًا (opt-in).AuditRun, AuditFinding, AuditInvocation,
ConfigChangeLog) هو فقط للإضافة (append-only) على طبقة المدير
والكائنات؛ يتم منع عمليات update() / bulk_update() المجمّعة.يُصنف كل عضو في السيرفر إلى فئة واحدة:
| الفئة | المعنى |
|---|---|
unknown_guest | عضو Discrd لا تعرف AA عنه شيئًا |
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`:```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",
]
ثم قم بتشغيل migrations:```sh python manage.py migrate aa_discord_audit
الـ `CurrentUserMiddleware` إلزامي — يرفع `apps.ready()` خطأ `ImproperlyConfigured` إذا كان مفقودًا. هذا ما يسمح لمعالج الإشارة `ConfigChangeLog` بنسب تعديلات المشرف إلى مستخدم حقيقي بدلاً من `<system>`.
## بداية سريعة
1. منح إذن `aa_discord_audit.run_audit` لدور المشغل الذي يقوم بتشغيل عمليات التدقيق.
2. قم بتشغيل تدقيق تجريبي (dry-run): ```sh
python manage.py audit_discord_roles --action report
InitialAuditAcknowledgement عبر المسؤول، أو تشغيل
python manage.py audit_acknowledge_initial. كلاهما يتطلب
aa_discord_audit.run_audit و
aa_discord_audit.acknowledge_initial_audit.الأسماء الرمزية manage_* مقسمة حسب نصف قطر الانفجار بحيث لا يمكن لمبتدئ لديه
manage_bot_account_uid تعطيل التدقيق عن طريق تحرير
ManagedRoleException.
جميع الإعدادات اختيارية. الإعدادات الافتراضية آمنة.```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
## أوامر الإدارة
| الأمر | الغرض |
|--------------------------|------------------------------------------------------------------|
| `audit_discord_roles` | نقطة الدخول الرئيسية. `--action {report,strip,strip_kick}`. |
| `audit_discord_roles --resume <run_id>` | إعادة مراجعة نتائج PENDING لتشغيل موجود. |
| `audit_discord_roles --abandon <run_id>` | وضع تشغيل عالق كـ ABANDONED. |
| `audit_discord_roles --diff <run_id>` | مقارنة الحالة الحالية بتشغيل سابق. |
| `audit_discord_roles --explain <member_id>` | تصنيف لكل عضو (للقراءة فقط). |
| `audit_discord_roles --policy-preview <json>` | عرض سياسة افتراضية. |
| `audit_discord_roles --from-fixture <path>` | إعادة التشغيل مقابل لقطة JSON. |
| `audit_acknowledge_initial` | إلغاء قفل التشغيل الجاف الأول من وحدة التحكم. |
| `audit_benchmark` | قياس أداء تحميل اصطناعي (راجع [`docs/performance.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/performance.md)). |
| `prune_audit_runs` | تقليم الاحتفاظ. |
| `audit_abandon_stuck_runs` | تسوية تشغيلات PENDING العالقة التي تسربت من عامل معطل – تحويلها إلى ABANDONED (راجع دليل التشغيل؛ للتشغيل RUNNING العالق استخدم `audit_discord_roles --abandon`). |
يتفاعل `audit_discord_roles` عند التشغيل الطويل مع `SIGTERM` و`SIGINT` بشكل نظيف: يتم تحويل التشغيل إلى `INTERRUPTED` وتخرج حلقة التطبيق عند الحدود التالية للنتائج بحيث يكون العمل الجزئي ثابتًا في مسار التدقيق. `audit_discord_roles --resume <run_id>` يستأنف التشغيل من النتائج المتبقية `PENDING`.
## التدقيق الدوري (Celery beat)
`audit_orphan_members` يشغل نفس مسار البناء + التطبيق وفقًا لجدول زمني تقوم بتوصيله بـ Celery beat (ليس مجدولًا افتراضيًا). المسار غير المراقب مقيد للأمان:
- **تقرير فقط ما لم يتم تسليحه.** يتم إجبار تشغيل beat على `report` بغض النظر عن السياسة ما لم يتم تعيين `AA_DISCORD_AUDIT_BEAT_ALLOW_DESTRUCTIVE` — إلغاء قفل التشغيل الجاف الأول لتشغيل CLI يدوي لا يسّلح الـ beat. يتم إطلاق تحذير عند بدء التشغيل عندما تكون السياسة مدمرة ولكن الـ beat لم يوافق.
- **منسوب.** كل تشغيل beat يكتب `AuditInvocation` مقبول من فاعل النظام (`triggered_by=BEAT`)، بحيث يغطي مسار تدقيق المدقق التشغيل غير المراقب أيضًا.
- **الشفاء الذاتي.** يتم تسوية تشغيل عالق بسبب ضربة حد زمني ناعم أو تعطل عامل إلى الحالة القابلة للاستئناف `INTERRUPTED` في الدورة التالية. `AA_DISCORD_AUDIT_BEAT_MIN_INTERVAL_MINUTES` هو حد أدنى تقريبي ضد جدول سريع تم تكوينه بشكل خاطئ.
## مهام Celery
يسجل الحزمة خمس مهام تحت بادئة الاسم `aa_discord_audit.*`. فقط مهام beat تحتاج إلى جدول زمني؛ الباقي يعمل بأحداث أو عند الطلب. لا شيء مجدول لك.
| المهمة | كيفية تشغيلها | ما تقوم به |
|------|-------------|--------------|
| `aa_discord_audit.audit_orphan_members` | Celery beat — تقوم بجدولتها | تدقيق البناء + التطبيق غير المراقب الموصوف أعلاه. تقرير فقط ما لم يتم تسليحه؛ يشارك قفل `discord.user_actions.<uid>` مع `update_groups` من AA. |
| `aa_discord_audit.retry_pending_kicks` | Celery beat — تقوم بجدولتها | مسح محدود يعيد إرسال الطرد المؤجل بسبب فشل Discord مؤقت، مع احترام فترة تهدئة لكل صف حتى لا يتعرض العضو الفاشل بشدة. |
| `aa_discord_audit.process_pending_run` | مدفوع بالأحداث — يتم وضعه في قائمة الانتظار بواسطة إطلاق ويب | يلتقط تشغيل `PENDING` الذي أنشأه إطلاق ويب، يحوله إلى `RUNNING`، ويدفع المسار مقابل علم التأكيد المجمد للتشغيل. |
| `aa_discord_audit.prune_audit_runs` | Celery beat / cron — تقوم بجدولتها (أيضًا أمر إدارة) | الاحتفاظ: انتهاء صلاحية مفتاح عدم التكرار ثم حذف الصف (راجع **أوامر الإدارة** ودليل التشغيل). |
| `aa_discord_audit.sample_guild_presence` | Celery beat — تقوم بجدولتها | عينة خفيفة، REST فقط، من أعضاء السيرفر / المتصلين / عدد المعززين في لقطة وجود أحدث فقط لمقاييس وجود Prometheus. مقيدة بـ `AA_DISCORD_AUDIT_PRESENCE_ENABLED`; حارس تخطي يحد من جدول سريع تم تكوينه بشكل خاطئ (حد أدنى `AA_DISCORD_AUDIT_PRESENCE_SAMPLE_INTERVAL_MINUTES`، افتراضي 10). خامل حتى يتم تقديم كل من إضافة `[metrics]` وإدخال beat (راجع [`docs/METRICS.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/METRICS.md)). |
لجدولة مهام beat، أضفها إلى `CELERYBEAT_SCHEDULE` في ملف `local.py` الخاص بك، على سبيل المثال:```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
}
مثبتة تحت التنقل الرئيسي للمصادقة باسم Discord Audit. وجهات عرض للقراءة فقط؛ كل قائمة تحمل مربع بحث من جانب الخادم يطابق عبر كل صف - وليس فقط الصفحة المعروضة - إلى جانب فلاتر القائمة المنسدلة الخاصة بها:
ProtectedDiscordMember، قفل التشغيل الأول) حددت النتيجة.يرى المشغلون الذين لديهم إذن aa_discord_audit.run_audit زر إطلاق التدقيق في صفحة عمليات التدقيق. يؤدي النقر عليه إلى فتح نافذة منبثقة Bootstrap تعرض وضع السياسة المُكوّن (تقرير فقط أو تدميري)، وحالة الإقرار الأولي، وزر تأكيد الإطلاق. إرسال POSTs إلى /run-launch/، والذي ينشئ AuditRun بحالة PENDING، ويُدرج process_pending_run عبر Celery، ويعيد التوجيه إلى صفحة تفاصيل التشغيل.
يتبع مسار الويب نفس بوابات الأمان لواجهة CLI:
audit_acknowledge_initial، تعرض النافذة المنبثقة كتلة رفض (بدون زر إرسال) بدلاً من إجراء تأكيد الإطلاق. يتم رفض POST الذي يتجاوز النافذة المنبثقة (مثل curl) من جانب الخادم، ويتم تسجيل الرفض في AuditInvocation، ويتم إعادة توجيه المشغل مع رسالة وميض.was_confirmation_bypassed = user.has_perm(run_audit_destructive) and policy_has_destructive(policy). لا تحتوي واجهة المستخدم على مفتاح تبديل — إذن التدمير وحده يحدد النية. المشغل الذي لديه إذن run_audit فقط ويطلق عملية تدقيق ضد سياسة تدميرية يحصل على تشغيل بصيغة REPORT-only صامت (نفس الإكراه الذي تطبقه CLI بدون --yes).أثناء وجود تشغيل في حالة غير نهائية (PENDING أو RUNNING أو INTERRUPTED)، تصوّت صفحة تفاصيل التشغيل على /runs/<pk>/state.json كل خمس ثوانٍ وتقوم بتحديث بطاقة الحالة في مكانها. يتوقف التصويت في علامات التبويب المخفية ويتوقف بمجرد وصول التشغيل إلى حالة نهائية.
أجهزة Prometheus الاختيارية خلف الامتداد [metrics] — في حال عدم وجودها، يتحول كل استدعاء مقياس إلى كعب لا يعمل بتكلفة شبه معدومة. تأتي الوحدة مع طبقة تعاون لا تعتمد: عند تثبيت django-prometheus، يتم تسجيل عدادات ورسوم بيانية للتدقيق في prometheus_client.REGISTRY الافتراضي، وتقوم طريقة عرض /metrics الخاصة بـ django-prometheus بتصديرها جنبًا إلى جنب مع سلاسلها الخاصة.```sh
pip install aa-discord-audit[metrics]
مقاييس اللقطة — وجود الخادم (guild) وتجميعات العضوية لكل مجموعة/دور — تأخذ مسارًا ثانيًا: فهي موجودة في سجل مخصص يتم تصديره بواسطة نقطة النهاية `/audit/discord/metrics` الخاصة بالوحدة. جامع العمليات المتعددة لـ django-prometheus يقرأ فقط ملفات mmap ويتجاوز المجمعات المخصصة، لذا تحتاج هذه المقاييس إلى هدف سحب (scrape) خاص بها. مثل أي هدف `/metrics`، فهو غير موثّق — قيّده عند الوكيل العكسي أو طبقة الشبكة. يظل كلا السطحين خاملين بدون إضافة `[metrics]`.
دليل المقاييس، ومفردات التصنيفات، ووصفات Grafana موجودة في [`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)).
## القيود
- **خادم واحد.** التدقيق يوفق بين خادم Discord الواحد الذي تم تكوين AA لاستخدامه؛ لا يمتد عبر خوادم متعددة.
- **أدوار مدارة من AA فقط.** إزالة/طرد تعمل على الأدوار المسماة من قبل AA وعضوية الخادم؛ الأدوار التي لا يديرها AA لا يتم لمسها أبدًا.
- **لا اكتشاف تلقائي للكُنًى.** يتم التعرف على حسابات البوت فقط عبر جدول `BotAccountUid` الصريح / `AA_DISCORD_AUDIT_BOT_UIDS` — `AA_DISCORD_AUDIT_AUTO_DISCOVER_BY_NICKNAME` محجوز للإصدار v2 وغير مطبق.
- **سطح ألفا.** قد تتغير أسماء الإعدادات وواجهة برمجة التطبيقات العامة قبل `1.0`.
## التوثيق
- [`docs/runbook.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/runbook.md) — دليل المشغل: أذونات بوت Discord، قائمة الفحص قبل التشغيل، تحرير قفل التشغيل الأول، دفاتر تشغيل الحوادث، مفاتيح التشخيص.
- [`docs/performance.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/docs/performance.md) — أرقام مرجعية `audit_benchmark` وتأثيرات الحجم.
- [`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، مفردات التصنيفات، وصفات Grafana.
- الكود المصدري: <https://gitlab.com/eveo7/aa-discord-audit>
- متتبع المشكلات: <https://gitlab.com/eveo7/aa-discord-audit/-/issues>
- سجل التغييرات: [`CHANGELOG.md`](https://gitlab.com/eveo7/aa-discord-audit/-/blob/main/CHANGELOG.md)
## التطوير```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
سلسلة الأدوات هي uv-only. طول السطر هو 79 (Python) / 120 (Markdown).
MIT — انظر LICENSE.
| الاسم الرمزي | البوابات |
|---|
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 |