Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 4 strong parameters failing when creating instances in rails console

Probably doing something stupid here, but here's my basic cookie cutter class:

class League < ActiveRecord::Base

    private
      def league_params
        params.require(:full_name).permit!
      end

end

And when creating a new instance of League:

2.0.0-p0 :001 > l = League.new(full_name: 'foo', short_name: 'bar')
WARNING: Can't mass-assign protected attributes for League: full_name, short_name

What exactly am I doing wrong here? This is a Rails 4.0.0.beta1 build + Ruby 2.0

** UPDATE **

I realize now that strong parameters are enforced in the Controller now and not in the model. The original question still stands. If they are permitted on the controller level, how can I properly whitelist attributes if I'm creating instances in the Rails console? Wouldn't I need to also use attr_accessible in this case also thereby completely duplicating what strong parameters is trying to "fix"?

like image 833
randombits Avatar asked Apr 11 '13 22:04

randombits


3 Answers

Two things. The league_params definition goes in the controller, not the model. And params.require() should contain the name of the model that is required to be present in the parameters, not the attributes. The attribute presence check should still be in the model validations. And be sure you really want to allow access to all attributes in the League model before you use permit!. So, it should look like this:

class LeaguesController < ApplicationController

  private
    def league_params
      params.require(:league).permit!
    end

end

Update:

Yes, if you want the attributes to be restricted when accessing the model directly, you would need to switch back to using the attr_accessible in the model. That functionality has been moved into this gem: https://github.com/rails/protected_attributes.

I think it is assumed that if you are working with the model directly in the console, you don't need the attributes to be protected as you know exactly what is being entered. As the console has full access to your app, it would be just as easy to hose the entire database as it would be to maliciously assign an attribute.

like image 102
ericeason Avatar answered Sep 29 '22 10:09

ericeason


The basic security reason for the existence of both strong parameters and attr_accessible is that there are certain attributes in the model that should not be allow to be changed unless it is the explicit intention of your code.

The slight difference between them is the perspective form which they do their jobs.

StrongParameters focus on the use case: each controller's action can be fine tuned to allow or disallow certain parameters, taking in consideration any condition. Total flexibility.

attr_accessible takes a different perspective. Instead of focusing on the use case, it focus on roles. So for example, depending on the role of the user certain attributes can be changed or not.


The way to use StrongParameters is to apply the require and permit keywords on the param hash.

require states that a key must be present on the params hash. require will raise an exception if there is not such a key.

permit states that a field is allowed. Any key that is not allowed will be removed from the hash and hence will not be passed to the model by means of mass-assignment.

Model

class League
   attr_protected :final_price  # Nobody can mass-assign the final price
   attr_accessible :winner_name, :as => :jury
end

And the controller

class LeaguesController < ApplicationController

These two actions use StrongParameters

  # A common user can create a league
  def create
    league = League.new(league_params)
    league.final_price = 1000
    league.save
    redirect_to(league)
  end

  # But only the admin can publish a league
  def publish_league
     league = League.find(params[:id]
     league.update_attributes(league_params_as_admin)
  end

This one uses attr_accessible

  def publish_the_winner
    league = League.find(params[:id]
    # We would expect the current_user.role to return :jury.
    league.assign_attributes(params[:league], :as => current_user.role)
  end

  private
    def league_params
      params.require(:league).permit(:name)
    end

    def league_params_as_admin
      params.require(:league).permit(:name, :status)
    end 

end

In my experience:

Use the flexibility of Strong Parameters to fine tune what attributes can be mass-assigned in each of your controllers.

Use the omnipresence of attr_accesible to make sure that certain attributes can not be mass-assigned no matter what. For example, in a Resque Task you may pass user input as a parameter. You would check that some attributes are not mass-assigned using attr_accesible.

More info:

http://api.rubyonrails.org/classes/ActiveModel/MassAssignmentSecurity/ClassMethods.html

https://github.com/rails/strong_parameters

like image 26
Nerian Avatar answered Sep 27 '22 10:09

Nerian


It seems the whitelisting is active, even though you are running Rails 4. Did you upgrade to Rails 4 from a Rails 3 app? Do you have this in your config/application.rb?

config.active_record.whitelist_attributes = true

Double-check that strong parameters are in force on all your models. If they are, you can change this setting to false.

Also, double-check that there is no attr_accessible in your model.

like image 28
clacke Avatar answered Oct 01 '22 10:10

clacke