بناء التطبيق

صلاحيات الأدوار داخل تطبيقك (rbac_permissions)

النطاقات تقول ما تسمح المنصّة لتطبيقك بقراءته. وهذه الكتلة تقول ما يسمح تطبيقك لموظّفي المدرسة بفعله داخله: مَن يرى صفحة الحضور، ومَن يعتمد الطلب، ومَن يصدّر التقرير. صلاحياتٌ تُنشأ في الجهة وتُمنح لأدوارها، ويحرسها كودك بـ can() — كأي صلاحية في المنصّة.

الشكل

{
  "moduleId": "okta-hdor",
  "rbac_permissions": {
    "tenant-admin": [
      "okta_hdor.attendance.view",
      "okta_hdor.attendance.update",
      "okta_hdor.reports.export"
    ],
    "teacher": [
      "okta_hdor.attendance.view",
      "okta_hdor.attendance.update"
    ]
  },
  "rbac_labels": {
    "feature": { "ar": "الحضور", "en": "Attendance" },
    "permissions": {
      "okta_hdor.attendance.view":   { "ar": "عرض الحضور",   "en": "View attendance" },
      "okta_hdor.attendance.update": { "ar": "تسجيل الحضور", "en": "Record attendance" },
      "okta_hdor.reports.export":    { "ar": "تصدير التقارير", "en": "Export reports" }
    }
  }
}
  • المفتاح اسم دور عامّ في المنصّة (tenant-admin, teacher, …) — دورٌ لا تعرفه المنصّة يُتجاوَز بصمت عند التثبيت، فلا تخترع أدواراً.
  • كل اسم <feature>.<resource>.<action>، والـ feature هو معرّف تطبيقك بصيغة snake_case: okta-hdor → okta_hdor. المنصّة تجمّع محرّر صلاحيات الجهة بالمقطع الأول وتُنشئ الاسم كما هو، فاسمٌ خارج مجموعتك (education.students.delete مثلاً) يقع داخل مجموعة المنصّة نفسها ويُظلّل مفرداتها — ولذلك يُرفَض بالاسم عند المزامنة ولا يصل النشر. أفعالٌ محدّدة (view, create, update, delete, export, approve) لا manage.
  • rbac_labels هي ما تراه المدرسة. المنصّة تركّب أسماء الصلاحيات من ملفات لغتها هي، وتعرض المفتاح الخام لما لا تعرفه — ومواردك بحكم التعريف مجهولة لديها. بلا هذه الكتلة يرى مدير المدرسة مجموعة اسمها okta_hdor وداخلها okta_hdor.attendance.view. عنوان للمجموعة وعنوان لكل صلاحية، بالعربية والإنجليزية (٨٠ حرفاً كحدّ)، وعنوانٌ لاسم غير مُعلَن يُسقَط.
  • حدّ الكتلة ١٠٠ صلاحية.

كيف تصل الإصدار

الكتلة تُقرأ من manifest.json على الفرع بزرّ «مزامنة من manifest» في محرّر الإصدار (أو أداة MCP sync_from_manifest)، وتظهر في بطاقة «صلاحيات الأدوار» قراءةً — مع كل بند رُفض وسببه. تستطيع أيضاً ضبطها مباشرةً بأداة set_rbac_permissions وقراءتها بـ get_rbac_permissions، لكن اجعل المستودع مصدر الحقيقة: الكود الذي ينادي can('okta_hdor.attendance.view') يعيش هناك، والاسمان يجب أن يتطابقا حرفاً بحرف.

ثم يمرّ الفاحص: validate_manifest يرفض الاسم خارج مجموعتك ويحذّرك من صلاحية بلا عنوان قبل أن تنشر.

متى تُنشأ في الجهة

عند التثبيت الأول وعند كل نشر — لا عند التثبيت وحده. حين تنشر إصداراً يعلن صلاحية جديدة، تُنشأ في كل جهة مثبِّتة وتُمنح لأدوارها المسمّاة عند وصول التحديث إليها؛ لا تنتظر المدرسة إعادة تثبيت.

ما تُعلنه لدور هو كل ما يحمله ذلك الدور منك

عند كل نشر تُسوّي المنصّة أدوارها العامّة مع بيانك: كل دور تُسمّيه في rbac_permissions يصير يحمل من صلاحياتك ما أعلنته له، ولا شيء غيره — فما أعلنه إصدار قديم لذلك الدور وتوقّفت عن إعلانه يُسحَب منه.

  • الدور الذي لا تذكره لا يُمَسّ. الصمت ليس «اسحب كل شيء»: تطبيقٌ لم يذكر tenant-admin قطّ لم يدّعِ عنه شيئاً، ولو فُهم الصمت سحباً لأخذ الشاشات من مَن يدير المدرسة.
  • ولإفراغ دور، سمِّه بقائمة فارغة: "administrator": []. هذه هي الجملة الوحيدة التي تقول «هذا الدور لا يحمل شيئاً من صلاحياتي»، وهي طريقك الوحيد لاسترجاع منحةٍ أعطاها إصدارٌ قديم.
  • مجموعات المدرسة لا تُمَسّ أبداً («محاسبة»، «شؤون الموظفين»)، ولا الصلاحية المُعطاة لشخص بعينه. تلك قرار المدرسة لا قرارك.
  • ولا شيء خارج مجموعتك: التسوية لا ترى إلا الأسماء التي تبدأ بـ <feature>. الخاص بك.

ولماذا هذا لازم: صفوف منح الأدوار العامّة بلا عمود جهة — الصفّ الواحد هو كل المدارس معاً. فتطبيقٌ أعلن ست عشرة صلاحية لـadministrator في إصدار قديم ثم توقّف عن إعلانها كان يترك كل إداري في كل مدرسة يحمل عرض كشوف الرواتب وتعديل ملفات الموظفين، بلا طريق للمطوّر يستردّها، وبلا شيء في المدرسة يقول من أين جاءت.

أما ما أنشأته المنصّة في الجهة فيبقى: الصلاحية تبقى موجودة في قائمة الجهة (فتستطيع المدرسة وضعها في مجموعة من مجموعاتها)، والذي يُسحَب هو منحها للدور العامّ. وسمِّ الصلاحية مرّةً واحدةً صحيحةً — إعادة التسمية تعني اسمين حيّين في كل مدرسة.

في الكود

// صفحة Livewire — الحارس في mount()، والقائمة تخفي الرابط بنفس الاسم:
protected ?string $permission = 'okta_hdor.attendance.view';

public function mount(): void
{
    abort_unless(auth()->user()?->can($this->permission), 403);
}

php artisan okta-hdor:make-page Attendance --permission=okta_hdor.attendance.view يولّد الحارس ومدخل القائمة معاً. إخفاء الرابط ليس تحكّماً في الوصول؛ الصفحة هي التي تُحرَس.

ما ليس هنا: لا نطاق يُطلب من الجهة ولا موافقة — هذه صلاحيات داخل تطبيقك على بيانات تطبيقك، والمدرسة تملك توزيعها على أدوارها من إعدادات الأدوار كما تفعل مع أي ميزة في المنصّة.


عرض الدليل كاملاً في صفحة واحدة · ↧ Markdown خام