2011-10-24 18:38:32 +01:00
|
|
|
|
КРАТКОЕ ОПИСАНИЕ ACL
|
|
|
|
|
====================
|
|
|
|
|
|
|
|
|
|
Предназначение
|
|
|
|
|
--------------
|
|
|
|
|
|
|
|
|
|
ACL предназначена для контроля прав пользователя на выполнение действий в
|
2011-10-24 23:07:59 +01:00
|
|
|
|
системе и доступа к моделям по областям видимости.
|
|
|
|
|
|
|
|
|
|
Решаемые задачи
|
|
|
|
|
---------------
|
|
|
|
|
|
|
|
|
|
* Проверка наличия у пользователя прав для выполнения метода контроллера;
|
|
|
|
|
* Прозрачная фильтрация моделей для исключения невидимых для пользователя
|
|
|
|
|
записей.
|
2011-10-24 18:38:32 +01:00
|
|
|
|
|
|
|
|
|
Возможности
|
|
|
|
|
-----------
|
|
|
|
|
|
|
|
|
|
* Неограниченное количество моделей, над которыми могут выполняться
|
|
|
|
|
действия (`target`);
|
|
|
|
|
* Неограниченное количество моделей, которые могут выполнять действия над
|
|
|
|
|
другими (`acter`);
|
|
|
|
|
* Геренатор прав основывающийся на структуре приложения (см. далее);
|
|
|
|
|
* Неограниченное количество ролей, которые могут назначаться для `acter` и
|
|
|
|
|
содержать любую комбинацию прав и доступных видимостей;
|
|
|
|
|
* Объединение прав `acter`-ов на глубину одной модели (см. далее);
|
|
|
|
|
* Разграничение назначения ролей по классам (не завершено, на данный
|
|
|
|
|
момент не критично);
|
2011-10-24 23:07:59 +01:00
|
|
|
|
* Разграничение ролей на глобальные и локальные (см. далее).
|
|
|
|
|
|
|
|
|
|
Типы моделей, с которыми взаимодействует ACL
|
|
|
|
|
--------------------------------------------
|
|
|
|
|
|
|
|
|
|
* __ActerModel__ -- модель, которая может выполнять действия, разрешенные
|
|
|
|
|
ролями, над другими моделями;
|
|
|
|
|
* __TargetTarget__ -- модель, над которой могут выполняться действия,
|
|
|
|
|
разрешенные ролями.
|
|
|
|
|
|
|
|
|
|
__ActerModel__ может иметь глобальную роль, которая определяет возможность
|
|
|
|
|
выполнения действий без привязки к конкретному экземпляру __TargetModel__ и
|
|
|
|
|
неограниченное количество прав по отношению к конкретному экземпляру
|
|
|
|
|
__TargetModel__.
|
|
|
|
|
|
|
|
|
|
__TODO__: *Реализовать дополнение необходимым функционалом моделей, выбранных
|
|
|
|
|
в качестве __ActerModel__ или __TargetModel__ при декларировании их роли в
|
|
|
|
|
системе*
|
|
|
|
|
|
|
|
|
|
Схема взаимодействия объектов ACL
|
|
|
|
|
---------------------------------
|
|
|
|
|
|
|
|
|
|
Функционал ACL реализуется путем взаимодействия моделей `Right, Role, Relation`,
|
|
|
|
|
реализующих основной функционал и особых моделей проекта, обозначенных на схеме
|
|
|
|
|
как `ActerModel` и `TargetModel`.
|
|
|
|
|
|
|
|
|
|
Экземпляры __ActerModel__ и __TargetModel__ связываются посредством модели
|
|
|
|
|
`Relation`, через которую экземпляр __ActerModel__ получает неограниченное
|
|
|
|
|
количество ролей по отношению к экземпляру __TargetModel__.
|
|
|
|
|
|
|
|
|
|
### Схема связей моделей:
|
2011-10-24 18:38:32 +01:00
|
|
|
|
|
2011-10-24 23:07:59 +01:00
|
|
|
|
--------------
|
|
|
|
|
| ActerModel |
|
|
|
|
|
/ --------------
|
|
|
|
|
--------- -------- |
|
|
|
|
|
| Right | | Role | V
|
|
|
|
|
--------- -------- ------------
|
|
|
|
|
... <= ... <= | Relation |
|
|
|
|
|
--------- -------- ------------
|
|
|
|
|
| Right | | Role | |
|
|
|
|
|
--------- -------- V
|
|
|
|
|
---------------
|
|
|
|
|
| TargetModel |
|
|
|
|
|
---------------
|
|
|
|
|
|
|
|
|
|
* Обозначения: <= -- Связь с несколькими моделями
|
|
|
|
|
<-,/,| -- Связь с одной моделью
|
2011-10-24 18:38:32 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Генератор прав
|
|
|
|
|
--------------
|
|
|
|
|
|
|
|
|
|
Генератор ролей является Rake-task-ом и запускается командой
|
|
|
|
|
`rake rights:generate`.
|
|
|
|
|
|
|
|
|
|
Желательно запускать после добавления нового метода в контроллер для того,
|
|
|
|
|
чтобы на этот метод в системе появилось право.
|
|
|
|
|
|
2011-10-26 17:50:35 +01:00
|
|
|
|
Загрузка ролей из дампа
|
|
|
|
|
-----------------------
|
|
|
|
|
|
|
|
|
|
Загрузку ролей из заранее подготовленного дампа можно произвести двумя
|
|
|
|
|
способами:
|
|
|
|
|
|
|
|
|
|
* В консоли, используя Rake task `rake roles:load`, который загрузит в базу
|
|
|
|
|
роли, описанные в `config/roles.yml`
|
|
|
|
|
* Через Web-интерфейс, на странице `/roles`, если у пользователя есть
|
|
|
|
|
соответствующие права. Для загрузки через Web-интерфейс необходимо выбрать
|
|
|
|
|
файл в поле выбора вверху страницы и нажать __Загрузить__.
|
|
|
|
|
|
|
|
|
|
Получение дампа ролей
|
|
|
|
|
---------------------
|
|
|
|
|
|
|
|
|
|
Дамп ролей может получить пользователь, имеющий на это права, зайдя на страницу
|
|
|
|
|
`roles` и нажав кнопку `Скачать в YML`.
|
|
|
|
|
|
2011-10-24 18:38:32 +01:00
|
|
|
|
Задание областей видимости моделей
|
|
|
|
|
----------------------------------
|
|
|
|
|
*Этот функционал скорее всего будет изменяться*
|
|
|
|
|
|
|
|
|
|
Если модель должна иметь несколько областей видимости, нужно сделать следующее:
|
|
|
|
|
|
|
|
|
|
* Добавить в модель константу `VISIBILITIES`, в которой задать названия областей
|
|
|
|
|
видимости;
|
|
|
|
|
* Добавить к таблице моделей поле `visibility:text`;
|
|
|
|
|
* Добавить `attr_accessible :visibility` в модель;
|
|
|
|
|
* Создать `scope :by_visibility`, принимающий аргументом массив областей
|
2011-10-24 23:07:59 +01:00
|
|
|
|
видимости.
|
2011-10-24 18:38:32 +01:00
|
|
|
|
|
|
|
|
|
После выполнения этих действий на странице редактирования роли появится поле
|
|
|
|
|
выбора областей видимости для этой модели.
|
|
|
|
|
|
2011-10-24 23:07:59 +01:00
|
|
|
|
### Пример:
|
2011-10-24 18:38:32 +01:00
|
|
|
|
|
|
|
|
|
model VisibilitiesExample < ActiveRecord::Base
|
|
|
|
|
VISIBILITIES = ['open', 'hidden', 'open_for_admins']
|
|
|
|
|
attr_accessible :visibility
|
|
|
|
|
|
|
|
|
|
scope :by_visibility, lambda {|v| {:conditions => ['visibility in (?)', v]}}
|
|
|
|
|
end
|
|
|
|
|
|
2011-10-24 23:07:59 +01:00
|
|
|
|
*Назначение методов описано в API*
|
|
|
|
|
|
2011-10-24 18:38:32 +01:00
|
|
|
|
Задание типа модели
|
|
|
|
|
-------------------
|
|
|
|
|
*Этот функционал скорее всего будет изменяться*
|
|
|
|
|
|
|
|
|
|
Если модель должна иметь возможность быть связанной с другими с использованием
|
|
|
|
|
ролей, необходимо произвести следующие действия:
|
|
|
|
|
|
|
|
|
|
* Добавить в модель декларацию `relationable`, с аргументом `:as`, который
|
|
|
|
|
может принимать заначения из `[:object, :target]`. Если модель будет
|
|
|
|
|
__acter__-ом, передается `:object`, иначе `:target`
|
|
|
|
|
Пример: `relationable :as => :object`
|
|
|
|
|
* Добавить в модель связь `belongs_to :global_role, :class_name => 'Role'`
|
|
|
|
|
* Добавить в модель связь с моделью `Relation`
|
|
|
|
|
* Если модель -- __acter__ и она должна использовать как свои роли, так и
|
|
|
|
|
роли из другой модели, необходимо добавить декларацию `inherit_rights_from`
|
|
|
|
|
которой аргументом присвоить имя/имена связей с моделями, из которых должны
|
|
|
|
|
браться роли.
|
|
|
|
|
|
2011-10-24 23:07:59 +01:00
|
|
|
|
### Примеры:
|
2011-10-24 18:38:32 +01:00
|
|
|
|
|
|
|
|
|
* Модель, являющаяся __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
|
|
|
|
|
|
2011-10-24 23:07:59 +01:00
|
|
|
|
*Назначение методов описано в 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
|
|
|
|
|
|
2011-10-26 17:50:35 +01:00
|
|
|
|
Использование 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
|
2011-10-24 23:07:59 +01:00
|
|
|
|
|
2011-10-24 18:38:32 +01:00
|
|
|
|
API для работы с ACL
|
|
|
|
|
--------------------
|
|
|
|
|
*Этот функционал скорее всего будет изменяться*
|
|
|
|
|
|
|
|
|
|
### Методы потомков `ActiveRecord::Base`
|
|
|
|
|
|
|
|
|
|
* Методы классов:
|
|
|
|
|
- `relationable` -- устанавливает, кем является модель (acter/target)
|
|
|
|
|
- `relationable?` -- может ли иметь связь с ролью/ролями с другими
|
2011-10-24 23:07:59 +01:00
|
|
|
|
- `relation_acters` -- список моделей, которые могут иметь роли
|
|
|
|
|
по отношению к другим (след. метод)
|
|
|
|
|
- `relation_targets` -- список моделей, над которыми могут совершаться
|
|
|
|
|
действия
|
|
|
|
|
- `relation_acter? (model)`, `relation_target? (model)` -- является ли
|
|
|
|
|
тем или другим
|
|
|
|
|
- `inherit_rights_from (:relation_name | [:relation_names])` -- права из
|
|
|
|
|
каких связанных моделей наследовать
|
|
|
|
|
- `visible_to (model)` -- все видимые для модели записи, может
|
|
|
|
|
включаться в цепочку (например, для paginate)
|
2011-10-24 18:38:32 +01:00
|
|
|
|
|
|
|
|
|
* Методы инстансов:
|
|
|
|
|
- `add_role_to(acter, role)` -- привязать acter-а с ролью к текущей записи
|
|
|
|
|
- `add_role_on(target, role)` -- привязать текущую модель с ролью
|
2011-10-24 23:07:59 +01:00
|
|
|
|
- `roles_to(object)` -- если object == :system, возвращает глобальные роли
|
|
|
|
|
текущей записи, если передана запись -- то роли текущей модели над записью
|
|
|
|
|
- `rights_to(object)` -- аргументы те же, но возвращается список прав,
|
|
|
|
|
собранный из всех ролей
|
|
|
|
|
- `right_to(controller_name, action)` -- возвращает запись с правом на
|
|
|
|
|
выполнение действия action в контроллере c именем `controller_name`
|
|
|
|
|
- `can_perform? (controller_name, action, target = :system)` -- показывает,
|
|
|
|
|
может ли текущая модель выполнить действие контроллера над целью
|
2011-10-24 18:38:32 +01:00
|
|
|
|
|
|
|
|
|
### Методы потомков `ActiveController::Base`
|
2011-10-24 23:07:59 +01:00
|
|
|
|
*Возможно, будут вынесены в хелпер для универсализации системы*
|
|
|
|
|
|
|
|
|
|
- `can_perform? (target = :system)` -- может ли `current_user` выполнить
|
|
|
|
|
текущее действие
|
|
|
|
|
- `check_global_rights` -- делает редирект назад, если пользователь вообще
|
|
|
|
|
не может совершить текущее действие
|
|
|
|
|
- `roles_to(object)` -- возвращает список ролей `current_user`-а по отношению
|
|
|
|
|
к объекту
|
|
|
|
|
- `rights_to(object)` -- возвращает список прав `current_user`-а по отношению
|
|
|
|
|
к объекту
|
2011-10-24 18:38:32 +01:00
|
|
|
|
|