通过关系有许多人的权威政策范围

时间:2014-12-02 17:30:25

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

首先发布在这里,所以我希望它有意义,但我已经在这上面旋转了很长时间。 首先是一个小环境。我正在构建一个包含3个模型的Wiki应用程序:用户,Wiki和协作(连接表)。

我正在使用Devise和Pundit,并且有4类用户可以根据他们的状态查看不同的wiki子集。

以下是规则:

  • 公共用户(未登录) - 应仅查看(不编辑)公共维基 (:hide => false)
  • 经过身份验证的用户(:role =>"标准") - 应该 查看,编辑&仅删除公共维基。
  • 高级用户(:role => " premium") - 查看,编辑,删除公共维基和创建的能力 private wikis(:hide => true)并将协作者添加到他们的私人 wiki,它给了他们对该维基的编辑权。
  • Admin(:role =>" admin")完全控制所有记录。

因此,我的Wiki索引视图中的策略范围(检查用户状态)中有一个冗长的scope.joins条件,以根据角色为current_user提供wiki列表的子集。

不断吐出这个错误:

Started POST "/__better_errors/59802a57d82fd17e/variables" for 127.0.0.1 at 2014-12-02 09:05:41 -0800
  Wiki Load (0.4ms)  SELECT "wikis".* FROM "wikis" INNER JOIN "collaborations" ON "collaborations"."wiki_id" = "wikis"."id" WHERE (hide = 'f' or user_id = 2 or collaborations.user_id = 2)
SQLite3::SQLException: ambiguous column name: user_id: SELECT "wikis".* FROM "wikis" INNER JOIN "collaborations" ON "collaborations"."wiki_id" = "wikis"."id" WHERE (hide = 'f' or user_id = 2 or collaborations.user_id = 2)  

这是政策

class WikiPolicy < ApplicationPolicy

  # What collections a user can see users `.where` 
  class Scope < Scope

    def resolve
      if user && user.role == 'admin'
        scope.all 
      elsif user
        scope.joins(:collaborations).where("hide = :hide or user_id = :owner_id or collaborations.user_id = :collaborator_id", {hide: false, owner_id: user.id, collaborator_id: user.id})
      else 
        scope.where hide: false
      end
    end
  end


  #Policies are boolean logic true or false to determine if a user has access to a controller action.
  def update?
    (user && user.role == 'admin') || (user && record.users.pluck(:id).include?(user.id)) || (user && user.id == record.owner.id )
  end

  def show?
    (user.present? && user.admin?) or not record.hide?
  end

  def premium?
    user.admin? or user.premium?
  end

  def edit?

  end

end      

我的模特

class Wiki < ActiveRecord::Base
  belongs_to :user
  has_many :collaborations, dependent: :destroy
  has_many :users, through: :collaborations

  def owner
    user
  end

  def collaborators
    users
  end

  validates :title, length: { minimum: 5 }, presence: true
  validates :body, length: { minimum: 20 }, presence: true

end

class User < ActiveRecord::Base
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable, :confirmable
  has_many :wikis
  has_many :collaborations 
  has_many :cowikis, through: :collaborations, source: :wiki

  after_initialize :init

  def admin?
    role == 'admin'
  end

  def premium?
    role == 'premium'
  end

  def standard?
    role == 'standard'
  end

private
  def init
    if self.new_record? && self.role.nil?
      self.role = 'standard'
    end
  end
end

 class Collaboration < ActiveRecord::Base
  belongs_to :user
  belongs_to :wiki
end

Wiki控制器

class WikisController < ApplicationController
  def index
   @wikis = policy_scope(Wiki)  
    # authorize @wikis
  end

  def show
    @wiki = Wiki.find(params[:id])
    authorize @wiki
  end

  def new
    @wiki = Wiki.new
    authorize @wiki
  end

  def create
    @wiki = Wiki.new(wiki_params)
    authorize @wiki
    if @wiki.save
      flash[:notice] = "Post was saved."
      redirect_to @wiki
    else
      flash[:error] = "There was an error saving the post. Please try again."
      render :new
    end
  end

  def edit
    @user = current_user
    @users = User.all
     @wiki = Wiki.find(params[:id])
     authorize @wiki
  end

 def update
   @wiki = Wiki.find(params[:id])
   authorize @wiki
   @wiki.collaborators = params[:wiki][:user_ids]
   if @wiki.update_attributes(wiki_params)
     flash[:notice] = "Post was updated."
     redirect_to @wiki
   else
     flash[:error] = "There was an error saving the post. Please try again."
     render :edit
   end
 end

  def destroy
    @wiki = Wiki.find(params[:id])
    authorize @wiki
    title = @wiki.title

    if @wiki.destroy
      flash[:notice] = "\"#{title}\" was deleted successfully."
      redirect_to wikis_path
    else
      flash[:error] = "There was an error deleting the wiki."
      render :show
    end
  end

  private

   def wiki_params
     params.require(:wiki).permit(:title, :body, :hide)
   end
end

我希望你不会被代码所淹没,但我希望提供尽可能多的信息。

感谢您的帮助!

哦,这很可能是有问题的代码,但我为了上下文包含了其他所有内容。

scope.joins(:collaborations).where("hide = :hide or user_id = :owner_id or collaborations.user_id = :collaborator_id", {hide: false, owner_id: user.id, collaborator_id: user.id})

1 个答案:

答案 0 :(得分:1)

这是错误消息中最有价值的部分:

SQLite3::SQLException: ambiguous column name: user_id

当SQL结果集具有多个具有相同名称的列时,可能会发生模糊列名称错误。在这种情况下,列为user_id

user_id列位于维基表和协作表上。这两个表都用在SQL查询中。

以下是导致问题的SQL代码段:

# DB can't figure out which table "user_id = 2" refers to:
WHERE (hide = 'f' or user_id = 2 or collaborations.user_id = 2)

我们需要在user_id列上指定一个表名前缀。

要执行此操作,请尝试在WikiPolicy中更改此行:

scope.joins(:collaborations)
    .where("hide = :hide or user_id = :owner_id or collaborations.user_id = :collaborator_id", {hide: false, owner_id: user.id, collaborator_id: user.id})

为:

scope.joins(:collaborations)
    .where("hide = :hide or wikis.user_id = :owner_id or collaborations.user_id = :collaborator_id", {hide: false, owner_id: user.id, collaborator_id: user.id})