• Час читання ~12 хв
  • 25.04.2023

Завантажити зразок коду: https://github.com/wnikk/-laravel-access-example

У цій статті ми розглянемо, як реалізувати рольовий контроль доступу (RBAC) в Laravel 10, щоб ефективно керувати доступом користувачів.
RBAC - це модель безпеки, де користувачам призначаються ролі на основі їх посадових обов'язків, і цим ролям надаються права доступу.
Ця методологія гарантує, що лише авторизовані користувачі мають доступ до певних функцій і даних у програмі.
Для реалізації RBAC ми будемо використовувати пакет "wnikk/laravel-access-rules" від Github, який спрощує створення ролей та дозволів.
Ми розглянемо кроки, пов'язані зі створенням ролей і дозволів, призначенням їх користувачам і захистом конфіденційної інформації від несанкціонованого доступу.

Однією з основних переваг реалізації RBAC в Laravel є те, що вона забезпечує детальний контроль доступу.
За допомогою RBAC ви можете визначити ролі для різних посад, які обмежують доступ до функцій та даних у програмі.
Наприклад, ви можете створити роль «адміністратора» з повним доступом до програми, тоді як роль «гостя» може переглядати лише певні сторінки.
Ви також можете створити настроювані ролі, які матимуть доступ до певних функцій, як-от "менеджер контенту" або "спеціаліст із виставлення рахунків".
Таким чином, користувачі мають доступ лише до функціональних можливостей, необхідних для виконання своїх трудових обов'язків.

This user profile page

Щоб створити RBAC в Laravel, ми будемо використовувати композиторський пакет "wnikk/laravel-access-rules", який забезпечує простий і гнучкий спосіб створення ролей і дозволів.
Цей пакет дозволяє нам призначати ролі користувачам, призначати дозволи ролям і призначати дозволи безпосередньо користувачам.
Ми розглянемо кроки, пов'язані з налаштуванням пакета, визначенням ролей і дозволів, а також призначенням їх користувачам.
Дотримуючись цього покрокового посібника, ви можете легко впровадити RBAC у свій додаток Laravel та забезпечити безпеку своїх облікових записів користувачів.

З чого почнемо?

Щоб спростити процес реалізації дозволу Laravel: Ми також будемо використовувати функціональність CRUD, яка посилається на реалізацію постійної програми:

  1. User Management - We create user management using Laravel 10. This allows for easier application of Laravel permission.
  2. Rules Management - Additionally, we implement rules management to limit access to content by defining a list of rules for the project.
  3. Permits and inheritance Management - Permissions management can be used to add roles to user accounts and assign Laravel permission to them.
  4. News Management - Finally, we can implement news management and apply Laravel permission with each role assigned to a user.

створення, читання, оновлення та видалення.
Ми застосуємо CRUD до всіх моделей нашого проекту, щоб забезпечити легке управління даними.

Якщо ви шукаєте приклади концепцій, розглянутих у цій статті, ви можете знайти їх легко доступними у відповідному сховищі GitHub. Просто перейдіть до сховища та перегляньте вихідний код, щоб побачити, як були виконані різні реалізації. Це дасть вам краще розуміння того, як концепції працюють на практиці і як ви можете застосувати їх до власних проектів.

Крок 1: Створіть додаток

Laravel Щоб почати реалізацію Laravel 10, першим кроком є створення нового додатка Laravel. Для цього відкрийте термінал або командний рядок і ініціюйте створення нового додатка Laravel. Виконуючи цей крок, ви будете на шляху до впровадження Laravel 10 та всіх його функцій у вашому веб-додатку: Крок 2:

composer create-project laravel/laravel rules-example

Встановлення пакетів

Далі нам потрібно буде встановити необхідний пакет Wnikk для правил контролю доступу (ACR) і пакет візуального управління. Це можна легко зробити, відкривши термінал і виконавши наведені нижче команди.

composer require wnikk/laravel-access-rules
composer require wnikk/laravel-access-ui

Щоб внести зміни в пакет Wnikk, нам потрібно буде запустити команду, яка генерує файли конфігурації, файли міграції та перегляд файлів. Виконавши цей крок, ви зможете налаштувати пакет відповідно до конкретних вимог вашого додатка Laravel: Крок 3:

php artisan vendor:publish --provider="Wnikk\\LaravelAccessRules\\AccessRulesServiceProvider"
php artisan vendor:publish --provider="Wnikk\\LaravelAccessUi\\AccessUiServiceProvider"

Оновіть модель

користувача Тепер ми інтегруємо ACR з нашою існуючою моделлю користувача. Цей крок важливий для забезпечення того, щоб наш додаток Laravel мав належний контроль доступу Нам просто потрібно додати в нього рису HasPermissions: Крок 4:

use Wnikk\LaravelAccessRules\Traits\HasPermissions;

class User extends Model {
    // The User model requires this trait
    use HasPermissions;

Налаштування підключення

до бази даних Для цілей цього прикладу ми використаємо файлову базу даних SQLite. Для початку створіть пустий файл з іменем './database/database.sqlite' і налаштуйте підключення до бази даних, як показано в наведеному прикладі.
Файл .env:

DB_CONNECTION=sqlite

На цьому етапі можна запустити команду перенесення. Виконавши цю команду, ми зможемо створити необхідні таблиці в нашій файловій базі даних SQLite, що дозволить ефективно керувати даними.

php artisan migrate

Тепер, коли ми впровадили працюючу систему контролю доступу з пакетом ACR, наступним кроком є додавання дозволів до моделей у нашому додатку. Дозволи визначають, які дії користувач із певною роллю може виконувати з певним ресурсом.

Крок 5: Створіть міграцію

новин Рухаючись вперед, нашим наступним кроком буде створення міграції для таблиці новин. Для виконання цього завдання виконайте наступну команду, яка згенерує необхідний файл і дозволить нам визначити схему таблиці:

php artisan make:migration create_news_table

Це створить новий файл міграції в каталозі бази даних / migrations нашого додатка Laravel. Нижче ви знайдете повний код, необхідний для визначення структури таблиці, включаючи різні поля та їх відповідні типи:Тепер ми повторно запустимо міграцію: Крок 6:

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('news', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
            $table->integer('user_id');
            $table->string('name', 70);
            $table->string('description', 320)->nullable();
            $table->text('body');
            $table->softDeletes();
        });
    }
    public function down(): void
    {
        Schema::dropIfExists('news');
    }
};

php artisan migrate

Створіть модель Тепер ми створимо модель

новин для отримання даних із таблиці новин. Щоб створити модель новин, просто запустіть наступну команду Artisan. Це створить модель новин у каталозі app\Models.

php artisan make:model News

Приклад коду для моделі новин: Крок 7:

<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

/**
 * Class News
 *
 * @property $id
 * @property $user_id
 * @property $name
 * @property $description
 * @property $body
 */
class News extends Model
{
    use HasFactory, SoftDeletes;

    protected $fillable = [
        'user_id',
        'name',
        'description',
        'body',
    ];
}

Створення сівалки

Тепер, коли в нашій базі даних є всі необхідні таблиці, прийшов час заповнити їх тестовими даними і встановити для них правила.

1. Створіть кілька нових користувачів:Source file database\seeders\CreateUserSeeder.php:

php artisan make:seeder CreateUserSeeder

<?php
namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;

class CreateUserSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        DB::table('users')->insert([
            'id' => 1,
            'name' => 'Test user 1',
            'email' => '[email protected]',
            'password' => Hash::make('12345'),
        ]);
        DB::table('users')->insert([
            'id' => 2,
            'name' => 'Test user 2',
            'email' => '[email protected]',
            'password' => Hash::make('password'),
        ]);
        DB::table('users')->insert([
            'name' => 'Test user 3',
            'email' => Str::random(10).'@mail.com',
            'password' => Hash::make(Str::random(10)),
        ]);
        DB::table('users')->insert([
            'name' => 'Test user 4',
            'email' => Str::random(10).'@mail.com',
            'password' => Hash::make(Str::random(10)),
        ]);
        DB::table('users')->insert([
            'name' => 'Test user 5',
            'email' => Str::random(10).'@mail.com',
            'password' => Hash::make(Str::random(10)),
        ]);
    }
}

2. Ми створюємо кілька новин.

php artisan make:seeder NewsTableSeeder

Вихідний файл database\seeders\NewsTableSeeder.php:

<?php
namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\News;

class NewsTableSeeder extends Seeder
{
    public function run(): void
    {
        News::create([
            'user_id' => 1,
            'name' => 'First news',
            'description' => 'Description of first news',
            'body' => 'Body content 1...',
        ]);
        News::create([
            'user_id' => 1,
            'name' => 'Second news',
            'description' => 'Description of second test news',
            'body' => 'Body content 2...',
        ]);
        News::create([
            'user_id' => 2,
            'name' => 'News of test user',
            'body' => 'Body content 3...',
        ]);
    }
}

3. У систему буде додано кілька правил для цілей тестування.

php artisan make:seeder CreateRulesSeeder

Самі правила:
Вихідний файл database\seeders\CreateRulesSeeder.php:

<?php
namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Wnikk\LaravelAccessRules\AccessRules;

class CreateRulesSeeder extends Seeder
{
    public function run(): void
    {
        // example #1 - route middleware
        AccessRules::newRule('example1.viewAny', 'View all users on example1');

        // example #2 - check in action
        AccessRules::newRule('example2.view', 'View data of user on example2');

        // example #3 - check on action options
        AccessRules::newRule([
            'guard_name' => 'example3.update',
            'title' => 'Changing different user data on example3',
            'options' => 'required|in:name,email,password'
        ]);

        // example #4 - global resource
        AccessRules::newRule('viewAny', 'Global rule "viewAny" for example4');
        AccessRules::newRule('view', 'Global rule "view" for example4');
        AccessRules::newRule('create', 'Global rule "create" for example4');
        AccessRules::newRule('update', 'Global rule "update" for example4');
        AccessRules::newRule('delete', 'Global rule "delete" for example4');

        // example #5 - resource for controller
        AccessRules::newRule('Examples.Example5.viewAny', 'Rule for one Controller his action "viewAny" example5');
        AccessRules::newRule('Examples.Example5.view', 'Rule for one Controller his action "view" example5');
        AccessRules::newRule('Examples.Example5.create', 'Rule for one Controller his action "create" example5');
        AccessRules::newRule('Examples.Example5.update', 'Rule for one Controller his action "update" example5');
        AccessRules::newRule('Examples.Example5.delete', 'Rule for one Controller his action "delete" example5');

        // example #6 - magic self
        AccessRules::newRule(
            'example6.update',
            'Rule that allows edit all news',
        'An example of how to use a magic suffix ".self" on example6'
        );
        AccessRules::newRule('example6.update.self', 'Rule that allows edit only where user is author');

        // example #7 - Policy
        AccessRules::newRule('Example7News.test', 'Rule event "test" example7');

        // Final example, add control to the Access user interface
        $id = AccessRules::newRule('Examples.UserRules.main', 'View all rules, permits and inheritance');
        AccessRules::newRule('Examples.UserRules.rules', 'Working with Rules', null, $id, 'nullable|in:index,store,update,destroy');
        AccessRules::newRule('Examples.UserRules.roles', 'Working with Roles', null, $id, 'nullable|in:index,store,update,destroy');
        AccessRules::newRule('Examples.UserRules.inherit', 'Working with Inherit', null, $id, 'nullable|in:index,store,destroy');
        AccessRules::newRule('Examples.UserRules.permission', 'Working with Permission', null, $id, 'nullable|in:index,update');
    }
}

4. Зараз ми створюємо роль суперадміністратора.

Від якого успадкують усі інші ролі користувачів. На цьому кроці важливо встановити три типи моделей, які можуть мати дозволи у файлі налаштувань за замовчуванням (конфігурація/доступ.php), групах, ролях і користувачах. Для суперадміністратора ми будемо використовувати ролі:

php artisan make:seeder CreateRootAdminRoleSeeder

Дозволами для кожної моделі можна керувати через пов'язаного з нею власника.
Вихідний файл database\seeders\CreateRootAdminRoleSeeder.php:

<?php
namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Wnikk\LaravelAccessRules\AccessRules;

class CreateRootAdminRoleSeeder extends Seeder
{
    public function run(): void
    {
        $acr = new AccessRules;
        $acr->newOwner('Role', 'root', 'RootAdmin role');

        // For example #1
        $acr->addPermission('example1.viewAny');

        // For example #2
        $acr->addPermission('example2.view');

        // For example #3
        $acr->addPermission('example3.update', 'name');
        $acr->addPermission('example3.update', 'email');
        $acr->addPermission('example3.update', 'password');

        // For example #4
        $acr->addPermission('viewAny');
        $acr->addPermission('view');
        $acr->addPermission('create');
        $acr->addPermission('update');
        $acr->addPermission('delete');

        // For example #5
        $acr->addPermission('Examples.Example5.viewAny');
        $acr->addPermission('Examples.Example5.view');
        $acr->addPermission('Examples.Example5.create');
        $acr->addPermission('Examples.Example5.update');
        $acr->addPermission('Examples.Example5.delete');

        // For example #6
        //For all - $acr->addPermission('example6.update');
        $acr->addPermission('example6.update.self');

        // For example #7
        $acr->addPermission('Example7News.test');

        // For final example
        $acr->addPermission('Examples.UserRules.index');
        $acr->addPermission('Examples.UserRules.rules');
        $acr->addPermission('Examples.UserRules.rules', 'index');
        $acr->addPermission('Examples.UserRules.rules', 'store');
        $acr->addPermission('Examples.UserRules.rules', 'update');
        $acr->addPermission('Examples.UserRules.rules', 'destroy');
        $acr->addPermission('Examples.UserRules.roles');
        $acr->addPermission('Examples.UserRules.roles', 'index');
        $acr->addPermission('Examples.UserRules.roles', 'store');
        $acr->addPermission('Examples.UserRules.roles', 'update');
        $acr->addPermission('Examples.UserRules.roles', 'destroy');
        $acr->addPermission('Examples.UserRules.inherit');
        $acr->addPermission('Examples.UserRules.inherit', 'index');
        $acr->addPermission('Examples.UserRules.inherit', 'store');
        $acr->addPermission('Examples.UserRules.inherit', 'destroy');
        $acr->addPermission('Examples.UserRules.permission');
        $acr->addPermission('Examples.UserRules.permission', 'index');
        $acr->addPermission('Examples.UserRules.permission', 'update');
    }
}

5. І, нарешті, додамо успадкування дозволів від суперадміністратора всім користувачам.

php artisan make:seeder AddRoleToAllUserSeeder

Вихідний файл database\seeders\AddRoleToAllUserSeeder.php: Тепер перейдемо до імпорту всіх інструкцій, створених на цьому кроці:

<?php
namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\User;

class AddRoleToAllUserSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        $all = User::all();
        foreach ($all as $one) $one->inheritPermissionFrom('Role', 'root');

        // or
        // $acr = new AccessRules;
        // $acr->setOwner('Role', 'root');
        // foreach ($all as $one) $one->inheritPermissionFrom($acr);

        // or
        // $mainUser = User::find(1);
        // foreach ($all as $one) $one->inheritPermissionFrom($mainUser);
    }
}

Для виконання тих самих маніпуляцій з контролем доступу та успадкуванням, розглянутими вище, альтернативним методом є використання інтерфейсу,

php artisan db:seed --class=CreateUserSeeder
php artisan db:seed --class=NewsTableSeeder
php artisan db:seed --class=CreateRulesSeeder
php artisan db:seed --class=CreateRootAdminRoleSeeder
php artisan db:seed --class=AddRoleToAllUserSeeder

який був доданий на початку цієї статті. Ви можете отримати доступ до інтерфейсу, відкривши адресу "/accessui/" у вашому проекті:

Список усіх ролей, групи та користувача:
List all roles, group and user

Перерахуємо всі правила:
List all rules

Перейдемо до найцікавішої частини ☕

Різні способи перевірки доступу і пов'язані з ними правила.
У майбутньому контролери, використані в прикладах, будуть служити джерелами даних JSON для інтерфейсу SPA, таким чином усуваючи необхідність створювати шаблони.
У будь-якому випадку, ми завжди можемо переглянути результати перевірки правил.

Приклад 1

У цьому прикладі ми використовуємо проміжне програмне забезпечення для маршрутизації для обмеження доступу до контролера.

Apped to file routes\web.php:

Route::get('/example1', [Example1Controller::class, 'index'])->middleware('can:example1.viewAny');

Контролер не використовується в цьому прикладі
Джерело для файлу ... Приклад1Контролер.php:

<?php
namespace App\Http\Controllers\Examples;

use Illuminate\Support\Facades\Response;
use App\Http\Controllers\Controller;
use App\Models\User;

class Example1Controller extends Controller
{
    public function index()
    {
        return Response::json(User::all(), 200);
    }
}

Перевірте, що сталося:
Example1 Response

Приклад 2

Let's try to check the permission in the action itself
Apped to file routes\web.php:

Route::get('/example2', [Example2Controller::class, 'show']);

Source file ... Example2Controller.php:

<?php
namespace App\Http\Controllers\Examples;

use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\Gate;
use App\Http\Controllers\Controller;

class Example2Controller extends Controller
{
    public function show()
    {
        Gate::authorize('example2.view');

        return Response::json(Auth::user()->toArray(), 200);
    }
}

Як і в прикладі, ви можете використовувати всі можливості фасаду воріт Laravel.

Приклад 3

This is quite similar to the previous example, but with a separate list of options.
Apped to file routes\web.php:

Route::any('/example3/{frm}', [Example3Controller::class, 'update']);

Source file ... Example3Controller.php:Чому

<?php
namespace App\Http\Controllers\Examples;

use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Hash;
use App\Http\Controllers\Controller;
use App\Enum\UserProfileFormEnum;

class Example3Controller extends Controller
{
    public function update(UserProfileFormEnum $frm, Request $request)
    {
        // Add the check by indicating after the point of the [Option] field
        Gate::authorize('example3.update.'.$frm->value);

        $user = Auth::user();
        switch ($frm)
        {
            case(UserProfileFormEnum::Name):

                if($request->name) $user->fill( $request->only(['name']) );

            break;
            case(UserProfileFormEnum::Password):

                if($request->password) $user->password = Hash::make($request->password);

            break;
            case(UserProfileFormEnum::Email):

                $validator = Validator::make($request->all(), [
                    'email' => 'required|email',
                ]);
                if ($validator->fails()) abort('403', $validator->messages());

                $user->email = $request->email;

            break;
        }

        return Response::json($user->save(), 200);
    }
}

відбувається така поведінка і чим " Опція" відрізняється від стандартного визначення правила ?

Варто відзначити, що поле «Опція» пов'язане не з правилом, а з самим дозволом.
Це робиться для того, щоб дозволити створення кількох дозволів у межах одного правила. Наприклад,
за ідентифікатором можна отримати всі записи, які мають доступ, без створення окремих таблиць або полів.

Приклад 4

In this example, we will utilize the built-in $this->authorizeResource() function which comes with the resource feature. This function is very convenient as it automatically creates checks for each action against the following rules: "viewAny", "view", "create", "update" and "delete".
Apped to file routes\web.php:

Route::apiResource('example4', Example4Controller::class)->parameters([
    'example4' => 'news'
]);

Source file ... Example4Controller.php:

<?php
namespace App\Http\Controllers\Examples;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Response;
use App\Http\Controllers\Controller;
use App\Models\News;

class Example4Controller extends Controller
{
    /**
     * Create the controller instance.
     */
    public function __construct()
    {
        $this->authorizeResource(News::class, 'News');
    }

    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        return Response::json(News::all());
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request)
    {
        $news = News::create($request->toArray());

        return Response::json($news->id, 201);
    }

    /**
     * Display the specified resource.
     */
    public function show(News $news)
    {
        return Response::json($news->toArray());
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, News $news)
    {
        $news->fill($request->toArray());

        return Response::json($news->save);
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(News $news)
    {
        return Response::json($news->delete());
    }
}

Приклад 5

У попередньому прикладі ми використовували глобальні правила, що не дуже зручно. Щоб подолати це, ми можемо створити функціонал для створення правил для кожного контролера окремо.

Щоб досягти цього, нам потрібно модифікувати головний контролер, для цього ми створимо ознаку.
Вихідний файл App\Http\Traits\GuardedController.php:

<?php
namespace App\Http\Traits;

use App\Http\Controllers\Controller;

trait GuardedController
{
    /**
     * Map of resource methods to ability names
     * @example ['index' => 'viewAny']
     *
     * @var string[]
     */
    //abstract protected $guardedMethods = [];

    /**
     * Do not automatically scan all available methods.
     *
     * @var bool
     */
    //abstract protected $disableAutoScanGuard = true;

    /**
     * List of resource methods which do not have model parameters.
     * @example ['index']
     *
     * @var string[]
     */
    //abstract protected $methodsWithoutModels = ['index'];

    /**
     * Get the map of resource methods to ability names.
     *
     * @return array
     */
    protected function resourceAbilityMap()
    {
        if (empty($this->disableAutoScanGuard)) {
            $methods = array_diff(
                get_class_methods($this),
                get_class_methods(Controller::class)
            );
            $map = array_combine($methods, $methods);
        } else {
            $map = [];
        }

        $map = array_merge($map, parent::resourceAbilityMap());
        $map = array_merge($map, $this->guardedMethods??[]);

        // Replace name for class App\Http\Controllers\Examples\Example1Controller
        // to guard prefix "Examples.Example1."
        $name = $this->getClassNameGate();

        // Replace standard rule "viewAny" to "Examples.Example1.viewAny"
        foreach ($map as &$item) {$item = $name.$item;}
        unset($item);

        return $map;
    }

    /**
     * Get the list of resource methods which do not have model parameters.
     *
     * @return array
     */
    protected function resourceMethodsWithoutModels()
    {
        $base = parent::resourceMethodsWithoutModels();

        return array_merge($base, $this->methodsWithoutModels??[]);
    }

    /**
     * Get name off class witch namespace for guard
     *
     * @param string|null $action
     * @return string
     */
    protected static function getClassNameGate(?string $action = null): string
    {
        // Replace name for class App\Http\Controllers\Examples\Example1Controller
        // to guard prefix "Examples.Example1."
        $name = str_replace([
            dirname(__TRAIT__, 2).DIRECTORY_SEPARATOR.'Controllers'.DIRECTORY_SEPARATOR,
            DIRECTORY_SEPARATOR,
            'Controller'
        ], [
            '', '.', '.'
        ], static::class);

        return $name.$action;
    }
}

Controller і його приклад залишаються точно такими ж, як і в попередньому.
Тільки додана риса (Файл ... Example5Controller.php) :Але помилка вже інша:

<?php
namespace App\Http\Controllers\Examples;
...
use App\Http\Traits\GuardedController;

class Example5Controller extends Controller
{
    use GuardedController;

    public function __construct()
    {
        $this->authorizeResource(News::class, 'News');
    }
...


Example5 Response

Таким чином, з мінімальними змінами існуючого коду,
Можна легко включити можливості динамічного контролю доступу.

Приклад 6

Here is a fairly simple example that is similar to the second one.
Through a few subtle differences, there is a magical behavior present here.
Apped to file routes\web.php:

Route::any('/example6/{news}', [Example6Controller::class, 'update']);

Source file ... Example6Controller.php:

<?php
namespace App\Http\Controllers\Examples;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\Gate;
use App\Http\Controllers\Controller;
use App\Models\News;

class Example6Controller extends Controller
{
    public function update(Request $request, News $news)
    {
        Gate::authorize('example6.update', $news);

        $news->fill($request->toArray());

        return Response::json($news->save?1:0);
    }
}

Давайте розглянемо це детальніше. Якщо у нас є правило "example6.update.self", нам потрібно вказати системі для перевірки правило "example6.update" і додати ".self" передати об'єкт запису, що перевіряється під капотом ACR.

Робота ACR буде виглядати наступним чином:Крім того, варто зазначити, що якщо ми перевіряємо не користувача, а іншу сутність, таку як модератор, *ACR * відстежує це, і перевірка буде виглядати так під капотом:

if (
    Gate::allows('example6.update.self')
    && $user->id === $news->user_id
) {
    return true;
}

$moderator = App\Models\Moderator::find('...');
if (
    Gate::forUser($moderator)->allows('example6.update.self')
    && $moderator->uuid === $news->moderator_uuid
) {
    return true;
}

Більше немає необхідності виконувати такі перевірки, оскільки магічний метод впорається з ними автоматично.

Приклад 7

Хоча це не ABAC, необхідна функціональність контролю доступу на основі атрибутів може бути досягнута шляхом додавання використання вбудованого механізму політики Laravel.

Всі попередні приклади були присвячені перевірці доступу до контролера, але той же підхід ми можемо використовувати і в "Політиці" для реалізації контролю доступу для атрибутів з усіма їх варіаціями.
Для цього необхідно згенерувати правила для нашої моделі:

php artisan make:policy NewsPolicy –model=News

Вихідний файл ... NewsPolicy.php:Після цього необхідно буде оновити в класі AuthServiceProvider $policies, як написано нижче:Тепер перевірте політику в контролері:

<?php
namespace App\Policies;

use App\Models\News;
use App\Models\User;
use Illuminate\Auth\Access\Response;

class NewsPolicy
{
    public function availableUpdateOnSomeTime(User $user, News $news): ?bool
    {
        if(
            $user->can('Example7News.allowedEditLast24Hours', $news)
            && stripos($user->name, 'author') !== false
            && ($news->created_at->isToday() || $news->created_at->isYesterday())
        ) {
            return true;
        }
        return null;
    }
}

protected $policies = [
    News::class => NewsPolicy::class,
];


Вихідний файл ... Example7Controller.php:Таким

<?php
namespace App\Http\Controllers\Examples;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Response;
use App\Http\Controllers\Controller;
use App\Models\News;

class Example7Controller extends Controller
{

    public function index(News $news)
    {
        $this->authorize('availableUpdateOnSomeTime', $news);

        return Response::json($news->toArray());
    }
}

чином, тепер ми маємо можливість перевіряти не тільки правила окремо, але й перевіряти атрибути моделі.

Важливо - якщо додати правило "availableUpdateOnSomeTime" і дозвіл користувачеві, то політика перевірятися не буде.

Останній приклад ⚡

У стані за замовчуванням інтерфейс AccessUi не включає жодних перевірок щодо рівня наданого доступу.
Щоб вирішити цю проблему, ми можемо створити контролер проксі-сервера в цьому прикладі, який перевірить усі дозволи, перш ніж виконувати будь-які маніпуляції з даними.
Спочатку вимкніть стандартні маршрути AccessUi,
File config/accessUi.php:Потім ми створимо 2 контролери:

/**
     * Panel Register
     *
     * This manages if routes used for the admin panel should be registered.
     * Turn this value to false if you don't want to use admin panel
     */
    'register' => false,

"UserRulesController" та "UserProfileController", який використовує рису "RunsOtherController" для запуску інших контролерів AccessUi.
Сама реалізація представлена у файлах "user-rules.blade.php" та "user-profile.blade.php".
Файли можуть бути трохи довгими, але їх можна переглянути окремо в репозиторії.

В результаті у нас з'являться окремі сторінки в нашому стилі з правами доступу verification:Авторизована сторінка профілю користувача:


This user profile page

Список правил і лише ролей (прихований користувач на сторінці власника):
Rules list and only roles list

На закінчення, впровадження використання "wnikk / laravel-access-rules" (ACR, ACL, RBAC) в Laravel є потужним способом забезпечити користувачам доступ лише до ресурсів, які вони уповноважені використовувати. За допомогою вбудованого проміжного програмного забезпечення та функцій авторизації Laravel можна легко створювати та керувати складними політиками контролю доступу як на глобальному, так і на специфічному для контролера рівнях. Використовуючи Access-Control-Rules, розробники можуть додавати динамічні можливості контролю доступу до своїх додатків Laravel з мінімальними змінами коду, гарантуючи, що додаток залишається безпечним і простим в обслуговуванні.

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

Про мене

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

Про автора CrazyBoy49z
WORK EXPERIENCE
Контакти
Ukraine, Lutsk
+380979856297