Système avancé d'autorisation des ressources à Laravel. Partie 2. Passerelles, politiques

introduction


Bonjour cher Khabrovchane.
Je continue ma série d'articles sur l'autorisation avancée des ressources à Laravel. Pour mieux comprendre ce qui sera discuté dans cet article - vous devez lire la première partie .


Pour commencer, je vais répéter brièvement l'énoncé du problème: il existe un grand nombre de modèles. Il est nécessaire de concevoir un système flexible et facilement extensible pour autoriser les actions de l'utilisateur en fonction de son rôle.
Dans cette partie, nous parlerons de la configuration de la politique de liaison <=> Gate. Et une des options pour écrire les droits des utilisateurs sur la base de données est également proposée.


Et bien sûr, je préciserai immédiatement que le matériel est conçu pour les programmeurs pratiquants et sera difficile à comprendre pour un développeur novice.



Partie 3. Politiques, passerelles


Partie théorique


Beaucoup de matériel sur Internet peut être trouvé sur le sujet - «Que choisir la politique ou la porte ?». Pendant un certain temps, je suis également arrivé dans une erreur similaire que vous devez choisir quelque chose. Mais à la fin, je suis arrivé à la conclusion - ce sont deux maillons de la même chaîne, qui sont le plus efficacement utilisés conjointement.
À Laravel, il y a deux directions principales pour déterminer cet ensemble - manuel et automatique. Je ne considérerai pas le manuel, car la prise en charge de ce type de bundle nécessitera de manière disproportionnée plus d'efforts de programmation que la mise en place d'une solution automatique.


— , () . (Gate) — . — , . , . , app. . app/Models.


Gate::guessPolicyNamesUsing('callback'). , boot() App\Providers\AuthServiceProvider( ).



Gate


(callback), guessPolicyNamesUsing() , . . : Policy . , , Laravel ( ). SuperAdmin , , — super-admin . — .
PHP 7.4 . 7.4 .


<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;

class AuthServiceProvider extends ServiceProvider
{
    public function boot()
    {
        $this->defineSuperAdmin();
        $this->defineAutoDiscover();
    }

    private function defineAutoDiscover()
    {
        Gate::guessPolicyNamesUsing(function ($class) {
            return str_replace("\\Models\\", "\\Policies\\" , $class) . 'Policy';
        });
    }

    private function defineSuperAdmin()
    {
        Gate::before(function($user) {
            return $user->hasRole('super-admin') ? true : null;
        });
    }
}

Policy


, , . . :


    php artisan make:policy PostPolicy --model=Models/Post

. . , DRY,- , . , . , getModelClass() . , ( ). . (import export — )


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

— , . , . . Spatie. laravel-permission. , — , , . , , . — , $user->can() $user->hasRole(), Spatie\Permission\Traits\HasRoles


(Models\User)

<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable
{
    use Notifiable;
    use HasRoles;

    /**     */
}

, (Admin User, ) — HasRoles, .
.


(ModelPolicy)

<?php

namespace App\Policies;

use App\Models\User;
use App\Models\Post;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Database\Eloquent\Model;

abstract class ModelPolicy
{
    use HandlesAuthorization;

    abstract protected function getModelClass(): string;

    public function viewAny(User $user)
    {
        return $user->can('view-any-' . $this->getModelClass());
    }

    public function view(User $user, Post $model)
    {
        return $user->can('view-' . $this->getModelClass());
    }

    public function create(User $user)
    {
        return $user->can('create-' . $this->getModelClass());
    }

    public function update(User $user, Post $model)
    {
        return $user->can('update-' . $this->getModelClass());
    }

    public function delete(User $user, Post $model)
    {
        return $user->can('delete-' . $this->getModelClass());
    }
}

$user->can('ability') . . ''-' ', — ( ). (SoftDelete). , .
. Post getResourceName() .


(PostPolicy)

<?php

namespace App\Policies;

use App\Models\Post;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;

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

    public function import(User $user)
    {
        return $user->can('import-' . $this->getModelClass());
    }

    public function export(User $user, Post $model)
    {
        return $user->can('export-' . $this->getModelClass());
    }
}

, import() , . — . . , .


, , . .


4. (Seeding)


, . .
spatie/laravel-permission, . .
:


    composer require spatie/laravel-permission
    php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"

(seeder)


    php artisan make:seeder PermissionsSeeder

run() database/seeds/DatabaseSeeder.php. PermissionsSeeder .


(DatabaseSeeder)

<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run()
    {
         $this->call(PermissionsSeeder::class);
    }
}

, — PermissionsSeeder. database/data php . :


    role => model => action

// (permissions_roles.php)

<?php

return [
    'admin' => [
        'App\\Models\\Post' => [
            'view',
            'view-any',
            'create',
            'update',
            'delete',
            'import',
            'export',
        ],
    ],
    'App\\Models\\Post' => [
        'post' => [
            'view',
            'view-any',
        ],
    ],
];

. , , — .


(PermissionsSeeder)


<?php

use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;

class PermissionsSeeder extends Seeder
{
    private $data = [];

    public function run()
    {
        $this->loadData();
        $this->seedRoles();
    }

    public function loadData(): void
    {
        $this->data = require_once database_path("data/permissions_roles.php");
    }

    private function seedRoles(): void
    {
        Role::create(['name' => 'super-admin', 'guard_name' => 'api']);

        foreach ($this->data as $roleName => $perms) {
            $role = Role::create(['name' => $roleName, 'guard_name' => 'api']);
            $this->seedRolePermissions($role, $perms);
        }
    }

    private function seedRolePermissions(Role $role, array $modelPermissions): void
    {
        foreach ($modelPermissions as $model => $perms) {
            $buildedPerms = collect($perms)
                ->crossJoin($model)
                ->map(function ($item) {
                $perm = implode('-', $item); //view-post
                Permission::findOrCreate($perm, 'api');

                return $perm;
            })->toArray();

            $role->givePermissionTo($buildedPerms);
        }
    }
}

, - . , , . , , .
, :


  • run() .
  • loadData() database/data $data
  • seedRoles() . , $data, . 'super-admin' , , .. ( AuthServiceProvider).
  • seedRolePermissions() , , «» , (permission). . () — findOrCreate(), laravel-permissions. , , .

, . :


    php artisan migrate --seed

. ! , :


  1. (Model).
  2. (Controller) ModelController.
  3. (Policy) ModelPolicy.
  4. .

, (. 1) (. ).


C'est tout pour le moment. J'espère que cet article vous a été utile. Et si quelqu'un est intéressé par un réglage encore plus fin des droits et restrictions des utilisateurs, à savoir le droit de l' utilisateur de regarder / modifier un attribut spécifique du modèle - cela est
discuté dans la troisième partie .


All Articles