我正在使用Pundit gem(使用Devise和Rolify)来限制基于登录用户角色的信息访问。
此时我的用户模型定义了三个角色:管理员,客户管理员和客户管理员。
用户属于客户。 客户有很多用户。
我在索引客户模型时成功实施了Pundit政策。管理员和客户管理员可以查看所有客户。客户管理员只能看到他们的OWN记录。
当我尝试限制Customer控制器的 show 方法时,问题就在于此。管理员和客户管理员可以查看所有客户。但是,客户管理员应该只能看到自己的记录。 但是现在,客户管理员可以在URL中输入任何ID并查看任何客户记录。
我对范围界定模糊。我的理解是,政策方法(即索引?和显示?)是限制世界卫生组织可以执行这些行动,而范围界定方法限制可以获得哪些记录。我在编写上述场景的正确范围时遇到了麻烦。
以下是客户控制人员:
class CustomersController < ApplicationController
before_action :set_customer, only: [:show, :edit, :update, :destroy]
after_action :verify_authorized
# GET /customers
# GET /customers.json
def index
@customers = policy_scope(Customer)
authorize Customer
end
# GET /customers/1
# GET /customers/1.json
def show
authorize @customer
end
# GET /customers/new
def new
@customer = Customer.new
authorize @customer
end
# GET /customers/1/edit
def edit
authorize @customer
end
# POST /customers
# POST /customers.json
def create
@customer = Customer.new(customer_params)
authorize @customer
respond_to do |format|
if @customer.save
format.html { redirect_to @customer, notice: 'Customer was successfully created.' }
format.json { render :show, status: :created, location: @customer }
else
format.html { render :new }
format.json { render json: @customer.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /customers/1
# PATCH/PUT /customers/1.json
def update
authorize @customer
respond_to do |format|
if @customer.update(customer_params)
format.html { redirect_to @customer, notice: 'Customer was successfully updated.' }
format.json { render :show, status: :ok, location: @customer }
else
format.html { render :edit }
format.json { render json: @customer.errors, status: :unprocessable_entity }
end
end
end
# DELETE /customers/1
# DELETE /customers/1.json
def destroy
authorize @customer
@customer.destroy
respond_to do |format|
format.html { redirect_to customers_url, notice: 'Customer was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_customer
@customer = Customer.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def customer_params
params.require(:customer).permit(:name, :parent_customer_id, :customer_type, :active, :currency)
end
end
以下是客户政策:
class CustomerPolicy < ApplicationPolicy
def index?
# Admins, ClientAdmins, and CustomerAdmins can index customers (see Scope class for filters)
@user.has_role? :admin or @user.has_role? :client_admin or @user.has_role? :customer_admin
end
def show?
# Admins, ClientAdmins, and CustomerAdmins can see any customer details
@user.has_role? :admin or @user.has_role? :client_admin or @user.has_role? :customer_admin
end
def update?
# Only Admins and ClientAdmins can update customer details
@user.has_role? :admin or @user.has_role? :client_admin
end
def destroy?
@user.has_role? :admin or @user.has_role? :client_admin
end
class Scope < Struct.new(:user, :scope)
def resolve
if (user.has_role? :admin or user.has_role? :client_admin)
# Admins and ClientAdmins can see all Customers
scope.where(:parent_id => nil)
elsif user.has_role? :customer_admin
# Customer Admins can only see their own Customer
scope.where(:id => user.customer) # THIS DOES NOT APPEAR TO GET INVOKED BY THE SHOW METHOD OF THE CONTROLLER
end
end
def show?
# NOT SURE WHAT TO PUT IN HERE
end
end
end
成功!!感谢railscard给我的启动,诀窍是修改节目?客户策略文件中的方法如下所示:
def show?
# Admins, ClientAdmins, and CustomerAdmins can see any customer details
# Students cannot see customer details
return true if user.has_role?(:admin) || user.has_role?(:client_admin)
return true if user.customer_id == @record.id && user.has_role?(:customer_admin)
false
end
请注意,我必须使用@record实例变量,因为它是应用程序策略类用来引用authorize方法传入的记录的内容。
谢谢!
答案 0 :(得分:7)
为了让Pundit的范围适用于show
行动,可以使用Pundit的policy_scope
助手(或policy_scope!
),或您可以从生成的show?
继承ApplicationPolicy
。
index
操作已正确使用policy_scope
,我们只需对show
操作执行类似的操作。以下是一些选项:
选项1:将show
操作修改为
def show
# Also remove :show from the :only option where
# before_action :set_customer, only: ... is called.
@customer = policy_scope(Customer).find(params[:id])
authorize @customer
end
或强>
选项2:将set_customer
修改为
def set_customer
@customer = policy_scope(Customer).find(params[:id])
end
或强>
选项3:修改CustomerPolicy #show?至强>
def show?
# scope call here will return the
# result of CustomerPolicy::Scope#resolve
# This is the same implementation generated
# in the default ApplicationPolicy so you could
# just delete this method here and inherit instead.
scope.where(:id => record.id).exists?
end
Here's the code生成默认的ApplicationPolicy#show?
方法。
有关其他详细信息,请参阅Scopes上的Pundit自述文件部分。
我认为您可以放心地删除show?
中的空CustomerPolicy::Scope
方法,我不相信它会被调用。
答案 1 :(得分:5)
我认为您不需要范围来限制show
操作的访问权限。
def show?
return true if user.has_role? :admin || user.has_role? :client_admin
return true if user.customer_id == customer.id && user.has_role? :customer_admin
false
end
Pundit范围通常用于获取用户有权访问的记录列表。如果是show
方法(或控制器中的任何其他方法,您调用authorize
)Pundit使用当前用户和给定客户实例化策略类,然后只需调用show?
方法来检查用户权限,即CustomerPolicy.new(current_user, @customer).show?