Skip to content

Instantly share code, notes, and snippets.

@elvisgiv
Last active May 31, 2017 13:12
Show Gist options
  • Save elvisgiv/6883ce0f01892cc525ac to your computer and use it in GitHub Desktop.
Save elvisgiv/6883ce0f01892cc525ac to your computer and use it in GitHub Desktop.

Установка Cancancan

Для того, чтобы установить 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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment