# کنترل دسترسی (RBAC)

تعریف کامل: `backend/src/modules/rbac/catalog.ts`
پیاده‌سازی بررسی: `backend/src/modules/rbac/service.ts`

## ۱. مدل

سه لایه:

```
کاربر ──< user_roles >── نقش ──< role_permissions >── مجوز
                │
                └── دامنه (scope): global | organization | business
                    scope_id → شناسه سازمان یا کسب‌وکار
```

هر انتساب نقش یک **دامنه** دارد. یعنی «مدیر سازمان» بودن یک واقعیت مطلق نیست؛
کاربر مدیر *یک سازمان مشخص* است. تابع `can(principal, permission, { organizationId })`
هم مجوز و هم دامنه را بررسی می‌کند.

## ۲. مجوزها (۴۰ کلید)

| گروه | کلیدها |
|---|---|
| محتوا | `article.create`, `article.edit.own`, `article.edit.any`, `article.submit`, `article.review`, `article.approve`, `article.publish`, `article.schedule`, `article.lock`, `article.correct`, `article.archive` |
| گزارش | `report.view.private`, `report.moderate`, `report.route`, `report.assign`, `report.respond`, `report.close`, `report.reopen`, `routing.manage` |
| سازمان | `organization.manage`, `organization.staff.manage`, `organization.verify`, `organization.message`, `task.manage` |
| کسب‌وکار | `business.manage`, `business.verify`, `guild.manage` |
| ارتباطات | `communication.manage` |
| آمار | `statistics.create`, `statistics.edit`, `statistics.publish` |
| سیستم | `user.manage`, `role.manage`, `permission.manage`, `taxonomy.manage`, `media.manage`, `comment.moderate`, `audit.view`, `analytics.view`, `settings.manage`, `search.manage`, `backup.manage` |

## ۳. نقش‌ها (۱۸ نقش)

| نقش | دامنه | خلاصه |
|---|---|---|
| `super_admin` | سراسری | دسترسی کامل — **اما قانون ۱۰ روزه برای او هم اعمال می‌شود** |
| `admin` | سراسری | مدیریت محتوا، کاربران و نهادها |
| `second_admin` | سراسری | جانشین با دامنه محدودتر |
| `editor_in_chief` | سراسری | مسئول نهایی تحریریه |
| `content_editor` | سراسری | ویرایش و بازبینی |
| `reporter` / `journalist` | سراسری | تولید محتوا؛ فقط خبر خود را می‌بیند و ویرایش می‌کند |
| `data_editor` / `researcher` | سراسری | مجموعه‌داده و روزنامه‌نگاری داده |
| `moderator` | سراسری | صف بررسی گزارش‌ها و دیدگاه‌ها |
| `organization_manager` | سازمان | مدیریت فضای کاری یک سازمان |
| `organization_staff` | سازمان | کارشناس رسیدگی به پرونده |
| `pr_officer` | سازمان | روابط عمومی، کانال‌های ارتباطی |
| `verified_official` | سازمان | مسئول تأییدشده برای ثبت پاسخ رسمی |
| `business_owner` / `business_manager` | کسب‌وکار | مدیریت پروفایل و پاسخ به شکایت |
| `guild_manager` | کسب‌وکار | مدیریت صنف |
| `citizen` | سراسری | ثبت گزارش، دیدگاه، ذخیره مطلب |

## ۴. جداسازی چند-مستاجری

توابع `scopedOrganizationIds(principal)` و `scopedBusinessIds(principal)` فهرست
نهادهایی را برمی‌گردانند که کاربر در دامنه‌ی آن‌ها نقش دارد. سپس:

```ts
function assertOrgAccess(principal, organizationId) {
  if (scopedOrganizationIds(principal).includes(organizationId)) return;
  if (can(principal, 'organization.manage')) return; // مدیر سراسری
  throw Errors.forbidden('شما به فضای کاری این سازمان دسترسی ندارید.');
}
```

این بررسی در **همه‌ی** مسیرهای `/api/civic/org/:organizationId/*` انجام می‌شود.
هیچ مسیری داده‌ی سازمان دیگر را بازنمی‌گرداند، حتی اگر شناسه‌ی معتبر حدس زده شود.

برای پرونده‌ها، `assertCaseAccess(principal, caseId, action)` سه سناریو را پوشش می‌دهد:
1. گزارش‌دهنده (اگر ناشناس نباشد) پرونده‌ی خودش را می‌بیند.
2. اعضای سازمان مسئول، پرونده‌های ارجاع‌شده به سازمان خودشان را می‌بینند.
3. دارنده‌ی `report.view.private` همه را می‌بیند.

## ۵. آنچه Frontend انجام می‌دهد و آنچه نمی‌دهد

`useSession().permissions` فقط برای این است که آیتم منویی که کاربر مجاز نیست
نمایش داده نشود. هر endpoint دوباره بررسی می‌کند و در صورت نبود مجوز `403` برمی‌گرداند.
آزمون `backend/tests/critical.test.ts` این را با فراخوانی مستقیم سرویس‌ها می‌سنجد.
