Rails pundit gem,允许查看非登录用户

时间:2017-10-06 08:38:00

标签: ruby-on-rails ruby ruby-on-rails-4 pundit

我正在使用pundit gem以向三个不同的用户(管理员,卖家,查看者)授予权限。目前我一切正常,管理员可以访问所有内容,卖家可以访问自己的产品,查看者可以查看产品。

我遇到的唯一问题是我希望非signed_up / signed_in用户能够通过搜索结果查看产品。现在,非sign_up / signed_in用户可以看到搜索结果,但无法访问节目视图。

以下是我的设置:

class ItemPolicy < ApplicationPolicy
  attr_reader :item

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

  def update?
    @user.is_a?(Admin) ? item.all : @user.items
  end

  def index?
    @user.is_a?(Admin) ? item.all : @user.items
  end

  def show?
    @user.is_a?(Admin) ? item.all : @user.items
  end

  def create?
   @user.is_a?(Admin) ? item.all : @user.items
  end

  def new?
    @user.is_a?(Admin) ? item.all : @user.items
  end

  def edit?
    @user.is_a?(Admin) ? item.all : @user.items
  end

  def destroy?
    @user.is_a?(Admin) ? item.all : @user.items
  end

  class Scope < Struct.new(:user, :scope)
    def resolve
      if user.is_a?(Admin)
        scope.where(:parent_id => nil)
      elsif user.is_a?(Seller)
        scope.where(:id => user.items)
      end
    end

    def show?
      return true if user.is_a?(Admin)
      return true if user.seller_id == seller.id && user.is_a?(Seller)
     false
    end
  end
end

控制器:

class ItemsController < ApplicationController
  before_action :set_item, only: [:show, :edit, :update, :destroy]


  def index
    authorize Item
    @items = policy_scope(Item)
  end

  def search
    if params[:term]
      @items = Item.search(params[:term]).order("created_at DESC")
    else
      @items = []
    end
  end


  def show
    @comments = Comment.where(item_id: @item).order("created_at DESC")
    @items = policy_scope(Item).find(params[:id])
    authorize @item
  end


  def new
    @item = Item.new
    authorize @item
    @categories = Category.order(:name)
  end


  def edit
   authorize @item
   @categories = Category.order(:name)
  end


  def create
    @item = Item.new(item_params)
    authorize @item

    respond_to do |format|
       if @item.save
        format.html { redirect_to @item, notice: 'Item was successfully created.' }
        format.json { render :show, status: :created, location: @item }
       else
        format.html { render :new }
        format.json { render json: @item.errors, status: :unprocessable_entity }
      end
    end
  end


  def update
    authorize @item
    respond_to do |format|
      if @item.update(item_params)
        format.html { redirect_to @item, notice: 'Item was successfully updated.' }
        format.json { render :show, status: :ok, location: @item }
      else
        format.html { render :edit }
        format.json { render json: @item.errors, status: :unprocessable_entity }
      end
    end
  end


   def destroy
    authorize @item
    @item.destroy
     respond_to do |format|
       format.html { redirect_to items_url, notice: 'Item was successfully destroyed.' }
       format.json { head :no_content }
     end
  end

  private
    def set_item
      @item = Item.find(params[:id])
      authorize @item
    end


    def item_params
      params.require(:item).permit(:title, :description, :image, :price, :category_id)
    end
end

application_contoller.rb

  class ApplicationController < ActionController::Base
  include Pundit
  protect_from_forgery prepend: true

  rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized

  def pundit_user
    current_seller || current_admin || current_viewer
  end

  private

  def user_not_authorized(exception)
    policy_name = exception.policy.class.to_s.underscore
    flash[:warning] = t "#{policy_name}.#{exception.query}", scope: "pundit", default: :default
    redirect_to(request.referrer || root_path)
  end
end

更新1

class ApplicationPolicy
  attr_reader :user, :record

  def initialize(user, record)
    raise Pundit::NotAuthorizedError, "must be logged in" unless user
    @user = user
    @record = record
  end

  def index?
    true # anybody can view
  end

  def show?
    true # anybody can view
  end

  def search?
    index?
  end

  def new?
    create?
  end

  def edit?
    update?
  end

  def destroy?
    update?
  end

  private
  # don't repeat yourself
  def admin?
    user.is_a?(Admin)
  end

  def seller?
    user.is_a?(Seller)
  end
end


class ItemPolicy < ApplicationPolicy
  attr_reader :item

  class Scope < Struct.new(:user, :scope)
    def resolve
      if admin?
        scope.where(parent_id: nil)
      elsif seller?
        # avoids a query for user.items
        scope.where(seller: user)
      end
    end
  end

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

  def update?
    admin? || is_owner?
  end

  def create?
    # just guessing here
    admin? || seller?
  end

  private

  def is_owner?
    # or whatever the association between the item and its owner is
    item.seller == user
  end
end

1 个答案:

答案 0 :(得分:1)

您在Pundit中混淆范围和授权方法。 new?show?等方法应返回一个布尔值,指示是否允许用户执行操作。

要允许未经授权的用户执行操作,您只需返回true即可。 范围用于缩小用户有权访问的记录。他们只有resolve方法。

class ApplicationPolicy
  # ...

  private
  # don't repeat yourself  
  def admin?
    user.is_a?(Admin)
  end

  def seller?
    user.is_a?(Seller)
  end
end


class ItemPolicy < ApplicationPolicy
  attr_reader :item

  class Scope < Struct.new(:user, :scope)
    def resolve
      if admin?
        scope.where(parent_id: nil)
      elsif seller?
        # avoids a query for user.items
        scope.where(seller: user)
      end
    end
  end

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

  def update?
    admin? || is_owner?
  end

  def index?
    true # anybody can view
  end

  def show?
    true # anybody can view
  end

  def search?
    index?
  end

  def create?
    # just guessing here
    admin? || seller?
  end

  def new?
    create?
  end

  def edit?
    update?
  end

  def destroy?
    update?
  end

  private

  def is_owner?
     # or whatever the association between the item and its owner is
     item.seller == user 
  end
end

您可以快速执行许多操作,而不是重复自己(条件),因为编辑权限与更新相同。您甚至可以在ApplicationPolicy中执行此操作,这样您就不必在每个策略类中重复它:

class ApplicationPolicy
  # ...

  def index?
    true # anybody can view
  end

  def show?
    true # anybody can view
  end

  def search?
    index?
  end

  def new?
    create?
  end

  def edit?
    update?
  end

  def destroy?
    update?
  end

  private
  # don't repeat yourself  
  def admin?
    user.is_a?(Admin)
  end

  def seller?
    user.is_a?(Seller)
  end
end

class ItemPolicy < ApplicationPolicy
  attr_reader :item

  class Scope < Struct.new(:user, :scope)
    def resolve
      if admin?
        scope.where(parent_id: nil)
      elsif seller?
        # avoids a query for user.items
        scope.where(seller: user)
      end
    end
  end

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

  def update?
    admin? || is_owner?
  end

  def create?
    # just guessing here
    admin? || seller?
  end

  private

  def is_owner?
     # or whatever the association between the item and its owner is
     item.seller == user 
  end
end

您还在控制器的许多位置对用户进行了两次授权,就像set_item回调已执行的那样:

class ItemsController < ApplicationController
  before_action :set_item, only: [:show, :edit, :update, :destroy]

  def index
    authorize Item
    @items = policy_scope(Item)
  end

  def search
    if params[:term]
      @items = Item.search(params[:term]).order("created_at DESC")
    else
      @items = Item.none # Don't use [] as @items.none? for example would blow up.
    end
  end


  def show
    @comments = Comment.where(item_id: @item).order("created_at DESC")
    authorize @item
  end


  def new
    @item = authorize(Item.new)
    @categories = Category.order(:name)
  end


  def edit
    @categories = Category.order(:name)
  end


  def create
    @item = authorize(Item.new(item_params))

    respond_to do |format|
      if @item.save
        format.html { redirect_to @item, notice: 'Item was successfully created.' }
        format.json { render :show, status: :created, location: @item }
      else
        format.html { render :new }
        format.json { render json: @item.errors, status: :unprocessable_entity }
      end
    end
  end

  def update
    respond_to do |format|
      if @item.update(item_params)
        format.html { redirect_to @item, notice: 'Item was successfully updated.' }
        format.json { render :show, status: :ok, location: @item }
      else
        format.html { render :edit }
        format.json { render json: @item.errors, status: :unprocessable_entity }
      end
    end
  end


  def destroy
    @item.destroy
    respond_to do |format|
      format.html { redirect_to items_url, notice: 'Item was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    def set_item
      @item = authorize( Item.find(params[:id]) )
    end


    def item_params
      params.require(:item).permit(:title, :description, :image, :price, :category_id)
    end
end
相关问题