Laravel. ACL. Загвоздка с логикой работы?

Пишу для личного использования расширение для Laravel для контроля доступа к сайту.
Не использовал никаких готовых решений.
Руководствуюсь логикой.
Сначала опишу то, что придумал, потом - вопрос.

Сомневался к чему привязаться: к URL или к методам в контроллерах. Остановился на методах в контроллерах.

Какие действия может понадобиться защищать: CRUD. Любые действия на сайте попадают под этот набор. В некоторых случаях будет использоваться не весь набор, но тем не менее. Например.

51e3994e5d0e4219b5bf08c35dda7538.PNG

Все действия, которые нужно защитить, будут храниться в таблице разрешений. Хранятся там записи вида ClassName.methodName. Для того, чтобы можно было разрешать пользователю какие-то защищенные действия, их нужно объединить в роли. Эти роли хранятся в таблице ролей. Таблицы Роли и разрешения имеют связь многие-ко-многим. Это позволит создавать как стандартные роли (admin, user и пр.), так и персональные при необходимости (vasya и т.д.). Пользователю можно назначить только роль, но предварительно ее надо создать. Т.к. расширение устанавливается на существующую систему авторизации/регистрации Laravel, то по умолчанию каждому пользователю будет назначаться единственная существующая изначально роль default, которая будет запрещать все действия для пользователя (либо можно настроить ряд общих разрешенных для всех). Т.е., чтобы пользователь имел возможность совершать запрещенные действия, ему нужно создать роль, добавить в эту роль разрешений и назначить эту роль пользователю.
Таким образом, проблема блокировки определенных действий в логике решена.
Для того, чтобы обозначить, что совершение какого-то действия зависит от роли, то достаточно вызвать в нужном RESTfull-методе ACL::hasPermission(). Если пользователь не имеет правло на действие, то отобразится представление с ошибкой. Если имеет, то ничего не произойдет. В метод ничего не придется передавать. Т.к. разрешения в таблице разрешений в видеClassName.methodName, то hasPermission возьмет __CLASS__, __METHOD__ сравнит их с ролями в текущей сессии (при авторизации пользователя роли будут добавляться в сессию, чтобы каждый раз не дергать из БД).

e2a61cc58b2647d4b9e11d81ccb9524f.PNGa81d012c5ee74aa3a728d05e2fd4abb8.PNG

Теперь проблема. Осталось еще одно. Может возникнуть необходимость в зависимости от роли изменять какие-то части view. Привязываться к id ролей или к имени - плохой вариант, т.к. они непостоянны. В любой момент они могут удалиться, измениться. Но вот разрешения относительно постоянны. Каким образом лучше формировать содержимое в зависимости от разрешений пользователя?
  • Вопрос задан
  • 4068 просмотров
Решения вопроса 1
kaluzhanin
@kaluzhanin
Я у себя применял подход, взятый у гема CanCan из RubyOnRails.

Идея такова: определяем trait Ability и добавляем ему к классу User. Сам этот trait имеет метод can, который разрешает/запрещает определенное действие:

<?php

trait Ability
{
    public function can($verb, $noun)
    {
        $class = is_string($noun) ? $noun : get_class($noun);
        if(is_callable([this, "canManage$class"]))
        {
            $this->{"canManage$class"}($verb, $noun);
        } else {
             $this->fallback($verb, $noun);
        }
    }

    public function canManageUser($verb, $noun)
    {
        switch($verb) {
            case 'create':
                return true;
            case 'edit':
                if ($this->isAdmin()) return true;
                if ($noun->id == $this->id)  return true;
                break;
            case 'delete':
            case 'destroy':
                if ($noun->id == $this->id) return false;
                break;
        }
        return $this->fallback($verb, $noun);
    }

    public function fallback($verb, $noun)
    {
        if ($this->isAdmin()) return true;
        return false;
    }

    public function isAdmin()
    {
        return $this->role == self::ADMIN;
    }
}


Таким образом мы изолируем семантику ролей в одном конкретном месте, и определяем разрешения не на уровне класса Сущностей, а на уровне конкретной сущности, что гораздо гибче.

Что касается использования в view, то никаких проблем:
@if(Auth::user() && Auth::user()->can('edit', $page))
<form></form>
@endif
@if(Auth::user() && Auth::user()->can('create', 'Page'))
<a href="#">Добавить страницу</a>
@endif
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
@anatoly60
А чем вы нарисовали схемы? Очень симпатично выглядят
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы