CanCan的load_and_authorize_resource触发了Forbidden Attributes。

17

我有一个标准的RESTful控制器,使用强参数。

class UsersController < ApplicationController
  respond_to :html, :js

  def index
    @users = User.all
  end

  def show
    @user = User.find(params[:id])
  end

  def new
    @user = User.new
  end

  def edit
    @user = User.find(params[:id])
  end

  def create
    @user = User.new(safe_params)

    if @user.save
      redirect_to @user, notice: t('users.controller.create.success')
    else
      render :new
    end
  end

  def update
    @user = User.find(params[:id])

    if @user.update_attributes(safe_params)
      redirect_to @user, notice: t('users.controller.update.success')
    else
      render :edit
    end
  end

  def destroy
    @user = User.find(params[:id])

    if current_user != @user
      @user.destroy
    else
      flash[:error] = t('users.controller.destroy.prevent_self_destroy')
    end
    redirect_to users_url
  end

  private

  def safe_params
    safe_attributes =
      [
        :first_name,
        :last_name,
        :email,
        :password,
        :password_confirmation,
      ]
    if current_user.is?(:admin)
      safe_attributes += [:role_ids]
    end
    params.require(:user).permit(*safe_attributes)
  end
end

在我的config/initializers文件夹中,我有一个名为strong_parameters.rb的文件。
ActiveRecord::Base.send(:include,  ActiveModel::ForbiddenAttributesProtection)

当我添加一个简单的CanCan的load_and_authorize_resource调用时,我得到了以下结果。
1) UsersController POST create with invalid params re-renders the 'new' template
 Failure/Error: post :create, user: @attr
 ActiveModel::ForbiddenAttributes:
   ActiveModel::ForbiddenAttributes
 # ./spec/controllers/users_controller_spec.rb:128:in `block (4 levels) in <top (required)>'

在测试中,@attr的定义为:

  before(:each) do
    @attr =
      {
        first_name: "John",
        last_name: "Doe",
        email: "user@example.com",
        password: "foobar",
        password_confirmation: "foobar"
      }
  end

在我的测试中,我已经正确设置了用户的登录和所需的管理员角色,所以我知道不是这个问题。我不知道为什么会触发ForbiddenAttributes错误。我确定这是我忽视的一些简单问题。有没有其他人遇到过这个问题并找到解决方法?

2个回答

20

我认为这是因为如果您不使用before_filter预加载它,CanCan将使用其自己的getter方法来请求资源。所以您可以将以下代码添加到控制器中,然后它应该正常工作:

class UsersController < ApplicationController
  before_filter :new_user, :only => [:new, :create]

  load_and_authorize_resource

  def new_user
    @user = User.new(safe_params)
  end
end

(然后对编辑/更新操作执行相同的操作。)


6
好的,我来翻译。如果您调用cancan的 load_and_authorize_resource 方法,它会尝试加载最“合理”的资源,即根据控制器名称确定的资源,这优先于 strong parameters gem 的操作。在这种情况下,它将尝试构建 User,即 @user = User.new(params[:user])。但是,strong_parameters 不允许以这种方式进行批量分配。如果您使用一个 before_filter 来设置 @user 实例变量,则 CanCan 将仅使用该变量。如果您的 before_filter 符合 strong_parameters 要求,就不应该出现错误。 - Peter Duijnstee
1
很高兴能帮到你!祝你的项目好运! - Peter Duijnstee
1
@PeterDuijnstee:我认为你想要的是:only => [:create],而不是:only => [:new, :create]。:new根本没有任何参数。因此,在params.require(:user).permit(*safe_attributes)中,它会被卡住。 - Victor Ronin
当然,这需要 params[:user] 参数。我对 Strong Parameters 没有太多经验,所以我错过了这个,好发现:) - Peter Duijnstee
@PeterDuijnstee,感谢您的回答。但更感谢您在评论中提醒我!实例名称在此问题中很重要!我的控制器是Admin::Ads,但我的模型是Ad,每当我的实例变量是@admin_ad时,它都不起作用。但当我将其更改为@ad时,它就可以工作了! - scaryguy
显示剩余3条评论

7
before_filter do
  params[:user] = safe_params
end
load_and_authorize_resource

1
重点在于 before_filter 要放在 load_and_authorize_resource 之前。 - Gerard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接