الدليل

المصادقة

كل طلب يحمل بيانات اعتماد واحدة. وصلاحية الرمز هي صلاحيات صاحبه في هذه اللحظة متقاطعةً مع نطاق الرمز نفسه، تُحسب مع كل نداء.

كل نداء لواجهة X-Radius يحمل بيانات اعتماد واحدة في ترويسة Authorization. لا مصافحة ولا كعكة جلسة، ولا يقرأ شيء في الواجهة محدِّد المستأجر من جسم الطلب أو من سلسلة الاستعلام — المستأجر يأتي من المضيف الذي تناديه ومن بيانات الاعتماد نفسها.

بيانات اعتماد من نوعين، وترويسة واحدة

تُقبل بيانات اعتماد من نوعين، ومعظم نقاط النهاية تقبل أيًّا منهما:

  • رمز جلسة مدير، يعيده POST /api/v1/auth/login. يخصّ شخصًا، وينتهي بعد مدة الجلسة التي ضبطها المستأجر، ويمكن إبطاله من الحساب ← الجلسات.
  • رمز واجهة برمجية، يُنشأ من المطوّر ← رموز الواجهة البرمجية. يبدأ بـ xrt_، ويخصّ سكربتًا، ويبقى صالحًا حتى يُبطَل أو ينتهي.
curl -s https://acme.example.com/api/v1/users \
  -H "Authorization: Bearer $XRADIUS_TOKEN"

ويُفرَّق بينهما بالشكل لا بالمسار: رمز الواجهة هو البادئة xrt_ حرفيًا يتبعها 43 محرفًا من أبجدية base64url بالضبط. أما رمز الجلسة فمفصول بالنقاط ولا يطابق هذا الشكل أبدًا، وأي ترويسة Authorization عشوائية تُرفض على هذا الفحص الشكلي قبل أن تكلّف نداءً لقاعدة البيانات. ولا يعرف شيء بعد هذا الفحص أيَّ النوعين استُعمل، فلا تكون نقطة نهاية "للجلسات فقط" بالصدفة — والواجهات التي ترفض الرموز ترفضها صراحةً، وهي مذكورة أدناه.

العنوان الأساسي

https://<اسم-مستأجرك>.<نطاق-النسخة>/api/v1

استخدم المضيف الذي تراه في المتصفح وأنت داخل على اللوحة. فنداء عنوان النسخة نفسه لا يحدّد مستأجرًا، فيعود كل طلب فارغًا أو غير مصرَّح، ويبدو ذلك تمامًا كبيانات اعتماد خاطئة. فإن فشل نداؤك الأول فتحقق من المضيف قبل أي شيء آخر. وانظر رموز الواجهة البرمجية لكيفية الحصول على بيانات الاعتماد نفسها وتدويرها.

التأكد من أن بيانات الاعتماد تعمل

GET /api/v1/auth/me هو أرخص نداء أول صحيح. فهو يتطلّب مصادقة لكنه غير مقيَّد بصلاحية، فتصله أي بيانات اعتماد عاملة، ويخبرك بالضبط بما تستطيع فعله:

{
  "data": {
    "version": "0.2.34",
    "manager": {
      "id": 41,
      "tenant_id": 12,
      "email": "ops@acme.example",
      "username": "ops",
      "status": "active",
      "two_factor_enabled": true
    },
    "roles": ["support"],
    "permissions": ["prm_users_index", "prm_users_update"],
    "is_admin": false,
    "settings": {
      "currency": "EGP",
      "timezone": "Africa/Cairo",
      "default_language": "en"
    }
  }
}

فإن نجح هذا النداء فالباقي مسألة اختيار نقاط النهاية. وإن كانت permissions أقصر مما توقعت فاقرأ القسم التالي قبل أن تشكّ في نقطة النهاية.

الصلاحية تقاطعٌ يُحسب مع كل طلب

صلاحية رمز الواجهة البرمجية الفعلية هي:

(صلاحيات صاحبه في هذه اللحظة)  ∩  (قائمة سماح الرمز)

ولا أحد الطرفين لقطة محفوظة. قائمة السماح مخزَّنة مع الرمز، وطرف الصاحب يُقرأ من قاعدة البيانات، أما التقاطع نفسه فيُحسب مع كل طلب على حدة ولا يُخزَّن في أي ذاكرة وسيطة. وهو يتجسَّد في موضع واحد — authz.Resolver.Resolve، الباب الوحيد الذي يمرّ منه كل سؤال عن الصلاحية في الشيفرة — فلا يستطيع معالِج أن ينسى تطبيقه.

وتتبع ذلك ثلاث نتائج، وكلها تفاجئ القارئ مرةً على الأقل:

  • لا يستطيع الرمز أن يفعل أكثر من صاحبه. فتأشير صلاحية لا يحملها الصاحب لا أثر له. النطاق سقف لا منحة.
  • الرمز ينكمش مع صاحبه، بصمت. فانزع صلاحيةً من دور الصاحب تنزعها من كل رمز يحمله ذلك الشخص، بلا إشعار، وبلا قيد تدقيق على الرمز، وبلا أي تغيّر ظاهر في قائمة نطاق الرمز نفسها. فالنطاق المخزَّن ما زال يعرض الصلاحية، والمجموعة الفعلية لم تعد تحتويها. والتكامل الذي انكسر ليلًا بـ 403 بلا أي نشر من جهتك سببه هذا عادةً.
  • الرمز المقيَّد مقيَّد حتى لمدير عام. فالرمز المحدود بـ"عرض المشتركين" ليس رمز إدارة، أيًّا كان من أنشأه.

وتعديل الأدوار يُسقط المجموعة المخزَّنة مؤقتًا لصاحبها فورًا في العملية التي نفّذت التعديل؛ وفي غيرها تُقرأ من جديد خلال نافذة ذاكرة المحلِّل الوسيطة، وهي 30 ثانية افتراضيًا. ولا شيء يخزّن المجموعة المضيَّقة، إذ إن تخزينها يسرّب نطاق رمزٍ واحد إلى الطلب التالي لصاحبه وهو داخل على اللوحة.

لماذا يعلن الرمز المقيَّد أن is_admin تساوي false

حين يكون هناك نطاق، يعيد المحلِّل مجموعة صلاحيات رموزها هي التقاطع الحقيقي وراية IsAdmin فيها مضبوطة قسرًا على false، حتى حين يكون الصاحب مدير المستأجر العام. وتُنزع أسماء الأدوار المحجوزة (tenant_admin وsys_admin) من مطالبات الطلب للسبب نفسه.

وهذا ليس احتياطًا زائدًا، بل هو الشكل الصحيح الوحيد. فـIsAdmin حقل مُصدَّر، وقرابة عشرين موضعًا تقرؤه مباشرةً لا عبر Can() — نطاقا إدارة المديرين والمشتركين، ومعالج التهيئة، والأدوار، ونقطة البيع، وشبكة NAS الخاصة، وتحديد نطاق قوالب الكروت، وصفحة لوحة القيادة، وإدارة التذاكر، واستعلامات قوالب الكروت، وخدمة مديري بوت تيليجرام. فقناعٌ يُطبَّق داخل Can() وحدها يترك كلّ واحد من هذه المواضع يرى مديرًا عامًا، فيحمل رمز "قراءة فقط" على حساب مدير المستأجر صلاحية إدارة كاملة بهدوء. وذلك ضمانٌ كاذب، وهو أسوأ من غياب الضمان أصلًا.

وما تراه أنت بصفتك المنادي: GET /auth/me على رمز مقيَّد يعيد "is_admin": false ومصفوفة permissions التي هي التقاطع — لا فهرس صلاحيات الصاحب. فاضبط برنامجك على تلك المصفوفة، لا على اسم دور الصاحب.

النطاق الفارغ لا يسمح بشيء

نطاق الرمز المقيَّد قائمة سماح، والقائمة الفارغة تعني لا يجوز له شيء. ولا تعني أبدًا "بلا قيود". فالرمز الذي أُنشئ بلا تأشير أي صلاحية يُصادَق عليه صحيحًا ثم ترفضه كل نقطة نهاية مقيَّدة بصلاحية.

والصلاحية الكاملة راية منفصلة على الرمز، لا قائمة فارغة. والقطبية مقصودة: فوضع العُرف المعاكس هو رمز يتوسّع، ووضع هذا العُرف هو رمز يتوقّف عن العمل.

ما يرفض بيانات اعتماد الآلات

بعض الواجهات ترفض رمز الواجهة البرمجية رفضًا قاطعًا، وتردّ 403 ERR_FORBIDDEN مع مفتاح الرسالة api_token_forbidden:

  • بيانات اعتماد الحساب — كلمة المرور والتحقق بخطوتين والجلسات. فالرمز الذي يستطيع تغيير كلمة مرور صاحبه أداةُ استيلاء على الحساب لكل من قرأه يومًا من ملف إعداد.
  • مسارات رموز الواجهة نفسها. وإلا استطاع الرمز أن يُصدر رموزًا ويدوّرها ويُظهرها فيوسّع نفسه خارج نطاقه في نداء واحد.
  • الدخول باسم مدير آخر (POST /managers/{id}/login-as)، إذ يتيح للرمز القفز إلى حساب أقوى.

أما الدخول باسم مشترك (POST /users/{id}/login-as) فمسموح، لأنه يسير في الاتجاه الآخر: الجلسة المستعارة تستطيع أقل مما يستطيعه المدير الذي أذن بها.

وبمعزل عن ذلك، ترفض حفنة من المسارات المنادي المنتحِل — أي العامل عبر الدخول باسم غيره — ومنها إعادة تعيين كلمات المرور وإسناد الأدوار واعتماد التعويضات. وهذه ترفض الجلسة المستعارة أيًّا كان نوع بيانات الاعتماد التي أُصدرت منها.

حين يفشل النداء

الـ 401 يعني دائمًا بيانات الاعتماد نفسها: مفقودة أو مشوَّهة أو غير صالحة تعميةً أو لم تعد سارية. ورمز الغلاف هو ERR_UNAUTHORIZED الخشن في كل هذه الحالات، فالمميِّز هو error.details.reason:

details.reason ما الذي حدث
missing_bearer_token لا ترويسة Authorization: Bearer أصلًا
invalid_token التوقيع أو الانتهاء أو الموضوع خاطئ؛ وكذلك رمز بوابة المشترك أُرسل لواجهة الإدارة
session_revoked أُنهيت الجلسة من الحساب ← الجلسات
api_token_invalid لا رمز بهذا الشكل، أو أن مستأجره لم يعد قابلًا للخدمة
api_token_revoked أُبطل نهائيًا — أنشئ رمزًا جديدًا
api_token_expired تجاوز تاريخ انتهائه
api_token_disabled مُطفأ، والإطفاء قابل للرجوع
api_token_owner_inactive المدير صاحب الرمز موقوف أو محذوف

والرمز المجهول والتخمين حسن الشكل يُجابان بالجواب نفسه، عن قصد.

أما الـ 403 فيعني أن بيانات الاعتماد سليمة وأن الصلاحية ليست كذلك: insufficient_permissions (وسّع نطاق الرمز، أو تحقق من أن صاحبه يحمل الصلاحية أصلًا)، أو api_token_forbidden (تلك النقطة ترفض بيانات اعتماد الآلات بحكم التصميم)، أو license_locked (المستأجر كله تجاوز سقف مشتركيه أو انتهى ترخيصه، فلا تجيب إلا صفحة الترخيص حتى يُحلّ ذلك).

وكل رد يحمل error.request_id. اذكره عند طلب المساعدة — فهو القيمة الوحيدة التي تجد الطلب بعينه في سجل الخادم.

آخر تحديث

اطرح سؤالًا

جرّبه على شبكتك.

50 مشترك لمدة 7 أيام، من غير ما تدفع حاجة.