17 KiB
КРАТКОЕ ОПИСАНИЕ ACL
Предназначение
ACL предназначена для контроля прав пользователя на выполнение действий в системе и доступа к моделям по областям видимости.
Решаемые задачи
- Проверка наличия у пользователя прав для выполнения метода контроллера;
- Прозрачная фильтрация моделей для исключения невидимых для пользователя записей.
Возможности
- Неограниченное количество моделей, над которыми могут выполняться
действия (
target
); - Неограниченное количество моделей, которые могут выполнять действия над
другими (
acter
); - Геренатор прав основывающийся на структуре приложения (см. далее);
- Неограниченное количество ролей, которые могут назначаться для
acter
и содержать любую комбинацию прав и доступных видимостей; - Объединение прав
acter
-ов на глубину одной модели (см. далее); - Разграничение назначения ролей по классам (не завершено, на данный момент не критично);
- Разграничение ролей на глобальные и локальные (см. далее).
Типы моделей, с которыми взаимодействует ACL
- ActerModel -- модель, которая может выполнять действия, разрешенные ролями, над другими моделями;
- TargetTarget -- модель, над которой могут выполняться действия, разрешенные ролями.
ActerModel может иметь глобальную роль, которая определяет возможность выполнения действий без привязки к конкретному экземпляру TargetModel и неограниченное количество прав по отношению к конкретному экземпляру TargetModel.
TODO: Реализовать дополнение необходимым функционалом моделей, выбранных в качестве ActerModel или TargetModel при декларировании их роли в системе
Схема взаимодействия объектов ACL
Функционал ACL реализуется путем взаимодействия моделей Right, Role, Relation
,
реализующих основной функционал и особых моделей проекта, обозначенных на схеме
как ActerModel
и TargetModel
.
Экземпляры ActerModel и TargetModel связываются посредством модели
Relation
, через которую экземпляр ActerModel получает неограниченное
количество ролей по отношению к экземпляру TargetModel.
Схема связей моделей:
--------------
| ActerModel |
/ --------------
--------- -------- |
| Right | | Role | V
--------- -------- ------------
... <= ... <= | Relation |
--------- -------- ------------
| Right | | Role | |
--------- -------- V
---------------
| TargetModel |
---------------
* Обозначения: <= -- Связь с несколькими моделями
<-,/,| -- Связь с одной моделью
Генератор прав
Генератор ролей является Rake-task-ом и запускается командой
rake rights:generate
.
Желательно запускать после добавления нового метода в контроллер для того, чтобы на этот метод в системе появилось право.
Загрузка ролей из дампа
Загрузку ролей из заранее подготовленного дампа можно произвести двумя способами:
- В консоли, используя Rake task
rake roles:load
, который загрузит в базу роли, описанные вconfig/roles.yml
- Через Web-интерфейс, на странице
/roles
, если у пользователя есть соответствующие права. Для загрузки через Web-интерфейс необходимо выбрать файл в поле выбора вверху страницы и нажать Загрузить.
Получение дампа ролей
Дамп ролей может получить пользователь, имеющий на это права, зайдя на страницу
roles
и нажав кнопку Скачать в YML
.
Задание областей видимости моделей
Этот функционал скорее всего будет изменяться
Если модель должна иметь несколько областей видимости, нужно сделать следующее:
- Добавить в модель константу
VISIBILITIES
, в которой задать названия областей видимости; - Добавить к таблице моделей поле
visibility:text
; - Добавить
attr_accessible :visibility
в модель; - Создать
scope :by_visibility
, принимающий аргументом массив областей видимости.
После выполнения этих действий на странице редактирования роли появится поле выбора областей видимости для этой модели.
Пример:
model VisibilitiesExample < ActiveRecord::Base
VISIBILITIES = ['open', 'hidden', 'open_for_admins']
attr_accessible :visibility
scope :by_visibility, lambda {|v| {:conditions => ['visibility in (?)', v]}}
end
Назначение методов описано в API
Задание типа модели
Этот функционал скорее всего будет изменяться
Если модель должна иметь возможность быть связанной с другими с использованием ролей, необходимо произвести следующие действия:
- Добавить в модель декларацию
relationable
, с аргументом:as
, который может принимать заначения из[:object, :target]
. Если модель будет acter-ом, передается:object
, иначе:target
Пример:relationable :as => :object
- Добавить в модель связь
belongs_to :global_role, :class_name => 'Role'
- Добавить в модель связь с моделью
Relation
- Если модель -- acter и она должна использовать как свои роли, так и
роли из другой модели, необходимо добавить декларацию
inherit_rights_from
которой аргументом присвоить имя/имена связей с моделями, из которых должны браться роли.
Примеры:
-
Модель, являющаяся acter:
class ActerModel < ActiveRecord::Base relationable :as => :object belongs_to :global_role, :class_name => 'Role' has_many :targets, :as => :object, :class_name => 'Relation' end
-
Модель, являющаяся acter и наследующая права другой модели:
class ActerWithInheritableRolesModel < ActiveRecord::Base relationable :as => :object ingerit_rights_from :another_acter_model has_many :another_acters_models belongs_to :global_role, :class_name => 'Role' has_many :targets, :as => :object, :class_name => 'Relation' end
-
Модель, являющаяся target:
class TargetModel < ActiveRecord::Base relationable :as => :target has_many :objects, :as => :target, :class_name => 'Relation' end
-
Модель, являющаяся и acter, и target:
class ActerAndTargetModel < ActiveRecord::Base relationable :as => :object relationable :as => :target belongs_to :global_role, :class_name => 'Role' has_many :targets, :as => :object, :class_name => 'Relation' has_many :objects, :as => :target, :class_name => 'Relation' end
Назначение методов описано в API
Использование ACL в контроллере
Если необходимо ограничить доступ ко всем методам контроллера по глобальной
роли пользователя вне зависимости от текущей модели, необходимо установить
before_filter :check_global_rights
.
В случае, если у пользователя нет прав для выполнения текущего действия, он
будет переотправлен на предыдущую страницу.
Если необходимо проверить, может ли пользователь выполнить конкретное действие,
необходимо в начале этого метода вызвать метод can_perform?
. Если методу
передан параметр, являющийся экземпляром класса TargetModel, метод возвратит
true
, если одна или несколько ролей пользователя над этой моделью позволяет
ему выполнить может выполнить действие и false
в противном случае. Если
необязательный параметр опущен, или в качестве параметра передано :system
,
учитываются только глобальные роли.
Примеры
-
Контроллер, некоторые методы которого доступны для всех:
class StuffController < ApplicationController def index # доступ у всех ... end def show # 'Что-то полезное' выполнится только у тех, чьи роли над # @data позволяют выполнить конкретное действие. @data = Stuff.find(params[:id]) if can_perform? @data #что-то полезное else # сообщаем пользователю, что он не может выполнить действие end end def create # 'Что-то полезное' выполнится только у тех, чьи # глобальные роли позволяют выполнить метод if can_perform? # что-то полезное else # сообщаем пользователю, что он не может выполнить действие end end end
-
Контроллер, доступ к методам которого возможен только при наличии необходимых прав в глобальных ролях:
class StuffController < ApplicationController before_filter :check_global_rights # разрешаем доступ только тем, # чьи роли это позволяют. def index # доступ только у тех, кому это позволяет глобальная роль ... end def show # 'Что-то полезное' выполнится только у тех, чьи роли # над @data это позволяют @data = Stuff.find(params[:id]) if can_perform? @data #что-то полезное else # сообщаем пользователю, что он не может выполнить действие end end end
Использование ACL во view
Используется метод can_perform?
модели, для которой нужно проверить права
доступа. Обычно этой моделью является current_user
.
Примеры:
-
Проверка на возможность выполнения глобального действия:
-if current_user.can_perform?('some_controller', 'some_aciton') %a{:href => controller_action_path}= Some description
-
Проверка на возможность выполнения действия над текущей моделью:
-if current_user.can_perform?('some_controller', 'some_aciton', @data) %a{:href => controller_action_path(@data)}= Some description
API для работы с ACL
Этот функционал скорее всего будет изменяться
Методы потомков ActiveRecord::Base
-
Методы классов:
relationable
-- устанавливает, кем является модель (acter/target)relationable?
-- может ли иметь связь с ролью/ролями с другимиrelation_acters
-- список моделей, которые могут иметь роли по отношению к другим (след. метод)relation_targets
-- список моделей, над которыми могут совершаться действияrelation_acter? (model)
,relation_target? (model)
-- является ли тем или другим -inherit_rights_from (:relation_name | [:relation_names])
-- права из каких связанных моделей наследовать -visible_to (model)
-- все видимые для модели записи, может включаться в цепочку (например, для paginate)
-
Методы инстансов:
add_role_to(acter, role)
-- привязать acter-а с ролью к текущей записиadd_role_on(target, role)
-- привязать текущую модель с рольюroles_to(object)
-- если object == :system, возвращает глобальные роли текущей записи, если передана запись -- то роли текущей модели над записьюrights_to(object)
-- аргументы те же, но возвращается список прав, собранный из всех ролейright_to(controller_name, action)
-- возвращает запись с правом на выполнение действия action в контроллере c именемcontroller_name
can_perform? (controller_name, action, target = :system)
-- показывает, может ли текущая модель выполнить действие контроллера над целью
Методы потомков ActiveController::Base
Возможно, будут вынесены в хелпер для универсализации системы
can_perform? (target = :system)
-- может лиcurrent_user
выполнить текущее действиеcheck_global_access
-- делает редирект назад или на главную, если пользователь вообще не может совершить текущее действиеroles_to(object)
-- возвращает список ролейcurrent_user
-а по отношению к объектуrights_to(object)
-- возвращает список правcurrent_user
-а по отношению к объекту