diff --git a/doc/acl/acl_intro.md b/doc/acl/acl_intro.md index 405c466da..e0d8e0c7c 100644 --- a/doc/acl/acl_intro.md +++ b/doc/acl/acl_intro.md @@ -5,7 +5,14 @@ -------------- ACL предназначена для контроля прав пользователя на выполнение действий в -системе доступа к моделям по областям видимости. +системе и доступа к моделям по областям видимости. + +Решаемые задачи +--------------- + +* Проверка наличия у пользователя прав для выполнения метода контроллера; +* Прозрачная фильтрация моделей для исключения невидимых для пользователя + записей. Возможности ----------- @@ -20,15 +27,55 @@ ACL предназначена для контроля прав пользова * Объединение прав `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 | + --------------- + + * Обозначения: <= -- Связь с несколькими моделями + <-,/,| -- Связь с одной моделью -* __Acter__ - модель, которая может выполнять действия, разрешенные ролями, - над другими моделями; -* __Target__ - модель, над которой могут выполняться действия, разрешенные - ролями; Генератор прав -------------- @@ -50,12 +97,12 @@ ACL предназначена для контроля прав пользова * Добавить к таблице моделей поле `visibility:text`; * Добавить `attr_accessible :visibility` в модель; * Создать `scope :by_visibility`, принимающий аргументом массив областей - видимости; + видимости. После выполнения этих действий на странице редактирования роли появится поле выбора областей видимости для этой модели. -Пример: +### Пример: model VisibilitiesExample < ActiveRecord::Base VISIBILITIES = ['open', 'hidden', 'open_for_admins'] @@ -64,6 +111,8 @@ ACL предназначена для контроля прав пользова scope :by_visibility, lambda {|v| {:conditions => ['visibility in (?)', v]}} end +*Назначение методов описано в API* + Задание типа модели ------------------- *Этот функционал скорее всего будет изменяться* @@ -82,7 +131,7 @@ ACL предназначена для контроля прав пользова которой аргументом присвоить имя/имена связей с моделями, из которых должны браться роли. -Примеры: +### Примеры: * Модель, являющаяся __acter__: @@ -121,6 +170,78 @@ ACL предназначена для контроля прав пользова 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 + + API для работы с ACL -------------------- *Этот функционал скорее всего будет изменяться* @@ -130,23 +251,38 @@ API для работы с ACL *  Методы классов: - `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) + - `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)` -- показывает, может ли текущая модель выполнить действие контроллера над целью + - `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_rights` -- делает редирект назад, если пользователь вообще не может совершить текущее действие - - `roles_to(object)` -- возвращает список ролей current_user-а по отношению к объекту - - `rights_to(object)` -- возвращает список прав current_user-а по отношению к объекту +*Возможно, будут вынесены в хелпер для универсализации системы* + + - `can_perform? (target = :system)` -- может ли `current_user` выполнить + текущее действие + - `check_global_rights` -- делает редирект назад, если пользователь вообще + не может совершить текущее действие + - `roles_to(object)` -- возвращает список ролей `current_user`-а по отношению + к объекту + - `rights_to(object)` -- возвращает список прав `current_user`-а по отношению + к объекту diff --git a/lib/ext/application_controller/base.rb b/lib/ext/application_controller/base.rb index 05cea068a..f0df2edaa 100644 --- a/lib/ext/application_controller/base.rb +++ b/lib/ext/application_controller/base.rb @@ -1,4 +1,4 @@ -class ApplicationController::Base +class ActionController::Base def can_perform? target = :system c = self.controller_name