Système avancé d'autorisation des ressources à Laravel. Partie 3. Lecture / écriture des attributs, propres modèles

introduction


Bonjour, cher Khabrovchans.

Aujourd'hui, je porte à votre attention la dernière partie d'une série d'articles sur l'autorisation avancée des actions avec ressources au Laravel. Pour mieux comprendre ce qui sera discuté dans cet article, vous devez lire les première et deuxième parties.


Cette fois, l'énoncé du problème a un peu changé: nous avons déjà l'autorisation des actions avec des ressources au moyen de l'autorisation des méthodes des contrôleurs correspondants. La tâche consiste à autoriser la modification / visualisation de champs spécifiques du modèle. Vous devez également réaliser la possibilité d'autoriser l'édition / la visualisation par l'auteur de vos modèles.


Ce matériel est destiné aux programmeurs expérimentés et sera difficile à comprendre pour un développeur novice.



Partie 4. Autorisation d'actions avec attributs


Partie théorique


Laravel HasAttributes, HidesAttributes, GuardsAttributes. . / , / — .


:


  • getHidden(), getVisible() .
  • isFillable() .
  • authUpdate() .
  • authView() .
  • totallyGuarded() .

— , , . . , . - Banana Monkey Jungle, . / — . .


Laravel :


  • $visible
  • $hidden
  • $guarded
  • $fillable

. $visible . — $guarded . — , .



// , . app/Extensions/Traits, app/Extensions/Traits/GuardedModel. , ( ).

authView()( ) authUpdate()( ), , .

getVisible() getHidden().

, . filterVisibility(), , () .

filterVisibility() $hidden makeHidden(), , userCanViewAttribute($key) . makeVisible()


isFillable($key), , userCanUpdateAttribute($key)


userCanUpdateAttribute($key) userCanViewAttribute($key) . $user->can() spatie/laravel-permission. , , .


GuardedModel


<?php

namespace App\Extensions\Traits;

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

trait GuardedModel
{
    protected function authView(): array
    {
        return [];
    }

    protected function authUpdate(): array
    {
        return [];
    }

    public function getHidden(): array
    {
        $this->filterVisibility();

        return parent::getHidden();
    }

    public function getVisible(): array
    {
        $this->filterVisibility();

        return parent::getVisible();
    }

    public function isFillable($key)
    {
        if (in_array($key, $this->authView())) {
            return $this->userCanUpdateAttribute($key);
        }

        return parent::isFillable($key);
    }

    private function filterVisibility(): void
    {
        $this->makeHidden($this->authView());

        $authVisible = array_filter(
            $this->authView(),
            fn ($attr) => $this->userCanViewAttribute($attr)
        );

        $this->makeVisible($authVisible);
    }

    private function userCanViewAttribute(string $key): bool
    {
        /** @var User $user */
        $user = auth()->user();
        $ability = !empty($user) && $user->can("view-attr-$key-" . static::class);

        return $ability;
    }

    private function userCanUpdateAttribute(string $key): bool
    {
        /** @var User $user */
        $user = auth()->user();
        $ability = !empty($user) && $user->can("update-attr-$key-" . static::class);

        return $ability;
    }

    public function totallyGuarded()
    {
        $guarded = (
            count($this->getFillable()) === 0
            && count($this->authView()) === 0
            && $this->getGuarded() == ['*']
        );

        return  $guarded;
    }
}

totallyGuarded(). Illuminate\Database\Eloquent\MassAssignmentException , . — , .

$user->can(«update-attr-$key-». static::class), . , attr. /.


, authView() authUpdate(). user user_id. user_id, user user_id . . , , $guarded, $hidden, $fillable, .


Posts


<?php

namespace App\Models;

use App\Extensions\Traits\GuardedModel;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use GuardedModel;

    protected $hidden = ['id'];

    protected $guarded = ['created_at', 'updated_at'];

    protected $fillable = ['title', 'description', 'user_id'];

    protected function authView(): array
    {
        return ['user_id', 'user'];
    }

    protected function authUpdate(): array
    {
        return ['user_id'];
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

5.



. user(), user_id. . , . $user->can('delete-self-'. $this->getModelClass()). , . isOwner(User $user, Model $model). . , , — .


Modèle général Politique de politique


<?php

namespace App\Policies;

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

abstract class ModelPolicy
{
    use HandlesAuthorization;

    abstract protected function getModelClass(): string;

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

        if ($user->can('delete-self-' . $this->getModelClass())) {
            return $this->isOwner($user, $model);
        }

        return false;
    }

    private function isOwner(User $user, Model $model): bool
    {
        if (!empty($user) && method_exists($model, 'user')) {
            return $user->getKey() === $model->getRelation('user')->getKey();
        }

        return false;
    }
}

Je note également que pour éviter les erreurs lors de l'exécution, il convient d'introduire une vérification de la disponibilité de la méthode user () ou similaire dans le modèle cible.


C'est tout pour le moment. J'espère que cet article vous a été utile. Nous avons un système d'autorisation d'actions assez complet et flexible. Si vous avez des questions, je répondrai certainement. Et bien sûr, les commentaires ou suggestions constructifs sont également les bienvenus. Si vous le souhaitez, vous pouvez voir la mise en œuvre de ce projet de formation plus en détail dans le référentiel .


All Articles