Laravel. Простейшие ACL

screenshot-laravel com 2016-03-28 10-36-38Как я уже писал, в Laravel есть встроенные средства авторизации. Но, к сожалению, штатная авторизация проверяет лишь залогинился пользователь или нет. Это может оказаться достаточно, когда у залогиненного пользователя лишь одна роль — суперадмининистратор.

А что если у пользователей могут быть разные роли «редактор», «корректор», «модератор» и прочее?
И у всех должны быть разный функционал и доступ. Вот тут и становятся нужны ACL.

Немного теории
———————————
Если абстрагироваться, чисто теоретически на сайте может быть несколько ролей. Много ролей. И много пользователей. На сайте могут быть пользователи с одинаковыми ролями. Например несколько администраторов или модераторов. С другой стороны один пользователь теоретически может иметь несколько ролей. Ну например, в новостной редакции есть такой «фигаро» на все руки, который и тексты вычитывает и комменты модерирует, но при этом не суперадминистратор.

В этом примере есть таблица «Roles», и есть таблица «Users». И они связаны много-ко-многим. То есть для хранения этой связи надо хранить некую промежуточную таблицу «Roles_Users».

Такая же структура данных была в Kohana.

У этой системы есть плюсы.
* Система полна. Она позволяет полностью описать отношения пользователей и ролей
* Система гибка. Она позволяет в любой момент дать любому пользователю доп.роль или наоборот.

Но есть и минус. Главный минус это сложность. Система сложна и не всегда оправдана. В моей практике не было ни одного сайта, где пользователь имел бы больше одной роли.

Поэтому в данном примере я сознательно огрубляю ситуацию. Я предполагаю, что
* У каждого пользователя может быть только одна роль
* Роли не могут создаваться/удаляться/модифицироваться по ходу работы сайта.

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

Например, если у нас пользователи могут быть только двух ролей — админ(ы) и простые пользватели, то можно у пользователей завести логическое поле is_admin.

Или, если ролей много, можно завести строковое поле (перечисляемое), в котором хранить роль. Например «admin», «editor», «moderator», «subscriber» и проч.

Делаем миграцию
————————————
Итак, нам надо добавить поле в таблицу `users`. Сначала создаём миграцию

$ php artisan make:migration --table=users add_role

Потом добавляем туда свои делишки. Содержательная часть должна получиться примерно такая:

class AddRole extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('role')->default('user');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
             $table->dropColumn('role');
        });
    }
}

Далее накатываем миграцию

$ php artisan migrate

Создаём middleware
——————————
Далее для каждого уровня проверки удобно было бы создать middleware. Например, проверяем на администратора

$ php artisan make:middleware CheckAdminRole

Эта команда создаёт болванку-заготовку для middleware /app/Http/Middleware/CheckAdminRole.php , в котором надо подправить метод handle

    public function handle($request, Closure $next)
    {
        if(!Auth::check() || Auth::user()->role != 'admin'){

            return redirect('/home');

        }

        return $next($request);
    }

Всё. Midleware готов. Дальше его надо зарегистрировать в файле /app/Http/Kernel.php

    protected $routeMiddleware = [
        ......................
	'checkadminrole' => \App\Http\Middleware\CheckAdminRole::class,      

Остаётся его подключить и использовать. Например в роутах:

Route::group(['middleware' => 'checkadminrole'], function () {     

	Route::get('/admin', function () {
		return "Тут админка";
	});
});

Всё работает!

Ну надо добавить, что для каждой роли нужен свой Middleware. Вот собственно и весь ACL