Rails 5 - Pundit - 对嵌套资源的授权

时间:2017-01-17 23:21:44

标签: ruby-on-rails ruby authorization pundit

我想弄清楚如何在我的Rails 5应用程序中使用Pundit。

我有Proposal,Potential和User的模型。协会是:

提案

has_many :potentials, inverse_of: :proposal
    accepts_nested_attributes_for :potentials, reject_if: :all_blank, allow_destroy: true

belongs_to :user

潜在

belongs_to :proposal, inverse_of: :potentials
  belongs_to :user

用户

has_many :proposals, dependent: :destroy
  has_many :potentials

我有针对这些资源的权威政策。

我目前正在努力弄清楚如何实施规则,以便只有在应用规则时才会显示潜力。

我的潜力在部分保存在我的提案视图文件夹中呈现。它有:

<% @proposal.potentials.each do | pot | %>
        <div class="panel">
            <% if policy(pot).show? %>
                <% if pot.private_comment == true %>
                    <p> <%= render :text => 'CONFIDENTIAL - NOT FOR PUBLIC DISCLOSURE' %></p>
                    <% end %>
                        <p><%=  pot.comment %>
                        </p>
                        <p style = "color: navy; text-align:right"><%= pot.user.full_name %>, <%= pot.user.organisation.title.titleize %></p>

                        <p style="font-style:italic; color: #FFFFFF; float:right"><%= text_for_status(pot)%></p>
                    </div>
                <% end %>
        <% end %>

在我的提案控制器中,显示操作,我有:

    before_action :set_proposal, only: [:show, :edit, :update, :destroy ]

def show
        @potentials = @proposal.potentials
end

private
    # Use callbacks to share common setup or constraints between actions.
    def set_proposal
      @proposal = Proposal.find(params[:id])
      authorize @proposal
    end

在我的潜在政策中,我将show的规则定义为列出的3个标准中的任何一个为真:

class PotentialPolicy < ApplicationPolicy


  def index?
    true
  end

    def show?
true  if record.private_comment != true ||
      if record.private_comment == true && @current_user == record.user_id ||
      if record.private_comment == true && @current_user  == record.proposal.user_id
      else false
      end
      end

  def new?
    true
  end

  def create?
    true
  end

  def edit?
    update?
  end

  def update?
    true if record.user_id == current_user.id
  end

  def destroy?
    false
  end

end

我的期望是,因为我要求在views / proposals / temporary_proposals.html.erb partial(上面)中检查可能的政策并在下面提取,

<% if policy(pot).show? %> 

注意:pot被定义为@ proposal.potential。

我能看到的唯一逻辑错误是当前用户是用户而不是用户ID。但是,如果我追加&#34; .id&#34;到current_user的末尾,我收到一条错误,上面写着&#34; id是nil&#34;。

Pundit将研究该提案的潜在政策,并确定是否显示该记录。

这不对,因为当我保存所有这些并尝试呈现提案节目时,我只能看到:private_comment属性不正确的潜力(但我确实符合第二和第三个合格许可的标准(即我创造了潜力和提议) - 所以我应该能够查看该记录)。

我的申请政策有:

class ApplicationPolicy
  attr_reader :user, :record

  def initialize(user, record)
    @user = user
    @record = record
  end

我理解这是因为我的潜在政策继承自我的申请政策,我应该能够引用@record来表示该政策试图处理的记录。对我来说,这意味着我应该能够将show动作定义为:

def show?
    true  if record.private_comment != true ||
          if record.private_comment == true && @current_user == @record.user ||
          if record.private_comment == true && @current_user  == @record.proposal.user
          else false
          end
    end
  end

但这会产生与上述尝试相同的错误结果。

谁能看到我错在哪里?

1 个答案:

答案 0 :(得分:1)

除非我遗漏了某些内容,否则您的政策中未定义@current_user。如何简化show?方法:

def show?
  return true unless record.private_comment?

  return [ record.user_id, record.proposal.user_id ].include? user.id
end

我认为像你所做的那样建立巨大的条件是一个常见的陷阱,当你想要一个行动的守卫时。

如果您在前面独立考虑最简单的故障或成功的路径,我发现政策方法更容易阅读和写入。通过奇怪的边缘情况来跟踪这些,最后是必要的默认值(通常为false)。

这样可以减少重复(就像您重复评估record.private_comment == true和更干净的代码一样。

在rails模型上也值得指出query methods exist for boolean attributes。这就是您可以record.private_comment?代替record.private_comment == true的方式。