Вы когда-нибудь задумывались, есть ли какой-нибудь простой способ ограничить некоторые базовые функции и возможности пользователям, которые соответствуют определенным критериям или чему-то в этом роде? Что ж, не беспокойтесь, вы можете использовать гем CanCanCan, чтобы сделать пользовательский интерфейс вашего приложения rails очень гибким. Используя CanCanCan, мы можем определять роли и разрешения для пользователя, с помощью которых мы можем ограничить опыт определенных типов пользователей, а также дает нам уровень безопасности.

CanCanCan Разрешения и возможности:

Начнем с основ,

Класс Ability содержит все разрешения. Здесь мы определяем все разрешения, которые хотим иметь в нашем приложении. Однако при необходимости мы можем разбить его на несколько классов, мы увидим их позже.

Class Ability
  include CanCan::Ability
def initialize(user)
    can :manage, Blog
  end
end

Метод can используется для определения разрешений и требует двух аргументов. Первое - это действие, для которого вы устанавливаете разрешение, второе - это класс объекта, для которого вы его устанавливаете.

can :manage, Blog  # user can perform any action on the article
can :read, :all       # user can read any object
can :manage, :all  #user has access to everything
can :highlight_pages, Blog   # custom action
can [:add_user, :delete], [Blog, Comment] # multiple actions

Важно знать, как происходит приоритет разрешений, подробнее об этом вы можете узнать здесь.

Используя: all, мы можем представить все классы.

Обычно мы работаем с четырьмя основными действиями, которые мы используем для определения и проверки разрешений. Это не то же самое, что 7 действий RESTful в Rails. CanCanCan автоматически добавляет несколько удобных псевдонимов для сопоставления действий контроллера.

alias_action :index, :show, :to => :read
alias_action :new, :to => :create
alias_action :edit, :to => :update

Мы также можем передать хэш условий, чтобы еще больше ограничить, к каким записям применяется это разрешение.

can :read, Blog, active: true, user_id: user.id

Для этих условий важно использовать только столбцы базы данных, чтобы ее можно было повторно использовать для Извлечения записей. Здесь active и user_id - столбцы таблицы Blog.

Вы можете использовать вложенные хэши для определения условий для ассоциаций. Здесь Блог можно читать только в том случае, если категория, к которой он принадлежит, относится к жанровому образованию.

can :read, Blog, category: { genre: 'education' }

Если ваши условия слишком сложны для определения в хэше, вы можете использовать блок, чтобы определить их в Ruby.

can :update, Blog do |blog|
  blog.view_count > 20
end

Использование определенных разрешений:

Теперь, когда мы рассмотрели основы определения разрешений, давайте посмотрим, как использовать их в нашем приложении.

Вы можете получить список записей, доступных текущему пользователю на основе действия.

@blogs = Blog.accessible_by(current_ability, :update)

Обратите внимание, что разрешения, определенные в блоке, не рассматриваются в приведенном выше коде. Вы получаете список записей блога, которые не прошли проверку view_count ›20. Current_ability - это метод, доступный с помощью CanCanCan в ваших контроллерах. Подробнее об этом можно узнать здесь.

После определения разрешений вы можете использовать метод can? в контроллере или представлении, чтобы проверить разрешение пользователя для данного действия и объекта.

can_manage = can? :manage, @blog

Вы также можете передать класс модели, но если существует блок или хэш условий, они будут проигнорированы при проверке класса, и он вернет true.

Авторизация контроллеров:

Мы можем использовать команду «authorize!» Для действия контроллера, чтобы авторизовать его.

def custom_action
  @blog = Blog.find(params[:blog])
  authorize! :custom_action, @blog
end

Вместо вызова авторизации! при каждом действии контроллера мы можем делать

class BlogsController < ActionController::Base
  load_and_authorize_resource
end

load_and_authorize_resource на самом деле является комбинацией load_resource и authorize_resource

«Load_resource» создаст объект для каждого действия контроллера, которое можно использовать для авторизации, как показано ниже.

@blogs = Blog.accessible_by(current_ability)  # collection routes
@blog = Blog.find(params[:id]) # member routes

«Authorize_resource» будет разрешать каждое действие контроллера на основе загруженного ресурса.

CanCanCan вызовет CanCan::AccessDenied исключение, когда пользователь выполнит действие контроллера, на которое у него нет разрешения.

Если вы хотите больше узнать об этом, вы можете заглянуть здесь.

Проблемы с обслуживанием одного файла capacity.rb:

Когда вы только начали создавать свое приложение с нуля, было бы неплохо просто сохранить все разрешения внутри файла capacity.rb, но как только ваше приложение начнет масштабироваться, вы можете начать замечать некоторые проблемы, такие как

  1. Каждый раз загружаются ненужные разрешения.
  2. Утомительный процесс определения и изменения разрешений

Модульная обработка файла capacity.rb:

Если ваше приложение будет разрастаться, очень важно преобразовать ваш файл capacity.rb в модули. Мы можем разделить capacity.rb на основе каждой модели, учитывая, что у вас есть две модели, а именно Пользователь и Блог.

ability.rb
class Ability
  include CanCan::Ability 
 
  def initialize(user)
    can :manage, :all if user.admin?
    can :edit, User, id: user.id
    can :read, Blog, published: true
    can :edit, Blog, user_id: user.id
  end
end

Мы разделяем права каждой модели на отдельные файлы способностей.

ability.rb
class Ability
  include CanCan::Ability
def initialize(user)
    can :manage, :all if user.admin?
  end
end
user_ability.rb
class UserAbility
  include CanCan::Ability
def initialize(user)
    can :manage, Comment, user_id: user.id
  end
end
blog_ability.rb
class BlogAbility
  include CanCan::Ability
def initialize(user)
    can :read, Blog, published: true
    can :edit, Blog, user_id: user.id
  end
end

Теперь мы можем загрузить только необходимые файлы способностей для конкретного контроллера. По умолчанию каждый контроллер просматривает файл capacity.rb для получения разрешений, однако мы можем переопределить это.

blogs_controller.rb
class BlogsController 
  def current_ability
    @current_ability ||= BlogAbility.new(current_user)
  end
end

Обратите внимание, что важно кэшировать объект способности, чтобы он не воссоздавался каждый раз. Когда ваш контроллер запущен, он будет читать только нужный вам файл способностей, экономя время и память.

Вы также можете объединить два файла способностей вместе.

def current_ability
 @current_ablity ||= 
 UserAbility.new(current_user).merge(Ability.new(current_user))
end

Разрешения в UserAbility будут иметь приоритет над Ability здесь. Хотя согласно официальной документации, будущие версии будут поддерживать это изначально, и переопределение current_ability не потребуется.

Francium Tech - технологическая компания, специализирующаяся на поставке высококачественного масштабируемого программного обеспечения на экстремальных скоростях. Цифры и размер данных нас не пугают. Если у вас есть какие-либо требования или вы хотите бесплатно проверить работоспособность своих систем или архитектуры, напишите письмо по адресу [email protected], мы свяжемся с вами!