【问题标题】:How to restrict foreign keys in Rails' update controller action?如何在 Rails 的更新控制器操作中限制外键?
【发布时间】:2013-09-27 15:28:50
【问题描述】:

在我的 Rails 应用程序中,我有 invoices,而它又可以有很多 projects

型号:

class Invoice < ActiveRecord::Base

  attr_accessible :project_id

end

控制者:

class InvoicesController < ApplicationController

  before_filter :authorized_user, :only => [ :show, :edit, :destroy ]
  before_filter :authorized_project, :only => [ :create, :update ]

  def create # safe
    @invoice = @project.invoices.build(params[:invoice])  
    if @invoice.save
      flash[:success] = "Invoice saved."
      redirect_to edit_invoice_path(@invoice)
    else
      render :new
    end
  end

  def update # not safe yet
    if @invoice.update_attributes(params[:invoice])
      flash[:success] = "Invoice updated."
      redirect_to edit_invoice_path(@invoice)
    else
      render :edit
    end
  end

  private

    def authorized_user
      @invoice = Invoice.find(params[:id])
      redirect_to root_path unless current_user?(@invoice.user)
    end

    def authorized_project
      @project = Project.find(params[:invoice][:project_id])
      redirect_to root_path unless current_user?(@project.user)
    end

end

我最担心的是,有朝一日,恶意用户可能会创建一个属于另一个用户的projectinvoice

现在感谢这个板上的一些人的帮助,我设法提出了一个before_filter,以确保在创建项目时不会发生这种情况。

问题是我不明白如何将此过滤器也应用于update 操作。

由于更新操作没有使用 Rails 的 build 函数,我根本不知道如何在其中获取我的 @project

有人可以帮忙吗?

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-3 activerecord associations


    【解决方案1】:

    在你的情况下,我会从current_user开始,而不是@project(提供Userhas_many :invoices):

    current_user.invoices.build(params[:invoice])
    

    除了显式检查current_user?(@invoice.user),您还可以这样做:

    def find_invoice
      @invoice = current_user.invoices.find(params[:id])
    end
    
    def find_project
      @project = current_user.projects.find(params[:invoice][:project_id])
    end
    

    错误的发票或项目会抛出 500,您可能想处理也可能不想处理。

    如果User has_many :invoices, :through =&gt; :projectsProject 因此has_many :invoices 那么:

    def find_invoice
      @invoice = @project.invoices.find(params[:id])
    end
    

    【讨论】:

    • 不要忘记从此处可能发生的任何 ActiveRecord::RecordNotFound 错误中进行救援。
    • @tadman 在一个简单的情况下,我不关心恶意用户是否获得 500。但通常需要救援(例如,用户可以从陈旧的索引页面访问已删除的发票)。
    • 啊,我想我终于搞定了。我花了很长时间才掌握它。非常感谢您的帮助。
    【解决方案2】:

    @project.invoices.build 方法会创建一个新的Invoice,它会自动与特定的@project 关联。您无需做任何工作,也不存在链接到错误项目的风险。

    不过,您需要确保 project_id 不是可访问属性。

    【讨论】:

    • 我知道。我不担心build 方法。我担心我的update 行动。现在,可以破解它并将project_id 替换为属于另一个用户的project_id。我正在使用选择框在表单中显示用户的project_ids。我无法从可访问中删除project_id,因为用户无法再更改它。
    • 您应该将其从可访问列表中删除,并将其分配到单独的通道中,以验证允许用户进行该分配。 @invoice.project_id = project_id 仅当您确认该项目是他们的时。
    • separate pass 是什么意思?
    • 就像你更新为:@invoice.attributes = params 然后@invoice.project_id = project_id 当你验证project_id 是好的。然后@invoice.save! 或者只是验证params[:project_id] 没问题。
    猜你喜欢
    • 2012-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-10
    相关资源
    最近更新 更多