strong parameters permit all attributes for nested attributes
Asked Answered
C

6

36

Is there a way in strong parameters to permit all attributes of a nested_attributes model? Here is a sample code.

class Lever < ActiveRecord::Base
 has_one :lever_benefit
 accepts_nested_attributes_for :lever_benefit
end

class LeverBenefit < ActiveRecord::Base
  # == Schema Information
  #  id          :integer          not null, primary key
  #  lever_id    :integer
  #  explanation :text
end

For lever strong parameters i am writing currently this

def lever
 params.require(:lever).permit(:name,:lever_benefit_attributes => [:lever_id, :explanation])
end

Is there a way for nested attributes i can write to permit all attributes without explicitly giving the attributes name like lever_id and explanation ?

Note: Please don't get confused with this question with permit! or permit(:all) this is for permitting all for nested attributes

Caracas answered 23/7, 2013 at 12:57 Comment(2)
try reading this answer may be this is helpful. > #14484463Bekelja
thanks, but i have seen this. If you notice it's doing the same thing of selective attribute filtering( assets_attributes: :filename ) which is passing filename. I want to permit all parameters for nested attributesCaracas
F
-4

The whole point of strong parameters is in its name: make your input parameters strong.
Permitting all the parameters would be a very bad idea, as it would permit anyone to insert values you don't necessarily want to be updated by your users.

In the example you give, you mention the two parameters you currently need to provide:
[:lever_id, :explanation].

If you permitted all the parameters, it would be possible for somebody to change any other value.
created_at, or lever_id for example.

This would definitely be a security issue and this is why you should not do it.
Explicitely specifying all your attributes might seem boring when you do it.
But this is necessary to keep your application secure.

Edit: For people downvoting this. This may not be the answer you're looking for, but it is the answer you need.
Whitelisting all nested attributes is a huge security flaw that strong params is trying to protect you with, and you're removing it.
Take a look at what lead to building strong_params, and how not using it can be bad for you: https://gist.github.com/peternixey/1978249

Flummox answered 23/7, 2013 at 14:1 Comment(8)
thanks, your answer is a valid point that it can be a security concern. I will go with explicitly mentioning the attributes.Caracas
If the hash you were receiving is stored in a serialised JSON column then there are no security concerns for the keys. (Other than existing concerns of too large an input). I have this use case and I would like to permit any arbitrary key in a hash.Rosenda
There are other ways of securing attributes. Command objects that toss out unpermitted parameters, for example, are a much better approach, IMHO.Radiocarbon
Even though I didn't directly answer the question, I believe I provided much better advice. Permitting all attributes is potentially a huge security issue and should never be done.Flummox
There are legitimate use cases where you would want to allow for adhoc params, like a serialized object with a loose or non-existent schema. This DOES happen quite a bit more often than neverTypist
This is a BAD answer. SO is for solving problems, not telling people how what they are doing is wrong. According to me, these types of answers should only be regarded as valid if they include alternative solutions to what the author might be trying to do. Like for e.g. refer to the anwers below.Narco
If so, then the :permit! method should be removed from the API, which it isn't the case. There are scenarios where you're sure you want to permit everything.Cleopatracleopatre
Yes, the permit syntax in rails has an open issue because it can't deal with 2D arrays, for instance. I think one's only option in those cases is to use a permit! equivalentBoyette
C
60

The only situation I have encountered where permitting arbitrary keys in a nested params hash seems reasonable to me is when writing to a serialized column. I've managed to handle it like this:

class Post
  serialize :options, JSON
end

class PostsController < ApplicationController
  ...

  def post_params
    all_options = params.require(:post)[:options].try(:permit!)
    params.require(:post).permit(:title).merge(:options => all_options)
  end
end

try makes sure we do not require the presents of an :options key.

Cessation answered 16/8, 2013 at 15:26 Comment(3)
This is the correct answer! you deserve a green checkbox!Devy
Why params.require(:post).fetch(:options, nil) instead of params.require(:post)[:options]?Blakemore
@PatrickBrinich-Langlois Also cannot remember, where the fetch came from. Updated the answer.Cessation
D
22

I am surprised at no one suggested this:

params.require(:lever).permit(:name,:lever_benefit_attributes => {})
Disjoined answered 26/3, 2020 at 0:45 Comment(3)
It allows any attributes within the hash in lever_benefit_attributes. Works for me,Notus
Three hours and many headaches later, I'm surprised too. Perfect for my jsonb column within devise_parameter_sanitizer.Farah
I have a parameter with 3 layers of nested arrays with dynamic values and this permits everything. Many thanks!Finicking
N
17

Actually there is a way to just white-list all nested parameters.

params.require(:lever).permit(:name).tap do |whitelisted|
  whitelisted[:lever_benefit_attributes ] = params[:lever][:lever_benefit_attributes ]
end

This method has advantage over other solutions. It allows to permit deep-nested parameters.

While other solutions like:

nested_keys = params.require(:lever).fetch(:lever_benefit_attributes, {}).keys
params.require(:lever).permit(:name,:lever_benefit_attributes => nested_keys)

Don't.


Source:

https://github.com/rails/rails/issues/9454#issuecomment-14167664

Northwestwards answered 25/9, 2014 at 21:2 Comment(1)
This will not work for deep-nested params in Rails 5. See why here: eileencodes.com/posts/…Gillmore
A
12

First, make sure that you really want to allow all values in a nested hash. Read through Damien MATHIEU's answer to understand the potential opening of security holes...

If you still need/want to allow all values in a hash (there are perfectly valid use cases for this, e.g. storing unstructured, user-provided metadata for a record), you can achieve it using the following bits of code:

def lever_params
  nested_keys = params.require(:lever).fetch(:lever_benefit_attributes, {}).keys
  params.require(:lever).permit(:name,:lever_benefit_attributes => nested_keys)
end

Note: This is very similar to tf.'s answer but a bit more elegant since you will not get any Unpermitted parameters: lever_benefit_attributes warnings/errors.

Amygdalin answered 13/12, 2013 at 14:9 Comment(0)
R
6

try

params.require(:lever).permit(:name, leave_benefit_attributes: LeaveBenefit.attribute_names.collect { |att| att.to_sym })
Redfin answered 2/11, 2013 at 21:12 Comment(0)
F
-4

The whole point of strong parameters is in its name: make your input parameters strong.
Permitting all the parameters would be a very bad idea, as it would permit anyone to insert values you don't necessarily want to be updated by your users.

In the example you give, you mention the two parameters you currently need to provide:
[:lever_id, :explanation].

If you permitted all the parameters, it would be possible for somebody to change any other value.
created_at, or lever_id for example.

This would definitely be a security issue and this is why you should not do it.
Explicitely specifying all your attributes might seem boring when you do it.
But this is necessary to keep your application secure.

Edit: For people downvoting this. This may not be the answer you're looking for, but it is the answer you need.
Whitelisting all nested attributes is a huge security flaw that strong params is trying to protect you with, and you're removing it.
Take a look at what lead to building strong_params, and how not using it can be bad for you: https://gist.github.com/peternixey/1978249

Flummox answered 23/7, 2013 at 14:1 Comment(8)
thanks, your answer is a valid point that it can be a security concern. I will go with explicitly mentioning the attributes.Caracas
If the hash you were receiving is stored in a serialised JSON column then there are no security concerns for the keys. (Other than existing concerns of too large an input). I have this use case and I would like to permit any arbitrary key in a hash.Rosenda
There are other ways of securing attributes. Command objects that toss out unpermitted parameters, for example, are a much better approach, IMHO.Radiocarbon
Even though I didn't directly answer the question, I believe I provided much better advice. Permitting all attributes is potentially a huge security issue and should never be done.Flummox
There are legitimate use cases where you would want to allow for adhoc params, like a serialized object with a loose or non-existent schema. This DOES happen quite a bit more often than neverTypist
This is a BAD answer. SO is for solving problems, not telling people how what they are doing is wrong. According to me, these types of answers should only be regarded as valid if they include alternative solutions to what the author might be trying to do. Like for e.g. refer to the anwers below.Narco
If so, then the :permit! method should be removed from the API, which it isn't the case. There are scenarios where you're sure you want to permit everything.Cleopatracleopatre
Yes, the permit syntax in rails has an open issue because it can't deal with 2D arrays, for instance. I think one's only option in those cases is to use a permit! equivalentBoyette

© 2022 - 2024 — McMap. All rights reserved.