Использование Pundit для роли super_admin с полным доступом

Мне интересно, как лучше/простейший способ предоставить пользователю «super_admin» доступ с помощью драгоценного камня Pundit — или какой самый простой способ предоставить пользователю доступ ко всем действиям контроллера на сайте?

Я понимаю, что могу отредактировать файл политики для каждого контроллера, а затем добавить что-то вроде

def delete?
  user.is_super_admin?
end

... к каждому действию, в каждом контроллере. Но есть ли хоть одно место, где я мог бы это определить?

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

Я уверен, что другим нужно было реализовать эту функцию? Какой самый простой способ? Спасибо!


person FireDragon    schedule 07.05.2015    source источник
comment
может быть, мне следует использовать чистый рубин и добавить что-то подобное в мой application_policy.rb, который мог бы проверить, является ли user.is_super_admin? для всех действий, определенных в классе? stackoverflow.com/a/5515144/580419   -  person FireDragon    schedule 07.05.2015
comment
cancancan gem позволяет это, я не знаю, почему у pundit нет встроенной функции. github. com/varvet/pundit/issues/191   -  person konyak    schedule 16.07.2020


Ответы (1)


Для (на самом деле) минимального раздражения, которое возникает из-за повторного ввода имени метода пару десятков раз в течение жизни проекта, я думаю, что, возможно, не столь очевидная выгода того стоит.

Во-первых, вы можете сэкономить несколько нажатий клавиш, определив метод super? на вашем ApplicationPolicy

def super?
  user.is_super_admin?
end

И тогда ваш delete? и другие методы можно было бы немного упростить.

def delete?
  super? || user.id == record.user_id # super admin or owner of the resource
end

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

Конечно, это может сэкономить вам пару нажатий клавиш, чтобы иметь некоторые метапрограммированные функции для автоматического вызова super?, при этом delete? отображается как

def delete?
  user.id == record.user_id # super admin or owner of the resource
end

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


Обновлять

Помимо мнений о читабельности кода, вот одна реализация, которая делает то, что вам, кажется, нужно. Вы также можете изучить перехват вызовов метода вместо удаления ? из имен методов, как я сделал ниже, чтобы убедиться, что method_missing подбирает их.

#
# Classes
#

User = Struct.new(:id, :name)
Foo  = Struct.new(:id, :title, :user_id)

class SuperUser < User; end

ApplicationPolicy = Struct.new(:user, :record) do

  attr_reader :user, :record

  def initialize(user, record)
    @user   = user
    @record = record
  end

  def method_missing(name)
    raise NoMethodError, "No method `#{name}` for #{self}" if (name =~ /\?$/).nil?

    user.is_a?(SuperUser) || send(name.to_s.sub(/\?$/, ""))
  end

end

class FooPolicy < ApplicationPolicy

  private

    def edit
      user.id == record.user_id
    end

end


#
# Fixtures
#

user = User.new(1, "Deefour")
foo  = Foo.new(1, "A Sample Foo", 1)

super_user = SuperUser.new(2, "FireDragon")


#
# Examples
#

FooPolicy.new(user, foo).edit?       #=> true
FooPolicy.new(super_user, foo).edit? #=> true
person deefour    schedule 08.05.2015
comment
ценю подсказку по определению повторно используемого метода в политике приложения, но в целом я не согласен с тем, что вы потеряете ясность или будете практиковать плохое кодирование, если не поместите «super? || ' в каждом методе, в каждом классе Pundit. Структуры классов Pundit отражают структуру ActionController::Base, и в ней уже есть вспомогательные методы, такие как before_action/after_action. Я уверен, что как программист Rails вы, вероятно, часто используете before_action и не скажете, что это сбивает с толку или плохо программирует для этого. Поэтому я думаю, что лучшим решением было бы отразить эти помощники с помощью Ruby, изучив его сейчас :-) - person FireDragon; 12.05.2015
comment
в моем первоначальном вопросе, хотя я думал о попытке реализовать в одном месте, но согласен, что это заходит слишком далеко. но написание собственного эквивалента 'before_action' для использования один раз в верхней части каждого класса было бы лучшим решением, которое я считаю... сохраняя ясность, позволяя вам не использовать его для действий, если он вам не нужен, и освобождая вы повторяете один и тот же код сто раз во многих файлах... - person FireDragon; 12.05.2015
comment
+0.5 +0.5 за первую часть вашего ответа! - person davegson; 11.08.2015