Pundit: авторизовать определенный ресурс для контроллера с другим именем

Допустим, я использую драгоценный камень Pundit для авторизации. У меня есть следующий контроллер:

class BlogsController < ApplicationController
  before_action :check_authorization
  ...

  private
  def check_authorization
    authorize :blog
  end
end

Пандит смотрит на аргумент, переданный, :blog и, следовательно, делает вывод, что есть BlogPolicy учебный класс. Затем он ищет соответствующее имя действия с вопросительным знаком, например: BlogPolicy#index?,

Если я хочу посмотреть авторизацию на конкретном ресурсе, я бы просто сделал это для этого check_authorization метод:

  def check_authorization
    authorize @blog
  end

Опять без проблем. Пандит смотрит на класс ресурса, Blog, а затем ищет BlogPolicy учебный класс.

  • Пример: приложение может вызвать BlogPolicy#show? метод и передать в этом @blog ресурс.

Вот проблема: что мне делать, когда я хочу авторизоваться на конкретном имени контроллера И авторизоваться на конкретном ресурсе, но этот ресурс не синхронизируется с именем контроллера?

Пример:

class SomeOtherBlogsController < ApplicationController
  before_action :check_authorization
  ...

  private
  def check_authorization
    authorize :some_other_blog, @blog
  end
end

Приведенный выше код не работает, но, надеюсь, он показывает, что я пытаюсь сделать. Давайте представим, что это происходит на SomeOtherBlogsController#show действие.

  • Я пытаюсь найти мудреца SomeOtherBlogPolicy#show? метод,
  • В рамках этого метода политики я хочу проверить авторизационный доступ @blog ресурс, а также.

Надеюсь, проблема очевидна. Поскольку класс ресурса не синхронизируется с именем контроллера, кажется, я не могу сделать выше. Так что если у меня есть один и тот же ресурс, используемый в разных контроллерах, мне не повезло. Я не смог бы проверить авторизацию ресурса в этих различных контекстах контроллера. Разве это невозможно сделать с Pundit?

Обновление:

В контроллере я также попытался напрямую вызвать метод Policy следующим образом:

SomeOtherBlogPolicy.new(current_user, @blog).show?

Тем не менее, называя это подняло Pundit::AuthorizationNotPerformedError, Таким образом, кажется, что больше происходит в этом authorize метод, чем просто вернуться true или же false,

2 ответа

Решение

Вы можете вручную указать класс ресурса для модели:

class Blog
  def self.policy_class
    SomeOtherBlogPolicy
  end
end

К сожалению, при вызове невозможно указать класс политики из контроллера authorize как это реализовано как:

 # Retrieves the policy for the given record, initializing it with the record
  # and current user and finally throwing an error if the user is not
  # authorized to perform the given action.
  #
  # @param record [Object] the object we're checking permissions of
  # @param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`).
  #   If omitted then this defaults to the Rails controller action name.
  # @raise [NotAuthorizedError] if the given query method returned false
  # @return [Object] Always returns the passed object record
  def authorize(record, query = nil)
    query ||= params[:action].to_s + "?"

    @_pundit_policy_authorized = true

    policy = policy(record)

    unless policy.public_send(query)
      raise NotAuthorizedError, query: query, record: record, policy: policy
    end

    record
  end

Это было бы довольно хорошим запросом, хотя функция.

Но, как вы можете видеть из приведенного выше кода, авторизации не так много, и вы можете легко создать свою собственную реализацию - вам просто нужно установить @_pundit_policy_authorized = true чтобы избежать этого надоедливого Pundit::AuthorizationNotPerformedError,

def my_authorize(record, query = nil, policy_class: nil)
  # bail early and defer to default implementation
  return authorize(record, query) unless policy_class
  query ||= params[:action].to_s + "?"
  @_pundit_policy_authorized = true
  policy = policy_class.new(pundit_user, record)
  unless policy.public_send(query)
    raise NotAuthorizedError, query: query, record: record, policy: policy
  end
  record 
end

Использование:

my_authorize(@blog, policy_class: SomeOtherBlogPolicy)

Теперь это встроенная функция в версии Pundit 2.0.0. Документация обновляется следующим:

При необходимости вы можете передать аргумент, чтобы переопределить класс политики. Например:

def create
  @publication = find_publication # assume this method returns any model that behaves like a publication
  # @publication.class => Post
  authorize @publication, policy_class: PublicationPolicy
  @publication.publish!
  redirect_to @publication
end

Можно ли также переопределить класс политики в представлении? Я пробовал это сделать, но получил ошибкуunknown keyword: :policy_class

<% if policy(@publication, policy_class: PublicationPolicy).create? %>
Другие вопросы по тегам