ملخص
يمكن للمستخدم غير المسؤول الذي يمتلك صفة مالك لمجموعة في Discourse دعوة أعضاء جدد مع إسناد تلك المجموعة من خلال مربع حوار الدعوة في واجهة الويب. الإجراء نفسه عبر واجهة برمجة التطبيقات (REST API) (/invites.json، مفتاح API بنطاق عام، وApi-Username صحيح) يعيد خطأ 403 invalid_access — ولكن فقط عند تضمين group_names/group_ids في الحمولة. والأهم من ذلك، أن منح المستخدم صفة المشرف لا يحل مشكلة استدعاء الـ API — بل فقط منح صفة المسؤول الكامل يحلها، على الرغم من أن صفة المشرف كافية لكل سيناريو آخر تم اختباره (واجهة الويب، دعوة عبر الـ API بدون مجموعة).
البيئة
- إصدار Discourse: 2026.7.1
- نوع مفتاح API: نطاق عام، CheriArchives = اسم المستخدم الدقيق للمستخدم الاختباري
خطوات إعادة الإنتاج
- إنشاء مستخدم غير مسؤول وغير مشرف (
<userid>)، وجعله مالكًا للمجموعة<group_name>. - في واجهة الويب، بصفتك
<userid>: دعوة بدون مجموعة → تنجح. دعوة مع إسناد<group_name>→ تنجح. - عبر الـ API، بصفتك
<userid>(مفتاح بنطاق عام،Api-Username=<userid>):POST /invites.jsonبدونgroup_names→ تنجح.POST /invites.jsonمعgroup_names=<group_name>(نفس المجموعة التي يملكها) → 403invalid_access.
- ترقية
<userid>إلى مشرف. تكرار استدعاء الـ API مع المجموعة من الخطوة 3 → لا يزال 403invalid_access. - ترقية
<userid>إلى مسؤول. تكرار استدعاء الـ API مع المجموعة من الخطوة 3، بنفس المفتاح والحمولة → تنجح.
السلوك المتوقع
بما أن ملكية المجموعة وحدها هي إذن كافٍ للمستخدم غير الموظف لإسناد تلك المجموعة عند الدعوة عبر واجهة الويب، فإن الـ API — المصادق عليه بنفس المستخدم عبر Api-Username — يجب أن يحترم نفس الإذن. على الأقل، يجب ألا تتصرف صفة المشرف (وهي من الموظفين) بشكل مطابق للمستخدم غير المميز هنا؛ فالحقيقة أن المشرف يفشل بنفس طريقة المستخدم بلا صفة، وأن المسؤول فقط هو الذي ينجح، يوحي بأن مسار الكود في الـ API يتحقق من user.admin? على وجه التحديد بدلاً من ملكية المجموعة أو حتى حالة الموظف العامة (user.staff?، وهي صحيحة للمشرفين).
السلوك الفعلي
يرفض الـ API أي دعوة مع مجموعة برمز 403 ما لم يكن المستخدم المنفذ مسؤولًا كاملاً. ملكية المجموعة (تم التحقق من عملها عبر واجهة الويب) وحالة المشرف/الموظف (تم التحقق منها عبر اختبار الترقية المباشر) كلاهما غير كافٍ لمسار الـ API، على الرغم من أن أيًا منهما كافٍ عبر واجهة الويب أو لحالة الـ API بدون مجموعة.
سؤال لفريق Discourse
هل يتطلب مسار الكود للدعوة مع المجموعة المصادق عليها عبر الـ API صراحةً user.admin?، بدلاً من التحقق من ملكية المجموعة أو user.staff? كما يفعل مسار واجهة الويب / المصادقة عبر الجلسة؟ إذا كان الأمر كذلك، فهل هذا مقصود (قيود خاصة بالـ API غير موثقة) أم إنه خطأ برمجي حيث يتباعد فحص الحارس في الـ API عن فحص الحارس في الويب لهذا الإجراء؟
سعيد بتوفير سجلات الطلب/الاستجابة الكاملة (مع إخفاء المفتاح) عند الطلب.