دعوة API مع معرّف group_names تُرجع 403 لمالك المجموعة ما لم يكن مديرًا

ملخص

يمكن لمستخدم غير إداري يكون مالكًا لمجموعة في Discourse دعوة أعضاء جدد مع تعيين تلك المجموعة بنجاح من خلال مربع حوار الدعوة في واجهة الويب. الإجراء نفسه عبر واجهة برمجة التطبيقات REST (/invites.json، مفتاح API بنطاق عام، Api-Username صحيح) يعيد خطأ 403 invalid_access — لكن فقط عند تضمين group_names/group_ids في الحمولة. يحدث هذا بغض النظر عما إذا تم تمرير group_names كنص عادي (مثل "group_names": "my-group") أو كمصفوفة (مثل "group_names": ["my-group"]) — كلا الصيغتين ينتجان خطأ 403 متطابقًا.

من الناحية الحاسمة، منح المستخدم صفة المشرف لا يصلح استدعاء API — فقط منح صفة المدير الكامل يفعل ذلك، على الرغم من أن صفة المشرف كافية لكل سيناريو آخر تم اختباره (واجهة الويب، دعوة API غير مرتبطة بمجموعة).

البيئة

  • إصدار Discourse: [املأ — صفحة Admin → Upgrade]
  • نوع مفتاح API: نطاق عام، Api-Username = اسم المستخدم الدقيق للمستخدم التجريبي

خطوات إعادة الإنتاج

  1. أنشئ مستخدمًا غير إداري وغير مشرف (<userid>)، واجعله مالكًا للمجموعة <group_name>.
  2. من خلال واجهة الويب، بصفتك <userid>: دعوة بدون مجموعة → تنجح. دعوة مع تعيين <group_name> → تنجح.
  3. من خلال API، بصفتك <userid> (مفتاح بنطاق عام، Api-Username=<userid>):
    • POST /invites.json بدون group_names → تنجح.
    • POST /invites.json مع group_names="<group_name>" (صيغة نص) → 403 invalid_access.
    • POST /invites.json مع group_names=["<group_name>"] (صيغة مصفوفة) → 403 invalid_access (فشل متطابق، تم اختبار كلا الصيغتين).
  4. ارتقِ بـ <userid> إلى مشرف. كرر استدعاءات API المرتبطة بالمجموعة من الخطوة 3 (كلا الصيغتين) → لا يزال 403 invalid_access.
  5. ارتقِ بـ <userid> إلى مدير. كرر استدعاءات API المرتبطة بالمجموعة من الخطوة 3، نفس المفتاح، نفس الحمولات → تنجح.

السلوك المتوقع

بما أن ملكية المجموعة وحدها تمنح إذنًا كافيًا لمستخدم غير موظف لتعيين تلك المجموعة عند الدعوة من خلال واجهة الويب، يجب أن تحترم API — المصادق عليها كالمستخدم نفسه عبر Api-Username — نفس الإذن. على الأقل، يجب ألا تتصرف صفة المشرف (التي تعد موظفًا) بشكل متطابق مع مستخدم غير مخصص هنا؛ حقيقة أن المشرف يفشل بشكل متطابق مع عدم وجود دور، وأن المدير فقط ينجح، تشير إلى أن مسار كود API يتحقق من user.admin? بشكل محدد بدلاً من ملكية المجموعة أو حتى حالة الموظف العامة (user.staff?، والتي تكون صحيحة للمشرفين).

السلوك الفعلي

ترفض API أي دعوة مرتبطة بمجموعة (صيغة معامل نص أو مصفوفة) ما لم يكن المستخدم المنفذ مديرًا كاملًا. ملكية المجموعة (تم التحقق من عملها من خلال واجهة الويب) وحالة المشرف/الموظف (تم التحقق منها عبر اختبار الترقية المباشر) كلاهما غير كافٍ لمسار API، على الرغم من أن أيًا منهما كافٍ من خلال واجهة الويب أو لحالة API غير المرتبطة بالمجموعة.

سؤال لفريق Discourse

هل يتطلب مسار كود الدعوة المرتبطة بالمجموعة المصادق عليها عبر API صراحةً user.admin?، بدلاً من التحقق من ملكية المجموعة أو user.staff? بالطريقة التي يفعلها مسار واجهة الويب / المصادق عليه عبر الجلسة؟ إذا كان الأمر كذلك، هل هذا مقصود (قيود API محددة غير موثقة) أم إنه خطأ برمجي حيث يتباعد فحص حارس API عن فحص حارس الويب لهذا الإجراء؟

سعيد بتوفير سجلات طلب/استجابة كاملة (مع إخفاء المفتاح) عند الطلب.