我正在使用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
答案 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