我不知道我在这里做错了什么,但看起来像是这样。
我使用Pundit进行授权,现在已经用它建立了一些模型。
我有一个只能由管理员创建的类别模型。另外,我也不希望用户看到显示/编辑/销毁视图。我只希望管理员可以访问它。到目前为止一切顺利。
将在下面添加一些代码:
category_policy.rb
class CategoryPolicy < ApplicationPolicy
def index?
user.admin?
end
def create?
user.admin?
end
def show?
user.admin?
end
def new?
user.admin?
end
def update?
return true if user.admin?
end
def destroy?
return true if user.admin?
end
end
categories_controller.rb
class CategoriesController < ApplicationController
layout 'scaffold'
before_action :set_category, only: %i[show edit update destroy]
# GET /categories
def index
@category = Category.all
authorize @category
end
# GET /categories/1
def show
@category = Category.find(params[:id])
authorize @category
end
# GET /categories/new
def new
@category = Category.new
authorize @category
end
# GET /categories/1/edit
def edit
authorize @category
end
# POST /categories
def create
@category = Category.new(category_params)
authorize @category
if @category.save
redirect_to @category, notice: 'Category was successfully created.'
else
render :new
end
end
# PATCH/PUT /categories/1
def update
authorize @category
if @category.update(category_params)
redirect_to @category, notice: 'Category was successfully updated.'
else
render :edit
end
end
# DELETE /categories/1
def destroy
authorize @category
@category.destroy
redirect_to categories_url, notice: 'Category was successfully destroyed.'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_category
@category = Category.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def category_params
params.require(:category).permit(:name)
end
end
application_policy.rb
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
@user = user
@record = record
end
def index?
false
end
def create?
create?
end
def new?
create?
end
def update?
false
end
def edit?
update?
end
def destroy?
false
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
scope.all
end
end
end
我的ApplicationController中已包含Pundit,并且rescue_from Pundit::NotAuthorizedError, with: :forbidden
也已在其中设置。
如果我使用管理员帐户登录,则授权本身有效,我可以访问/ categories / *。如果我注销,则得到以下信息:NoMethodError at /categories
undefined method
admin?'对于nil:NilClass`
在写问题时,我认为我发现了问题-我猜Pundit正在寻找一个nil的用户,因为我没有登录。解决此问题的正确方法是什么样的?
最诚挚的问候
答案 0 :(得分:1)
最常见的方法是从未登录用户无法访问的页面重定向用户。只需在控制器中添加一个先行操作即可:
class CategoriesController < ApplicationController
before_action :redirect_if_not_logged_in
<...>
private
def redirect_if_not_logged_in
redirect_to :home unless current_user
end
end
(我假设您拥有current_user
方法,该方法返回用户实例或nil。请将:home更改为您要重定向用户的位置)
答案 1 :(得分:0)
有多种方法可以实现您想要的。
最明显(但有点脏)和简单明了的方法是在每种情况下检查用户的存在:
user && user.admin?
它不会因nil
错误而失败,因为条件的第二部分将不会执行。但这看起来不太好,对吧?特别是如果您必须将其复制到CategoryPolicy
中的所有方法中。
您可以做的是,通过创建一个User
类来使{{1}响应的GuestUser
类,使Pundit认为您通过了false
}}方法(https://en.wikipedia.org/wiki/Null_object_pattern):
在面向对象的计算机编程中,空对象是没有引用值或具有定义的中性(“空”)行为的对象。空对象设计模式描述了此类对象的用途及其行为(或缺少行为)
并在admin?
为user
时使用它。实际上,它将如下所示:
nil
这样,您将不必更改任何其他代码,因为传递的模型将响应策略(class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
@user = user || GuestUser.new
@record = record
end
# ...
end
class GuestUser
def admin?
false
end
end
)所期望的方法。您可能想在其他位置(不在策略文件中)定义此admin?
,这取决于您是否希望应用程序的其他部分重用该行为。
您还可以继续使用P. Boro答案中的重定向方法。从某种意义上说,它的灵活性较差,但是如果您除了重定向所有未登录的用户之外不需要任何其他功能,则可以完全正常工作。