rosa-build/doc/acl/acl_intro.md

17 KiB
Raw Blame History

КРАТКОЕ ОПИСАНИЕ 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-а по отношению к объекту