Need help with stronger_parameters?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

zendesk
226 Stars 24 Forks Apache License 2.0 216 Commits 0 Opened issues

Description

Type checking and type casting of parameters for Action Pack

Services available

!
?

Need anything else?

Contributors list

stronger_parameters

CI

This is an extension of

strong_parameters
with added type checking and conversion.

Simple types

You can specify simple types like this:

params.permit(
  id: Parameters.id,
  name: Parameters.string
)

Arrays

You can specify arrays like this:

params.permit(
  id: Parameters.array(Parameters.id)
)

This will allow an array of id parameters that all are IDs (integer less than 2**31, greater than 0) and convert to Fixnum (

'2' --> 2
).

Empty array -> nil

Rails converts empty arrays to nil, so often

Parameters.array | Parameters.nil
is needed.

Allowing nils

It can be convenient to allow nil for all attributes since ActiveRecord converts it to false/0.

ActionController::Parameters.allow_nil_for_everything = true

Rejecting nils

You can reject a request that fails to supply certain parameters by marking them as required with the

.required
operator:
params.permit(
  name: Parameters.string.required, # will not accept a nil or a non-String
  email: Parameters.string          # optional, may be omitted
)

This also works in conjunction with the

&
and
|
constraints. For example, to express that a
uid
must be either a string or a number:
params.permit(
  uid: (Parameters.string | Parameters.integer).required
)

Nested Parameters

params.permit(
  name: Parameters.string,
  emails: Parameters.array(Parameters.string),
  friends: Parameters.array(
    Parameters.map(
      name: Parameters.string,
      family: Parameters.map(
        name: Parameters.string
      ),
      hobbies: Parameters.array(Parameters.string)
    )
  )
)

This will allow parameters like this:

{
  "name": "Mick",
  "emails": ["[email protected]", "[email protected]"],
  "friends": [
    {"name": "Morten", "family": {"name": "Primdahl"}, "hobbies": ["work", "art"]},
    {"name": "Eric", "family": {"name": "Chapweske"}, "hobbies": ["boating", "whiskey"]}
  ]
}

ActiveModel Nested Attributes

params.require(:author).permit(
  name: Parameters.string,
  books_attributes: Parameters.array(
    Parameters.map(
      title: Parameters.string,
      id: Parameters.id,
      _destroy: Parameters.boolean
    )
  )
)

This will allow parameters like this:

{
  "author": {
    "name": "Eric Chapweske",
    "books_attributes": [
      {"title": "Boatin' and Drinkin'", "id": 234, "_destroy": true},
      {"title": "Advanced Boatin' and Drinkin'", "id": 567}
    ]
  }
}

Combining Requirements

If you want to permit a parameter to be one of multiple types, you can use the

|
operator:
params.require(:ticket).permit(
  status: Parameters.id | Parameters.enum('open', 'closed')
)

This will allow these parameter sets:

{
  "ticket": {
    "status": 123
  }
}
{
  "ticket": {
    "status": "open"
  }
}

You can use the

&
operator to apply further restrictions on the type:
params.require(:user).permit(
  age: Parameters.integer & Parameters.gte(0)
)

This requires the parameter to be an integer greater than or equal to 0.

Combining Requirements in Arrays

You can also use the

|
and
&
operators in arrays:
params.require(:group).permit(
  users: Parameters.array(Parameters.id | Parameters.string)
)

This will permit these parameters:

json
{
  "group": {
    "users": [123, "[email protected]", 345, 676, "[email protected]"]
  }
}

Rollout in log-only mode

Just want to log violations in production:

# config/environments/production.rb
ActionController::Parameters.action_on_invalid_parameters = :log

Controller support

Include

PermittedParameters
into a controller to force the developer to explicitly permit params for every action.

Examples:

class TestController < ApplicationController
  include StrongerParameters::ControllerSupport::PermittedParameters

permitted_parameters :all, locale: Parameters.string # permit :locale in all actions for this controller

permitted_parameters :show, id: Parameters.integer def show end

permitted_parameters :create, topic: { forum: { id: Parameters.integer } } def create end

permitted_parameters :index, {} # no parameters permitted def index end

permitted_parameters :update, :skip # use when migrating old controllers/actions def update end end

Log only mode for invalid parameters

To only log invalid (not unpermitted) parameters during rollout of stronger_parameters:

class MyController < ApplicationController
  log_invalid_parameters! if Rails.env.production? # Still want other environments and controllers to raise

permitted_parameters :update, user: { name: Parameters.string } def update end end

Notifying users about unpermitted params

Add headers to all requests that have unpermitted params (does not log invalid):

# config/application.rb
config.stronger_parameters_violation_header = 'X-StrongerParameters-API-Warn'
curl -I 'http://localhost/api/users/1.json' -X POST -d '{ "user": { "id": 1 } }'
=> HTTP/1.1 200 OK
=> ...
=> X-StrongerParameters-API-Warn: Removed restricted keys ["user.id"] from parameters

Types

| Syntax | (Simplified) Definition | |--------------------------------|--------------------------------------------------------------------------------------------| | Parameters.string | value.isa?(String) | | Parameters.integer | value.isa?(Fixnum) or '-1' | | Parameters.float | value.isa?(Float) or '-1.2' | | Parameters.datetime | value.isa?(DateTime) or '2014-05-13' or '2015-03-31T14:34:56Z' | | Parameters.datetimeiso8601 | value is a date that conforms to ISO8601: '2014-05-13' or '2015-03-31T14:34:56Z' | | Parameters.regexp(/foo/) | value =~ regexp | | Parameters.enum('asc', 'desc') | ['asc', 'desc'].include?(value) | | Parameters.lt(10) | value < 10 | | Parameters.lte(10) | value <= 10 | | Parameters.gt(0) | value > 0 | | Parameters.gte(0) | value >= 0 | | Parameters.integer32 | Parameters.integer & Parameters.lt(2 ** 31) & Parameters.gte(-2 ** 31) | | Parameters.integer64 | Parameters.integer & Parameters.lt(2 ** 63) & Parameters.gte(-2 ** 63) | | Parameters.id | Parameters.integer & Parameters.lt(2 ** 31) & Parameters.gte(0) | | Parameters.bigid | Parameters.integer & Parameters.lt(2 ** 63) & Parameters.gte(0) | | Parameters.uid | Parameters.integer & Parameters.lt(2 ** 32) & Parameters.gte(0) | | Parameters.ubigid | Parameters.integer & Parameters.lt(2 ** 64) & Parameters.gte(0) | | Parameters.boolean | Parameters.enum(true, false, 'true', 'false', 1, 0) | | Parameters.nil | value is nil | | Parameters.nilstring | value is nil, '', 'undefined' | | Parameters.file | File, StringIO, Rack::Test::UploadedFile, ActionDispatch::Http::UploadedFile or subclasses | | Parameters.decimal(8,2) | value is a String, Integer or Float with a precision of 9 and scale of 2 | | Parameters.hex | value is a String that matches the hexadecimal format |

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.