Strong Parameters

Rails allows you to instantize a model object using parameters hash (attribute => value). The mechanism is called mass assignment and thanks to that you can easily pass request parameters (params) and build an object without a need to assign attributes manually one by one. Here is an example:

# app/controllers/registrations_controller.rb

class RegistrationsController < ApplicationController
  def signup
	params[:user] # => {"name"=>"Elvis Presley", "email"=>"elivis@example.com"}
	@user = User.new(params[:user])
  end

  # ...
end

Mass-assignment vulnerability and protected attributes

Unfortunately, this can sometimes cause a harm. Without additional validation attackers can pass parameters that the developer never intended to be set. It is called mass-assignment vulnerability.

First Rails versions introduced “protected attributes” to protect model attributes from mass-assignment vulnerability. On the model level it was possible to declare a white or black list of attributes.

# app/models/user.rb

class User < ActiveRecord::Base
  attr_accessible :name, :email

  # ...
end

Strong Parameters

The solution was not very flexible and Rails 4 introduced Strong Parameters, which allows to filter attribute on the controller level. Now, you can decide depend on the context, which attributes to permit for mass update.

# app/controllers/registrations_controller.rb

class RegistrationsController < ApplicationController
  def signup
	@user = User.new(signup_params)
  end

  # ...

  private

    def signup_params
      params.require(:user).permit(:name, :email)
    end
end

Permit only selected

Let’s have a closer look and check the available methods. permit allows to declare which keys (attributes) are allowed. All other will be filtered out. As a result it returns ActionController::Parameters with all available parameters.

# Create ActionController::Parameters object with some parameters for test purposes
params = ActionController::Parameters.new({
  name: 'Elvis Presley',
  email: 'elvis@example.com'
})

# The object includes all request parameters with the permitted flag is set to false
irb(main):001:0> params
=> <ActionController::Parameters {"name"=>"Elvis Presley", "email"=>"elvis@example.com"} permitted: false>

# Use permit to filter out all parameters except name
irb(main):002:0> params.permit(:name)
=> <ActionController::Parameters {"name"=>"Elvis Presley"} permitted: true>

# You can pass any number of parameter names you want to pass
irb(main):003:0> params.permit(:name, :email)
=> <ActionController::Parameters {"name"=>"Elvis Presley", "email"=>"elvis@example.com"} permitted: true>

# In case the key does not exists it returns an empty object
irb(main):004:0> params.permit(:non_existing)
=> <ActionController::Parameters {} permitted: true>

# Do not chain permit method if you want to permit more than one param
irb(main):005:0> params.permit(:name).permit(:email)
=> <ActionController::Parameters {} permitted: true>

# The orginal params object won't be changed
irb(main):006:0> params
=> <ActionController::Parameters {"name"=>"Elvis Presley", "email"=>"elvis@example.com"} permitted: false>

Require to make sure the param exists

The second method you might want to use is require which allows you to make sure the param exists.

# Create ActionController::Parameters object with some parameters for test purposes
params = ActionController::Parameters.new({
  name: 'Luke Skywalker',
  email: 'luke@example.com'
})

# Check if param :name exists.
irb(main):001:0> params.require(:name)
=> "Luke Skywalker"

# Please note it returns the param value, not the ActionController::Parameters object
irb(main):002:0> params.require(:name).class
=> String

# You can check only one param at a time
irb(main):003:0> params.require(:name, :email)
Traceback (most recent call last):
        1: from (irb):27
ArgumentError (wrong number of arguments (given 2, expected 1))

# When param is missing it raises an exception
irb(main):004:0> params.require(:age)
Traceback (most recent call last):
        2: from (irb):27
        1: from (irb):28:in `rescue in irb_binding'
ActionController::ParameterMissing (param is missing or the value is empty: age)

We can combine require and permit to make sure a hash of attributes is send.

# Create ActionController::Parameters object with some parameters for test purposes
params = ActionController::Parameters.new({
  person: {
  name: 'Elvis Presley',
  email: 'elvis@example.com',
  role: 'admin'
  }
})

# Check if :person param exists. Please notice it returns a hash.
irb(main):001:0> params.require(:person)
=> <ActionController::Parameters {"name"=>"Elvis Presley", "email"=>"elvis@example.com", "role"=>"admin"} permitted: false>

# Permit :name from :person hash
irb(main):002:0> params.require(:person).permit(:name)
=> <ActionController::Parameters {"name"=>"Elvis Presley"} permitted: true>

# Permit :name and :email from :person hash
=> <ActionController::Parameters {"name"=>"Elvis Presley", "email"=>"elvis@example.com"} permitted: true>

# Sometimes you might want to permit all params but it is not considered as safe operation.
irb(main):003:0> params.require(:person).permit!
=> <ActionController::Parameters {"name"=>"Elvis Presley", "email"=>"elvis@example.com", "role"=>"admin"} permitted: true>

Summary

Strong Parameters are very flexible and allows to protect our internal code from unwanted params. For more details please check Rails documentation for Strong Parameters.