Forbidden Attributes Error in Rails 4 when encountering a situation where one would have used attr_accessible in earlier versions of Rails
Asked Answered
G

5

44

With the recent upgrade to Rails 4, updating attributes using code resembling the below does not work, I get a ActiveModel::ForbiddenAttributes error:

@user.update_attributes(params[:user], :as => :admin)

Where User has the following attr_accessible line in the model:

attr_accessible :role_ids, :as =>admin
# or any attribute other than :role_ids contained within :user

How do you accomplish the same task in Rails 4?

Glorygloryofthesnow answered 3/7, 2013 at 14:5 Comment(0)
G
42

Rails 4 now has features from the strong_parameters gem built in by default.

One no longer has to make calls :as => :admin, nor do you need the attr_accessible :user_attribute, :as => admin in your model. The reason for this is that, by default, rails apps now have 'security' for every attribute on models. You have to permit the attribute you want to access / modify.

All you need to do now is call permit during update_attributes:

@user.update_attributes(params[:user], permit[:user_attribute])

or, to be more precise:

@user.update_attributes(params[:user].permit(:role_ids))

This single line, however, allows any user to modify the permitted role. You have to remember to only allow access to this action by an administrator or any other desired role through another filter such as the following:

authorize! :update, @user, :message => 'Not authorized as an administrator.'

. . . which would work if you're using Devise and CanCan for authentication and authorization.

Glorygloryofthesnow answered 3/7, 2013 at 14:11 Comment(0)
G
39

If you create a new Rails 4 site you'll notice that generated controllers now include a private method which you use to receive your sanitised params. This is a nice idiom, and looks something like this:

private

  def user_params
    params.require(:user).permit(:username, :email, :password)
  end

The old way of allowing mass assignment was to use something like:

attr_accessible :username, :email, :password

on your model to mark certain parameters as accessible.

Upgrading

To upgrade you have several options. Your best solution would be to refactor your controllers with a params method. This might be more work than you have time for right now though.

Protected_attributes gem

The alternative would be to use the protected_attributes gem which reinstates the attr_accessible method. This makes for a slightly smoother upgrade path with one major caveat.

Major Caveat

In Rails 3 any model without an attr_accessible call allowed all attributes though.

In Rails 4 with the protected_attributes gem this behaviour is reversed. Any model without an attr_accessible call has all attributes restricted. You must now declare attr_accessible on all your models. This means, if you haven't been using attr_accessible, you'll need to add this to all your models, which may be as much work as just creating a params method.

https://github.com/rails/protected_attributes

Gore answered 18/12, 2013 at 13:11 Comment(0)
C
9

This problem might also be caused by the Cancan gem

Just add to application_controller.rb

before_filter do
  resource = controller_name.singularize.to_sym
  method = "#{resource}_params"
  params[resource] &&= send(method) if respond_to?(method, true)
end

Works without any further modifications of code got it from here: https://github.com/ryanb/cancan/issues/835#issuecomment-18663815

Caber answered 24/1, 2014 at 10:46 Comment(2)
Totally helpful, would wasted hours if not for this.Shult
This fixed it for me, but I'm not using cancan? Only devise.Vineland
S
5

Don't forget to add your new user_params method to the controller action:

  def create
    @user = User.new(user_params)

    @user.save
    redirect_to 'wherever'
  end
Sizeable answered 22/10, 2014 at 16:34 Comment(0)
T
-2
def create
  @user = User.create(user_params)
  ....
end

def update
  @user = User.find(params[:id])
  if @user.update_attributes(blog_params)
    redirect_to home_path, notice:  "Your profile has been successfully updated."
  else
    render action: "edit"
  end
end

private
  def user_params
    params.require(:user).permit(:name, :age, :others)
  end
Theomorphic answered 14/12, 2014 at 16:39 Comment(3)
@pixelearth, If you know why please let me know :)Theomorphic
please explain why he needs to do thisStallworth
Where are your blog_params?Seaway

© 2022 - 2024 — McMap. All rights reserved.