Для того, чтобы установить cancancan в свой проект нужно сделать следующее:
- gem 'cancancan', '~> 1.10' в Gamefile
- rails g cancan:ability в cmd (командной строке) проекта. Это создаст файл ability.rb в _..\app\models_
class Ability
include CanCan::Ability
def initialize(user)
end
end
Далее мы пишем load_and_authorize_resource во всех RESTfulных контроллерах, либо в одном контроллере, а остальные (в том числе и НЕ RESTfulные) просто наследуем от него: https://github.com/CanCanCommunity/cancancan/wiki/Non-RESTful-Controllers
class AccountBaseController < ApplicationController
before_filter :authenticate_user! # для Devise
load_and_authorize_resource # for cancancan
end
# RESTful controllers
class OrdersController < AccountBaseController
код контроллера
end
# NOT RESTful controllers
class CatalogsController < CatalogBaseController
skip_load_and_authorize_resource # эта запись должна быть в каждом НЕ рестфульном контроллере
before_action :authorize_not_restful
def authorize_not_restful # этот метод проводит авторизацию каждого экшена перед которым он вызывается
authorize! action_name.to_sym, controller_name.to_sym # action_name, controller_name - имя текущего метода и контроллера (берется из рута) соответственно
end
def index
some code
end
def print
some code
end
end
Т.к. у нас в проекте возможно МНОГО ролей у одного пользователя, создаем столбец roles_mask в таблице users
rails generate migration add_roles_mask_to_users roles_mask:integer
rake db:migrate
который будет хранить роли (роль) пользователя в целочисленном виде.
Далее пишем в файле ..\app\models\user.rb
class User < ActiveRecord::Base
...your code
ROLES = %i[admin manager receiver] # массив символов [:admin, :manager, :receiver]
def roles=(roles) # ждет на вход массив ролей, например ["receiver"] или ["receiver", "manager"], etc
roles = [*roles].map { |r| r.to_sym }
self.roles_mask = (roles & ROLES).map { |r| 2**ROLES.index(r) }.inject(0, :+)
end
def roles # возвращает массив ролей, например [:receiver] или [:receiver, :manager], etc
ROLES.reject do |r|
((roles_mask.to_i || 0) & 2**ROLES.index(r)).zero?
end
end
def has_role?(role) # ждет на вход символ, например :admin
roles.include?(role)
end
end
...your code
В константу ROLES мы заносим роли которые будут у нас в проекте.
Метод has_role? возвращает true или false при наличии/отсутствии данной роли у юзера.
Методы roles=(some parameter) и roles дают нам возможность преобразовывать массив ролей юзера в целочисленные значения.
@item1 = Users.find(3) # находим юзера с id 3
@item1.roles=["receiver"] # присваиваем ему роль/роли
@item1.save # сохраняем в базе данных
@item1.roles # => [:receiver] - возвращает массив роли/ролей
@item1.roles_mask # => 4 - целочисленное значение роли/ролей в БД
Затем пишем в файле ..\app\models\ability.rb возможности для каждой из ролей для RESTfulных и NOT RESTfulных контроллеров
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.has_role? :admin
can :manage, :all # for RESTful controllers
elsif user.has_role? :manager
can :manage, [Client, Supplier] # for RESTful controllers
elsif user.has_role? :receiver
can [:read, :invoices, :print, :operations], Order # for RESTful controllers
can [:read], [Doc, Invoice] # for RESTful controllers
can [:read], [User] # for RESTful controllers
can :index, :catalogs # for NOT RESTFUL controllers !!!
elsif user.has_role? :booker
can :print, :catalog # for NOT RESTFUL controllers !!!
end
end
Разберем случай RESTfulных контроллеров can [:read, :invoices, :print, :operations], Order:
- can - метод cancancan гема, который разрешает читать [:read] или изменять [:manage] записи на страницах, которые обслуживаются RESTfulными и NOT RESTfulными методами
- [.., :invoices, :print, :operations] - названия НЕ RESTfulных экшенов в RESTfulных контроллерах
- , Order - имена модели/моделей для RESTfulных контроллеров
Разберем случай NOT RESTfulных контроллеров can :index, :catalogs
- can - см.выше
- :index - имя метода, во вьюхе которого мы разрешаем смотреть/изменять данные
- :catalogs - имя контроллера, в котором мы разрешаем работать с методом (в нашем случае :index)