Pundit - Политики не распознаются

Я внедряю pundit и хочу ограничить действия user#edit и user#update только текущим_пользователем.

def edit
  @user = current_user
  authorize(@user)
end

def update
  @user = current_user
  authorise(@user)
  if @user.update_attributes(user_params)
    flash[:success] = "Profile updated"
    redirect_to edit_user_path
  else
    render 'edit'
  end
end

Ниже приводится моя попытка политики, которая (а) не работает и (б) нелогична.

class UserPolicy

  attr_reader :user, :user

  def initialise(user, user)
    @user = user
  end

  def update?
    true
  end

  alias_method :edit?, :update?

end

Теперь я обновил свою UserPolicy, как показано ниже. Я установил действия на false для тестирования, так как все было разрешено:

class UserPolicy < ApplicationPolicy

  def new?
    create?
  end

  def create?
    false
  end

  def edit?
    update?
  end

  def update?
    false
    #user.id == record.id
  end

end

Однако моя политика не признается. После дальнейшего чтения я добавил следующее в свой ApplicationController:

after_filter :verify_authorized, except: :index
after_filter :verify_policy_scoped, only: :index

Когда я теперь перехожу к моему действию user#edit, я получаю:

Pundit::AuthorizationNotPerformedError

person Dercni    schedule 20.08.2015    source источник


Ответы (1)


Во-первых, убедитесь, что у вас есть...

ваше-приложение/приложение/контроллеры/application_controller.rb

class ApplicationController < ActionController::Base
  include Pundit
end

your-app/app/policies/application_policy.rb с разрешениями по умолчанию для обычных действий.

class ApplicationPolicy
  attr_reader :user, :record

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

  def index?
    false
  end

  def show?
    scope.where(:id => record.id).exists?
  end

  def create?
    false
  end

  def new?
    create?
  end

  def update?
    false
  end

  def edit?
    update?
  end

  def destroy?
    false
  end

  def scope
    Pundit.policy_scope!(user, record.class)
  end

  class Scope
    attr_reader :user, :scope

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

    def resolve
      scope
    end
  end

Затем в вашем UserPolicy

ваше-приложение/приложение/policies/section_policy.rb

class UserPolicy < ApplicationPolicy
  def edit?
    user.id == record.id
  end

  def update?
    edit?
  end
end

Таким образом, по умолчанию user будет вашим текущим пользователем, а record будет @user, определенным в действиях редактирования и обновления.

Вам не нужно явно вызывать метод authorize. Pundit знает, что делать с вашим атрибутом @user. Итак, ваш контроллер должен быть:

def edit
  user
end

def update
  if user.update_attributes(user_params)
    flash[:success] = "Profile updated"
    redirect_to edit_user_path
  else
    render 'edit'
  end
end

private

def user
  @user ||= User.find(params[:id])
end

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

person Leantraxxx    schedule 20.08.2015
comment
Большое спасибо. Поэтому в ApplicationPolicy указаны разрешения по умолчанию для действий REST, которые можно переопределить. то есть по умолчанию обновить? является ложным и редактировать? использует обновление? значения по умолчанию. Однако эти значения по умолчанию переопределяются UserPolicy, которая разрешает, является ли current_user владельцем записи? - person Dercni; 21.08.2015
comment
Да, вы переопределяете значения по умолчанию update? и edit? в UserPolicy (UserPolicy наследует от ApplicationPolicy). Использование политики приложения не обязательно, но это хорошая практика. В ваших политиках вы переопределяете только то, что вам нужно. - person Leantraxxx; 21.08.2015
comment
Я не понимаю, как запись устанавливается в ApplicationPolicy. Например, если бы я должен был создать UserPolicy для нового, в этот момент не было бы экземпляра пользователя, поэтому какая запись была бы установлена? - person Dercni; 21.08.2015
comment
У вас всегда есть @user. В вашем новом действии вы можете иметь @user = User.new - person Leantraxxx; 21.08.2015
comment
Если по какой-то причине (не в случае нового действия) у вас нет @user, вы всегда можете вызвать authorize the_object_you_want, :the_policy_action_you_want? github.com/elabs/pundit#headless-policies - person Leantraxxx; 21.08.2015
comment
Хорошо, и это объясняет, почему индекс является исключением из правила, поскольку это действие класса, тогда как другие действия являются действиями экземпляра. - person Dercni; 21.08.2015
comment
the_object_you_want будет record внутри политики - person Leantraxxx; 21.08.2015
comment
Я предполагаю, что если я не укажу объект, Pundit знает, что в пользовательском контроллере запись должна быть (в)пользователе, а в контроллере статьи запись должна быть (в)статье и т. д. - person Dercni; 21.08.2015
comment
да, именно поэтому я сказал: Вам не нужно явно вызывать метод авторизации. Pundit знает, что делать с вашим атрибутом @user - person Leantraxxx; 21.08.2015
comment
Спасибо за помощь. здорово :) Получил еще один подобный вопрос, но я начну новую тему. - person Dercni; 21.08.2015
comment
Пожалуйста. Вам нужно прочитать об областях, чтобы работать с действиями коллекции, такими как index. github.com/elabs/pundit#scopes - person Leantraxxx; 21.08.2015
comment
Чтобы заставить его работать, мне нужно авторизовать @user в моем действии users#edit? - person Dercni; 21.08.2015