Вам действительно нужны entrust или laravel-permission для реализации вашей авторизации?

https://adelf.pro/2018/authorization-packages
  • Перевод
«Так… мне нужна простая авторизация. Какая-нибудь админская роль, и может роль редактора/модератора. Сейчас погуглим. О! Для laravel уже есть готовые пакеты! zizaco/entrust, spatie/laravel-permission и другие! Давай выберем какой-нибудь!»

Примерно так все и происходит. Потом миграция пакета добавляет в базу 5 табличек для хранения ролей, пермишенов и их отношений. Все правила авторизации, такие как роли 'admin' и 'editor' могут делать 'edit posts', хранятся в этих таблицах. Обычно в проекте много копий базы данных. Копии разработчиков, тестовая база(ы) и продакшен. В итоге все эти правила авторизации вынуждены синхронизироваться между базами данных.

Я встречал пару проектов где была одна главная копия правил в базе данных продакшена и остальные все копировали оттуда.

Идея использование Seeder класса(пример) намного лучше. Нужно только запустить

php artisan db:seed AuthSeeder

и у вас свежая версия правил в базе данных. Таким образом, этот seeder класс становится неким Single Source Of Truth. Хорошо, но в этом подходе все еще много неудобств:

  • Seeder должен быть довольно умным, чтобы не просто создавать роли и пермишены и связи между ними, но и синхронизировать старые версии. Т.е. удалить или создать связи между ролями и пермишенами если надо.
  • Правила хранятся в базе и необходима постоянная синхронизация между ними. Каждое изменение требования вида «редакторы не должны теперь редактировать посты, только публиковать их» приводит к изменению в seeder'е, сихронизации кодобазы через гит или как-нибудь, и «НЕ ЗАБЫТЬ ЗАПУСТИТЬ AuthSeeder!»
  • Правила авторизации могут быть сложными. Например публикация может быть отредактирована не только редакторами или админами, но и автором этой публикации. Поэтому вместо простого middleware:

['middleware' => ['permission:edit posts']]

разработчики должны использовать стандартную авторизацию laravel:

class PostPolicy
{
    public function edit(User $user, Post $post)
    {
         return $user->id == $post->owner_id 
             || $user->hasPermissionTo('edit posts');
    }
}

Так может самый простой путь не самый лучший?


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

Попробуем проанализировать что обычно проектам нужно? Простая ролевая система. Почти всегда одна роль на юзера. Администратор. Ну может еще редактор или модератор. И эти роли имеют некие пермишены. Пойдем самым прямым путем! Добавим новое поле в таблицу users! is_admin или role. Потом пару методов-хелперов в класс User:

class User
{
    public function isAdmin(): bool {...}
    public function isEditor(): bool {...}
}

Теперь пермишены. Laravel предоставляет два основных метода их описания: Gates и Policies. Я буду использовать gates(тут еще небольшой трюк с функцией в переменной, но для любителей функционального программирования это и не трюк вовсе):

    $isAdmin = function (User $user) {
        return $user->isAdmin();
    }

    $isEditorOrAdmin = function (User $user) {
        return $user->isAdmin() || $user->isEditor();
    }

    Gate::define('foo-permission', $isAdmin);
    Gate::define('bar-permission', $isAdmin);

    Gate::define('editor-permission', $isEditorOrAdmin);

    // Complex permission
    Gate::define('edit-post', function(User $user, Post $post) {
         return $user->id == $post->owner_id 
            || $user->isAdmin();
    });

Если проекту нужно несколько ролей на пользователя, то просто добавляем таблицу user_roles и изменяем методы-хелперы класса User. Содержимое seeder класса для *trust пакетов и этой code-based авторизации практически идентично! Но правила теперь просто хранятся в коде и нет необходимости постоянно синхронизировать их в базах данных.

Я не хочу сказать, что эти пакеты бесполезны. Этот подход весьма полезен в проектах со сложной системой авторизации, где клиент сам хочет настраивать роли впоследствии. А еще есть проекты с динамическими пермишенами. Пример: форум с подфорумами. Каждый подфорум может иметь своих модераторов, каждый модератор обладает определенными администратором правами на этом подфоруме. Еще пример — telegram и его группы. Там тоже самое. Таким проектам действительно необходима сложная, хранимая в базе данных, система авторизации со всеми ее проблемами. Но большинству других — нет.

Ситуация с пакетами для laravel становится похожей на ситуацию с компонентами для Delphi(старички помнят). Пакеты ставят не раздумывая — нужны реально или нет.

Так, мне бы тут в своем проекте посчитать $a + $b. Нет ли какого-нибудь laravel-sum пакета?

P.S. Прошу прощения за «пермишены», но хорошего точного перевода я не нашел.
Поделиться публикацией
Комментарии 12
    +3
    Толковый пост. Статическая бизнес-логика должна быть в коде, а не в базе данных. Относится не только к PHP и Laravel, а вообще к любому фреймворку. И не только к системе прав пользователей.
      –2
      Чем отличается ваш пост от этого?
        +1
        Я не открываю gates & policies, а просто намекаю, что можно пользоваться ими безо всяких доп пакетов. В статье по вашей линке, они юзаются параллельно. Посыл совсем другой.
        0

        Во всех этих пакетах печально то что нельзя пермишенны для анонимусов.
        Хотя вроде с выходом 5.7 могло что-то поменятся.

          +1
          Часто пишут по старинке. Типа, как два года назад использовал какую то стороннюю библиотеку, так и дальше на новых проектах ее продолжают использовать, несмотря на то что фреймвоорк уже предлагает лучшие решения из коробки. Но это же разбираться надо, а многим лень
            +1
            Идея использование Seeder класса(пример) намного лучше.


            А почему вы не расматривате вариант вынести создание ролей в миграцию? При таком подходе у всех разработчиков будет актуальное состояние данных в БД.
              +1
              Это, кстати, хороший вариант :) но немного холиварный. Не по себе мне миграции использовать не для изменения структуры бд. Но, возможно, я не прав.
                0

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

                  0
                  Ну… прям опровергнуть полностью ваш подход мне нечем. всякие SRP приплетать тут не очень умно наверно. Могу лишь сказать, что один из пакетов неявно рекомендует именно сиды — github.com/spatie/laravel-permission#database-seeding
                    +1

                    Адептусы Святого Шпателя не есть истина в последней инстанции, ибо Великая Книга прямо говорит нам:


                    Laravel includes a simple method of seeding your database with test data using seed classes.
                      0
                      Могу лишь согласиться. Так написано прямо начиная с 4.2, я проверил :)
                      А мы в своих проектах в миграциях держали лишь структурные изменения. В сидах же лежали умные классы, которые любые старые наборы изначальных данных приводили к нужному свежему набору.
                      Так удобнее… поскольку сид можно было менять. а вот миграцию будь добр каждый раз создай новую.
                      Похоливарить можно, но нет смысла.
              0
              По мне, так policy за глаза хватет, когда 1 пользователь = 1 роль.

              Для чего-то сложного, типа роли+права, по хорошему надо писать пакет под конкретный проект (в принципе ничего сложного), — на то и лара.

              Выскажу своёё фи с точки зрения «из коробки» — из коробки оно хорошо в 2х случаях:
              а) оно вписывается в архитектуру приложения и будет востребовано (основной параметр)
              б) вы «буржуй» и думаете в ИХ ключе понимания строения вселенной. Поясню. Русскоговорящие (основной язык архитектора — Русский) думают несколько сложнее буржуев, так как сам язык сложнее. В следствии чего решения более простые, масштабируемые (например пакет настолько удобный, что переносится от проекта к проекту; смотрим на Сову и удивляемся её живучести), и либо используют механику (в данном случае лары) либо вообще настолько простые, что выдают требуемый результат без использования той же лары, средствами основного языка.

              Как-то…

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое