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? %>