Как передать дополнительный контекст областям Pundit? - PullRequest
0 голосов
/ 09 мая 2018

У меня есть система, где User может быть связано со многими Portals, однако права пользователя могут отличаться для разных порталов.

Например, пользователь может видеть неопубликованные сообщения на одном портале, но не на другом портале.

Для таких методов, как show?, я могу отключить портал от записи.

def show?
  portal = record.portal
  # logic to check whether, for this particular portal, 
  # this user has permission to view this record
end

Однако это решение не работает для областей действия политики.

Есть ли какой-нибудь способ, скажем, передать на портале метод policy_scope в контроллере?


Одним из решений, которое я видел повсюду, является установка (временного) атрибута для пользователя, чтобы методы политики могли его использовать. например,

# model
class User < ActiveRecord::Base
  attr_accessor :current_portal
  ...
end

# controller
posts = portal.posts
current_user.current_portal = current_portal
policy_scope posts

# policy Scope
def resolve
  portal = user.current_portal
  # logic to scope these records by user's portal permissions
end

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

У кого-нибудь есть предложения?

1 Ответ

0 голосов
/ 09 мая 2018

Классы авторизации Pundit - это старые классы ruby. Используя метод pundit authorize, вы можете передать объект для авторизации и дополнительный запрос. Когда вы, например, используете метод авторизации как

authorize @product, :update?

метод authorize вызовет метод update? класса ProductPolicy. Если запрос не передан, имя действия контроллера +? метка будет установлена ​​как запрос. Взгляните на Определение метода авторизации Pundit .

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

module Pundit
  def authorize(record, query=nil, options=nil)
    query ||= params[:action].to_s + "?"

    @_pundit_policy_authorized = true

    policy = policy(record)

    if options.nil?
      raise NotAuthorizedError, query: query, record: record, policy: policy unless policy.public_send(query)
    else
      raise NotAuthorizedError, query: query, record: record, policy: policy unless policy.public_send(query, options)
    end
  end
end

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

class ProductPolicy
  ...
  def update?(options)
    ...
  end

  def new?
    ...
  end
  ...
end

Как видите, у вас могут быть методы политики, которые принимают аргумент дополнительных опций.

Затем вы можете использовать метод authorize в ваших контроллерах одним из следующих способов:

authorize @product
authorize @product, nil, { owner: User.find(1) }
authorize @product, :some_method?, { owner: User.find(1) }
authorize @product, :some_method?
...