Advanced resource authorization system in Laravel. Part 1. Model, Controller

Introduction


Hello, dear Khabrovchans.
In the course of my work on the api platform, I spent a lot of time looking for the right way to authorize user actions. The task was set as follows - to create a fairly extensive system of access control and actions.
At the same time, most of them are on a regular CRUD, but other controller actions will need to be authorized.
So it is necessary to create a simple and at the same time effective and flexible system. There were a lot of cones, because in these articles I decided to demonstrate a somewhat simplified version of what I did.


I would like to add separately - the material is designed for practicing programmers, and will be difficult for a beginner to understand. This article will not schedule the installation of the project, and the settings for connecting to the database. All this you can easily find on the Internet.



Part 1. Model, Controllers


So, the task: There are many models, the number of which can increase and decrease during the development of the project.
Actions on each of them should be authorized by roles.
There are simple actions, such as CRUD, and additional ones (for example, import, export ).
It is necessary to simplify the work of the developer (to your beloved) as much as possible to introduce additional models and methods to them.
The article provides an example for an Api application, but the solution is suitable not only for that.


Model


Post. ( ) app.
app/Models Posts. ( , -).
. :


    php artisan make:model Models/Post --api --migration
    php artisan migrate

: , . , .



PostController CRUD
Controller.
ModelController Controller , , PostController.
PostControllerimport() export(Post $post).
, .



Laravel Illuminate\Foundation\Auth\Access\AuthorizesRequests.
App\Http\Controllers\Controller ModelController. Controller , .
authorizeResource($model).
, (Middleware) .
:


    can:viewAny,App\Models\Post
    can:view,post
    can:update,post

, , — :


  1. viewAny view apiResource
  2. , — .

:


. resourceAbilityMap() .


<?php
protected function resourceAbilityMap()
{
    return [
        'index' => 'viewAny',
        'show' => 'view',
        'create' => 'create',
        'store' => 'create',
        'edit' => 'update',
        'update' => 'update',
        'destroy' => 'delete',
    ];
}

. Laravel . , . index() , show(Post $post) . resourceMethodsWithoutModels()


<?php
protected function resourceMethodsWithoutModels()
{
    return ['index', 'create', 'store'];
}


(ModelController)

app/Http/Controllers ModelController. :


  • $guardedMethods « » => « ».
    (Policy) , , (Gate). .
  • $methodsWithoutModels .
  • getModelClass() .
    .
  • authorizeResource($this->getModelClass()) . ( (Route), )
  • resourceAbilityMap() resourceMethodsWithoutModels() , , .

<?php

namespace App\Http\Controllers;

abstract class ModelController extends Controller
{
    /** @var array 'method' => 'policy'*/
    protected $guardedMethods = [];

    protected $methodsWithoutModels = [];

    protected abstract function getModelClass(): string;

    public function __construct()
    {
        $this->authorizeResource($this->getModelClass());
    }

    protected function resourceAbilityMap()
    {
        $base = parent::resourceAbilityMap();

        return array_merge($base, $this->guardedMethods);
    }

    protected function resourceMethodsWithoutModels()
    {
        $base = parent::resourceMethodsWithoutModels();

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

(PostController)

:


  • getModelClass() .
  • $methodsWithoutModels « » => « » . . , =. , , .
  • $methodsWithoutModels , . import
  • — , . — .

<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\Request;

class PostController extends ModelController
{
    /** @var array 'method' => 'policy'*/
    protected $guardedMethods = [
        'export' => 'export',
        'import' => 'import',
    ];

    protected $methodsWithoutModels = ['import'];

    protected function getModelClass(): string
    {
        return Post::class;
    }

    public function index()
    { /**     */ }

    public function store(Request $request)
    { /**    */ }

    public function show(Post $post)
    { /**    */ }

    public function update(Request $request, Post $post)
    { /**    */ }

    public function destroy(Post $post)
    { /**    */ }

    public function import()
    { /**     */ }

    public function export(Post $post)
    { /**    */ }
}

(Routes)


.
'routes/api.php' ( 'routes/web.php', ) .
, , .
— ( ) , Route::apiResource('posts', 'PostController'). Laravel. .
— 'auth:api'. , .


<?php

use App\Http\Controllers\PostController;
use Illuminate\Support\Facades\Route;

Route::group(['middleware' => ['auth:api']], static function () {
    Route::post('posts/import', 'PostController@import')->name('posts.import');
    Route::get('posts/{post}/export', 'PostController@export')->name('posts.export');
    Route::apiResource('posts', 'PostController');
});

. :


php artisan route:list

— ( )


+-----------------------+---------------------------+-------------------------------+
|URI                    |Action                     |Middleware                     |
+-----------------------+---------------------------+-------------------------------+
|api/posts              |...\PostController@index   |...,can:viewAny,App\Models\Post|
|api/posts              |...\PostController@store   |...,can:create,App\Models\Post |
|api/posts/import       |...\PostController@import  |...,can:import,App\Models\Post |
|api/posts/{post}       |...\PostController@show    |...,can:view,post              |
|api/posts/{post}       |...\PostController@update  |...,can:update,post            |
| api / posts / {post} | ... \ PostController @ destroy | ..., can: delete, post |
| api / posts / {post} / export | ... \ PostController @ export | ..., can: export, post |
+ ----------------------- + ------------------------- - + ------------------------------- +



The next step is to configure the link Gate (Gate) <-> Policy. But more about that in the second part .


All Articles