محدود کردن دسترسی بر اساس نقش (*Role Based Access Control*) نوعی مدیریت دسترسی است که در آن هر کاربر دارای یک نقش (*Role*) بوده و هر نقش دارای یک مجموعه دسترسی است. در این سؤال قرار است این سیستم را پیادهسازی کنید.
# جزئیات پروژه
پروژهی اولیه را از [این لینک](/contest/assignments/20699/download_problem_initial_project/67890/) دانلود کنید. ساختار فایلهای این پروژه بهصورت زیر است:
```
rbac_inital
├── app
│ ├── Console
│ │ └── Kernel.php
│ ├── Exceptions
│ │ └── Handler.php
│ ├── Http
│ │ ├── Controllers
│ │ │ ├── Controller.php
│ │ │ └── RBACController.php
│ │ ├── Middleware
│ │ │ ├── Authenticate.php
│ │ │ ├── EncryptCookies.php
│ │ │ ├── PreventRequestsDuringMaintenance.php
│ │ │ ├── RedirectIfAuthenticated.php
│ │ │ ├── TrimStrings.php
│ │ │ ├── TrustHosts.php
│ │ │ ├── TrustProxies.php
│ │ │ └── VerifyCsrfToken.php
│ │ └── Kernel.php
│ ├── Models
│ │ ├── Permission.php
│ │ ├── Role.php
│ │ └── User.php
│ └── Providers
│ ├── AppServiceProvider.php
│ ├── AuthServiceProvider.php
│ ├── BroadcastServiceProvider.php
│ ├── EventServiceProvider.php
│ └── RouteServiceProvider.php
├── bootstrap
│ ├── cache
│ │ ├── packages.php
│ │ └── services.php
│ └── app.php
├── config
│ ├── app.php
│ ├── auth.php
│ ├── broadcasting.php
│ ├── cache.php
│ ├── cors.php
│ ├── database.php
│ ├── filesystems.php
│ ├── hashing.php
│ ├── logging.php
│ ├── mail.php
│ ├── queue.php
│ ├── services.php
│ ├── session.php
│ └── view.php
├── database
│ ├── factories
│ │ └── UserFactory.php
│ ├── migrations
│ │ ├── 2014_10_12_000000_create_users_table.php
│ │ ├── 2014_10_12_100000_create_password_resets_table.php
│ │ ├── 2019_08_19_000000_create_failed_jobs_table.php
│ │ ├── 2020_09_27_092338_create_roles_table.php
│ │ ├── 2020_09_27_092758_create_permissions_table.php
│ │ ├── 2020_09_27_092917_create_permission_role_table.php
│ │ └── 2020_09_27_093704_create_role_user_table.php
│ ├── seeders
│ │ └── DatabaseSeeder.php
│ └── database.db
├── public
│ ├── favicon.ico
│ ├── index.php
│ ├── robots.txt
│ └── web.config
├── resources
│ ├── css
│ │ └── app.css
│ ├── js
│ │ ├── app.js
│ │ └── bootstrap.js
│ ├── lang
│ │ └── en
│ │ ├── auth.php
│ │ ├── pagination.php
│ │ ├── passwords.php
│ │ └── validation.php
│ └── views
│ └── welcome.blade.php
├── routes
│ ├── api.php
│ ├── channels.php
│ ├── console.php
│ └── web.php
├── storage
│ ├── app
│ │ └── public
│ ├── framework
│ │ ├── cache
│ │ │ └── data
│ │ ├── sessions
│ │ ├── testing
│ │ └── views
│ └── logs
│ └── laravel.log
├── tests
│ ├── Feature
│ │ └── RBACSampleTest.php
│ ├── Unit
│ ├── CreatesApplication.php
│ └── TestCase.php
├── README.md
├── artisan
├── composer.json
├── composer.lock
├── package.json
├── phpunit.xml
├── server.php
└── webpack.mix.js
```
<details class="brown">
<summary>راهاندازی پروژه</summary>
**برای اجرای پروژه، باید `php` و `composer` را از قبل نصب کرده باشید.**
+ ابتدا پروژهی اولیه را دانلود و از حالت فشرده خارج کنید.
+ دستور `composer install` را در پوشهی اصلی پروژه برای نصب نیازمندیها اجرا کنید.
</details>
## مدلها
مدلها در پروژه اولیه داده شده است. همچنین میتوانید با اجرای دستور `php artisan migrate` مایگریشنها را اجرا کرده و پایگاه داده را به طور کامل داشته باشید. در این پروژه از پایگاه دادهی *SQLite* استفاده شده است. در ادامه، لیست مدلها و توضیحات آمده است:
1. `User`: مدل کاربر که دارای فیلدهای `name`، `email` و `password` است؛ فیلدهای `name` و `password` را میتوانید دلخواه مقدار دهید.
2. `Role`: مدل نقش که دارای فیلد `name` است
3. `Permission`: مدل دسترسی که دارای فیلد ``name`` است
علاوه بر این مدلها، دو جدول `permission_role` و `role_user` نیز وجود دارند که جدولهای رابطه هستند.
شما باید متدهای زیر را در کلاس `RBACController` پیادهسازی کنید:
1. `createRole($name)`: این متد با ورودی گرفتن نام نقش آن را اضافه میکند.
2. `addPermissionToRole($class, $function, $role)`: این متد با گرفتن نام کلاس، نام متد و نام نقش، اجازه دسترسی یک نقش به یک متد از یک کلاس را میدهد.
3. `addUser($email)`: این متد یک کاربر با ایمیل داده شده را ایجاد میکند.
4. `addRoleToUser($role, $email)`: این متد با با دریافت نام نقش و ایمیل کاربر، آن نقش را به کاربر مورد نظر میدهد. هر کاربر میتواند چند نقش داشته باشد.
برنامهی شما به این صورت داوری میشود که کلاسی با ارثبری از کنترلر اصلی لاراول (`App\Http\Controllers\Controller`) به کد شما اضافه میشود. شما باید چهار متد `create`، `update`، `store` و `edit` را در کلاس `Controller` طوری پیادهسازی کنید که بهترتیب چهار دسترسی `create`، `update`، `store` و `edit` برای کلاسی که از `Controller` ارثبری کرده است را بررسی کند. در صورتی که برای کاربر لاگینشده دسترسی موردنظر وجود نداشته باشد، متد موجود در `Controller` باید عدد `403` را برگرداند (`return 403;`). در غیر اینصورت، این متدها نباید مقداری را برگردانند. **تضمین میشود** که هنگام تست این متدها، کاربر در سایت لاگین کرده است (با استفاده از `Auth::user()` میتوانید اطلاعات کاربر را دریافت کنید).
**توجه:** شما تنها مجاز به تغییر فایلهای موجود در پوشهی `/app` هستید.
# آنچه باید آپلود کنید
پس از اعمال تغییرات، کل پروژه به غیر از پوشهی `vendor` را *Zip* کرده و آپلود کنید. نام فایل *Zip* اهمیتی ندارد.
# قسمت آموزشی
در این قسمت راهنماییهای سوال، به مرور اضافه میشود. مشکلاتتان در راستای حل سوال را میتوانید از بخش ["سوال بپرسید"](https://quera.ir/contest/clarification/20699/) مطرح کنید.
<details class="blue">
<summary>راهنمایی ۱</summary>
برای پیادهسازی متد `createRole`، میتوان از متد `create` در مدل `Role` استفاده کرد. متد `addUser` نیز مشابه این متد پیادهسازی میشود:
```php
public function createRole($name)
{
Role::create([
'name' => $name
]);
return response()->json();
}
```
برای پیادهسازی متد `addRoleToUser`، میتوان ابتدا `Role` متناظر با نام ورودی و `User` متناظر با ایمیل ورودی را دریافت کرد و سپس `Role` را به `User` *attach* کرد:
```php
public function addRoleToUser($role, $email)
{
$role = Role::where('name', $role)->first();
$user = User::where('email', $email)->first();
$user->roles()->attach($role);
return response()->json();
}
```
برای پیادهسازی متد `addPermissionToRole`، یک روش برای ذخیرهسازی نام کلاس و نام متد در فیلد `name` دسترسی، جداسازی آنها با استفاده از یک کاراکتر *space* است:
```php
public function addPermissionToRole($class, $function, $role)
{
$permission = Permission::firstOrCreate([
'name' => $class . ' ' . $function
]);
$role = Role::where('name', $role)->first();
$role->permissions()->attach($permission);
return response()->json();
}
```
</details>
<details class="blue">
<summary>راهنمایی ۲</summary>
برای پیادهسازی متد `create` در کلاس `Controller`، باید ابتدا `Role`های کاربر و سپس `Permission`های متناظر با `Role`ها را دریافت کرد. نام کلاسی که این متد را صدا میزند با استفاده از کلاس `ReflectionClass` یا `get_called_class` قابل دریافت است. در صورتی که دسترسی موردنظر وجود نداشته باشد، باید عدد `403` را برگرداند.
برای جلوگیری از مشکل $N+1$، میتوان از `join` استفاده کرد:
```php
public function create()
{
$className = str_replace(__NAMESPACE__ . '\\', '', get_called_class());
$hasPermission = Auth::user()
->roles()
->join('permission_role', 'roles.id', '=', 'permission_role.role_id')
->join('permissions', 'permission_role.permission_id', '=', 'permissions.id')
->where('permissions.name', $className . ' create')
->exists();
if (!$hasPermission) {
return 403;
}
}
```
سایر متدهای کلاس `Controller` نیز مشابه متد `create` پیادهسازی میشوند.
</details>
ارسال پاسخ برای این سؤال
در حال حاضر شما دسترسی ندارید.