0

在我的 Rails 应用程序中,我users有谁可以拥有很多projects,而后者又可以拥有很多invoices.

如何确保用户只能为的一个项目创建发票,而不能为另一个用户的项目创建发票?

class Invoice < ActiveRecord::Base

  attr_accessible :number, :date, :project_id

  validates :project_id,  :presence   => true,
                          :inclusion  => { :in => ????????? }

end

谢谢你的帮助。


class InvoicesController < ApplicationController

  def new  
    @invoice = current_user.invoices.build(:project_id => params[:project_id])
  end

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

end
4

3 回答 3

1

我认为这不应该进行验证。您应该确保用户选择的项目是他的项目之一。

你可以在你的控制器上做一些事情,比如:

project = current_user.projects.find params[:project_id]
@invoice = Invoice.new(project: project)
# ...

您的创建操作可能看起来像这样。

  def create
    @invoice = current_user.invoices.build(params[:invoice])
    @invoice.project = current_user.projects.find params[:invoice][:project_id]
    if @invoice.save
      flash[:success] = "Invoice saved."
      redirect_to edit_invoice_path(@invoice)
    else
      render :new
    end
  end
于 2013-09-21T16:12:30.140 回答
1

project_id是“敏感”属性 - 所以从 attr_accessible 中删除它。你是对的,你不应该相信表格中的参数,你必须检查它。

def create
  @invoice = current_user.invoices.build(params[:invoice])
  # @invoice.project_id is nil now because this attr not in attr_accessible list
  @invoice.project_id = params[:invoice][:project_id] if current_user.project_ids.include?(params[:invoice][:project_id])
  if @invoice.save
    flash[:success] = "Invoice saved."
    redirect_to edit_invoice_path(@invoice)
  else
    render :new
  end
end

如果用户试图破解您的应用程序并更改project_id为非拥有值,则方法create渲染部分new无效@invoice不要忘记留下project_idon presence的验证。

如果您遇到异常Can't mass-assign protected attributes...,有几种方法可以做。最简单的方法是: 1. 从环境配置(开发、测试、生产)中删除行

# Raise exception on mass assignment protection for Active Record models
config.active_record.mass_assignment_sanitizer = :strict

2. 在分配之前拒绝params中的敏感参数。

# changes in method create
def create
  project_id = params[:invoice].delete(:project_id)
  @invoice = current_user.invoices.build(params[:invoice])
  @invoice.project_id = project_id if current_user.project_ids.include?(project_id)
  ...
end
于 2013-09-21T20:24:57.993 回答
0

好的,幸运的是,这次我设法提出了自己的解决方案。

我没有对我的控制器进行任何更改(“让我们保持瘦”),而是向我的模型添加了一个验证方法:

class Invoice < ActiveRecord::Base

  attr_accessible :number, :date, :project_id

  validates :project_id,  :presence     => true,
                          :numericality => { :only_integer => true },
                          :inclusion    => { :in => proc { |record| record.available_project_ids } }

  def available_project_ids
    user.project_ids
  end

end

我不确定这是好的还是坏的编码实践。也许有人可以对此有所了解。但就目前而言,这对我来说似乎很安全,到目前为止我还无法以任何方式破解它。

于 2013-09-22T19:16:56.743 回答