Pundit 授权当前用户

Pundit authorizing current user

我的创建、更新和销毁政策"likes" 要求用户登录。

我对政策的措辞如下:

class LikePolicy < ApplicationPolicy
  def create?
    user && record.user_id == user.id
  end

  def update?
    create?
  end

  def destroy?
    create?
  end
end

我喜欢的#create controller动作如下:

def create
  @article = Article.find(params[:article_id])
  @like = @article.likes.new(user_id: current_user.id, value: params[:value] == 1 : 1 ? -1)
  authorize(@like)
  @like.save
  redirect_to @article
end

这是可行的,但是使用策略来确认用户已登录是不合逻辑的,因为代码将在之前的代码中失败,例如引用 current_user 的地方。

这是关于如何授权包含 user_id 的记录的公认做法吗?

通常我会通过单独的身份验证检查来检查用户是否已登录。如果您的应用程序主要需要用户登录,那么在授权之前进行身份验证检查将确保您始终拥有 current_user.

另外,对于创建,我发现我不喜欢调用 new/authorize/save 只是为了确保用户正在创建属于他们的对象的想法。那是真正与授权无关的应用程序逻辑。对于 create,您可以简单地传入 class,Pundit 不介意。

根据您是否先进行身份验证检查,您的授权可以简单地 return 为真,或者您可以检查 !user.nil.

我发现对 Pundit 有帮助的最后一件事是尝试将逻辑抽象为某种自我记录方法。它使阅读专家策略更容易,还允许您将逻辑片段抽象到 ApplicationPolicy 或通过模块包含它们。在下面的示例中,is_owner? 可以很容易地抽象出来,因为它在许多情况下都可用。

示例 1:先验证再授权

class LikesController
  # Either from devise, or define in ApplicationController
  before_action :authenticate_user!

  def create
    authorize(Like)
    article = Article.find(params[:article_id])
    article.likes.create(user_id: current_user.id, ...)
    redirect_to article
  end
end

class LikePolicy < ApplicationPolicy
  def create?
    true
  end

  def update?
    is_owner?
  end

  def destroy?
    is_owner?
  end

  private

  def is_owner?
    user == record.user
  end
end

示例 2:仅授权

class LikesController
  def create
    authorize(Like)
    article = Article.find(params[:article_id])
    article.likes.create(user_id: current_user.id, ...)
    redirect_to article
  end
end

class LikePolicy < ApplicationPolicy
  def create?
    !user.nil?
  end

  def update?
    is_owner?
  end

  def destroy?
    is_owner?
  end

  private

  def is_owner?
    user && user == record.user
  end
end