如何限制Rails关联中的外键?

时间:2013-09-26 13:56:22

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

在我的Rails应用中,我users可以拥有多个projects,而tasks可以有多个class Task < ActiveRecord::Base attr_accessible :project_id end

模型:

class TasksController < ApplicationController

  def create
    @task = current_user.tasks.build(params[:task])    
    if @task.save
      flash[:success] = "Task saved."
      redirect_to edit_task_path(@task)
    else
      render :new
    end
  end

  def update
    if @task.update_attributes(params[:task])
      flash[:success] = "Task updated."
      redirect_to edit_task_path(@task)
    else
      render :edit
    end
  end

end

控制器:

project_ids

Rails的标准做法是确保用户 A 无法为用户 B 创建任务?

现在,我正在通过表单中的选择框选项限制用户可用的{{1}}。但是,这很容易通过浏览器控制台入侵,根本不安全。

如何改进?

感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

我会使用before过滤器检查所需项目是否属于当前用户:

class TasksController < ApplicationController
  before_filter :find_project, only: :create

  def create
    @task = @project.tasks.build(params[:task])    
    if @task.save
      flash[:success] = "Task saved."
      redirect_to edit_task_path(@task)
    else
      render :new
    end
  end

  private

  def find_project
    @project = current_user.projects.where( id: params[ :task ][ :project_id ] ).first
    redirect_to( root_path, notice: 'No such project' ) unless @project
  end
end

因此,如果给定的project_id与属于当前用户的项目不匹配,则会重定向。

更多的rails方式是使用嵌套资源:

resources :projects
  resources :tasks, shallow: true
end

你会有这样的路线:

GET /projects/1/tasks (index)
GET /projects/1/tasks/new (new)
POST /projects/1/tasks (create)
GET /tasks/1 (show)
GET /tasks/1/edit (edit)
PUT /tasks/1 (update)
DELETE /tasks/1 (destroy)

但这不会有太大差异,你仍然需要检索Post:

class TasksController < ApplicationController
  before_filter :find_project, only: [ :index, :new, :create ]
  before_filter :find_task, only: [ :show, :edit, :update, :delete ]

  # other actions

  def create
    @task = @project.tasks.build(params[:task])    
    if @task.save
      flash[:success] = "Task saved."
      redirect_to edit_task_path(@task)
    else
      render :new
    end
  end

  private

  def find_project
    @project = current_user.projects.where( id: params[ :project_id ] ).first
    redirect_to( root_path, notice: 'No such project' ) unless @project
  end

  def find_task
    @task = current_user.tasks.where( id: params[ :id ] ).first
    redirect_to( root_path, notice: 'No such task' ) unless @task
  end
end

答案 1 :(得分:1)

最简单的方法是查找范围并利用#find可以引发RecordNotFound的事实。 Rails将拯救该异常并为您呈现404.

class TasksController < ApplicationController
  helper_method :project

  def create
    @task = project.tasks.build(params[:task])    
    if @task.save
      flash[:success] = "Task saved."
      redirect_to edit_task_path(@task)
    else
      render :new
    end
  end

  private

  def project
    @project ||= current_user.projects.find(params[:task][:project_id])
  end
end

我还要补充一点,您还应该为其所属项目下的任务范围设置范围。使用嵌套资源的/projects/:project_id/tasks/:id之类的东西。