• Время чтения ~12 мин
  • 25.04.2023

Скачайте пример кода: https://github.com/wnikk/-laravel-access-example

В этой статье мы рассмотрим, как реализовать управление доступом на основе ролей (RBAC) в Laravel 10 для эффективного управления доступом пользователей.
RBAC — это модель безопасности, в которой пользователям назначаются роли в зависимости от их должностных обязанностей и этим ролям предоставляются права доступа.
Эта методология гарантирует, что только авторизованные пользователи имеют доступ к определенным функциям и данным в приложении.
Для реализации RBAC мы будем использовать пакет «wnikk/laravel-access-rules» от Github, который упрощает создание ролей и разрешений.
Мы расскажем о том, как создавать роли и разрешения, назначать их пользователям и защищать конфиденциальную информацию от несанкционированного доступа.

Одним из основных преимуществ внедрения RBAC в Laravel является то, что он обеспечивает детальный контроль доступа.
С помощью RBAC можно определить роли для различных должностей, которые ограничивают доступ к функциям и данным в приложении.
Например, вы можете создать роль «admin» с полным доступом к приложению, в то время как роль «guest» может иметь возможность просматривать только определенные страницы.
Вы также можете создавать пользовательские роли, которые имеют доступ к определенным функциям, таким как «менеджер контента» или «специалист по выставлению счетов».
Таким образом, пользователи имеют доступ только к тем функциям, которые им необходимы для выполнения своих должностных обязанностей.

This user profile page

Чтобы создать RBAC в Laravel, мы будем использовать пакет composer "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

Это создаст новый файл миграции в каталоге database/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. Создайте несколько новых пользователей:

php artisan make:seeder CreateUserSeeder

База данных исходного файла\seeders\CreateUserSeeder.php:

<?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. Сейчас мы создаем роль суперадминистратора.

От которого будут наследоваться все остальные роли пользователей. На этом шаге важно задать три типа моделей, которые могут иметь разрешения в группах, ролях и пользователях файла параметров по умолчанию (config/access.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 Frontend, что избавит вас от необходимости создавать шаблоны.
Во всяком случае, мы всегда можем просмотреть результат проверки правил.

Пример 1

В этом примере мы используем промежуточное ПО на маршрутизации для ограничения доступа к контроллеру.

Приложение к файлу 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
Приложение к файлу routes\web.php:

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

Исходный файл ... Пример2Контроллер.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 Gate.

Пример 3

This is quite similar to the previous example, but with a separate list of options.
Приложение к файлу routes\web.php:

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

Исходный файл ... 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);
    }
}

Почему происходит такое поведение и чем "Option" отличается от стандартного определения правила?

Стоит отметить, что поле «Опция» привязано не к правилу, а к самому разрешению.
Это делается для того, чтобы разрешить создание нескольких разрешений в рамках одного правила. Например
Можно получить все записи по идентификатору, к которым есть доступ, без необходимости создавать отдельные таблицы или поля.

Пример 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".
Приложение к файлу routes\web.php:

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

Исходный файл ... 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.
Приложение к файлу routes\web.php:

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

Исходный файл ... 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:

<?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;
    }
}

После этого необходимо будет обновиться в классе AuthServiceProvider $policies, как написано ниже:Теперь проверьте политику в контроллере:

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,
Файл config/accessUi.php:

/**
     * 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,

Затем мы создадим 2 контроллера: «UserRulesController» и «UserProfileController», который использует черту «RunsAnotherController» для запуска других контроллеров AccessUi.
Сама реализация просматривается в файлах "user-rules.blade.php" и "user-profile.blade.php".
Файлы могут быть немного длинными, но их можно просматривать отдельно в репозитории.

В результате мы получим отдельные страницы в нашем стиле с проверкой прав доступа:Страница профиля авторизованного пользователя:


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