Когда нужно использовать Gate::after в Laravel

Gate::after в Laravel

Политики (policies) в Laravel — отличный способ организовать логику авторизации, которая крутится вокруг моделей. Долгое время я использовал Gate::before для настройки вседозволенности суперадминов. Но, однажды, работая над новым приложением, у меня в голове щелкнуло и я понял как мне может быть полезен Gate::after. Сейчас я поделюсь с вами этим знанием.

Предполагается, что вы разбираетесь как работает авторизация в Laravel. Сперва я тоже не читал соответствующую документацию.

Разъяснение Gate::before

Моё новое приложение, упомянутое во вступлении, позволяет организаторам мероприятий регистрировать свои события, слоты и докладчиков, а участники могут оставлять отзывы обо всём этом.

Давайте посмотрим на часть политики, которая управляет правами пользователя на администрирование события.

class EventPolicy
{
   use HandlesAuthorization;

   public function administer(User $user, Event $event)
   {
      return $user->organizes($event);
   }
}

С помощью этой политики вы можете выполнить аутентификацию следующим образом:

$user->can('administer', $event); // возвращает логическое значение

Возвращает true для пользователей, которые организуют событие, и false для всех остальных.

В нашей системе есть суперадмины, которым разрешено делать все, что угодно. Они не организуют мероприятия, но они должны иметь возможность управлять ими. Это решается добавлением Gate::before:

// где-то в сервис-провайдере

Gate::before(function ($user, $ability) {
    if ($user->isSuperAdmin()) {
        return true;
    }
});

Замыкание, переданное в Gate::before будет использовано до EventPolicy (и всех других политик). Если вы суперадмин, то теперь вы можете управлять мероприятием, даже если вы не организуете его. Жизнь хороша!

Обратите внимание, что если вы не суперадмин, мы не возвращаем false, а null (ничего не возвращая). Это сделано специально. Если бы мы вернули false, то классы политик не были бы проверены. Только суперадмины смогли бы управлять мероприятиями.

Разъяснение Gate::after

В нашем приложении слот может быть просмотрен только залогиненными пользователями. И только один раз для каждого пользователя. Пользователь не может просмотреть слот до его запуска.

Вот класс политики:

class SlotPolicy
{
    use HandlesAuthorization;

    public function review(User $user, Slot $slot)
    {
        if ($user->hasReviewed($slot)) {
            return false;
        }

        if ($slot->starts_at->isFuture()) {
            return false;
        }
        
        return true;
    }
}

Для не суперадминов это работает нормально. Все наши правила соблюдаются. Но Gate::before, упомянутый в предыдущем разделе, даёт нашим суперадминам слишком много власти. Теперь они могут просматривать слоты несколько раз, и даже до его запуска. Давайте это исправим!

Вместо Gate::before, давайте используем Gate::after.

// где-то в сервис-провайдере

Gate::after(function ($user, $ability) {
   return $user->isSuperAdmin();
});

Теперь политики будут вызываться первыми, даже для суперадмин. Все пользователи, включая суперадминов, не могут просматривать не запущенные слоты. Отлично!

Если вы были внимательны, то, вероятно, заметили, что для проверки review в SlotPolicy — Gate::after не вызывается, потому что SlotPolicy уже возвращает true. Действительно, эта конкретная проверка будет работать и без Gate::after.

Но давайте еще раз посмотрим на EventPolicy из предыдущего раздела.

class EventPolicy
{
   use HandlesAuthorization;

   public function administer(User $user, Event $event)
   {
      return $user->organizes($event);
   }
}

В текущем состоянии суперадминам не разрешено управлять событиями, потому что команда administer возвращает логическое значение. Давайте это исправим.

class EventPolicy
{
  use HandlesAuthorization;

  public function administer(User $user, Event $event)
  {
     if($user->organizes($event)) {
        return true;
     }
  }
}

Теперь мы ничего не возвращаем для обычных пользователей, не организующих мероприятие. В этом случае будет вызвана наша функция обратного вызова Gate::after, который вернет true для суперадминов и false для всех остальных пользователей.

 

Автор: Freek Van der Herten
Перевод: Алексей Широков

Наш Телеграм-канал — следите за новостями о Laravel.