Uzzar
Uzzar

Reputation: 703

Strong Params: params.permit returns Unpermitted parameters despite whitelist

UsersProfileController has strong params that looks like so:

    def user_profile_params
      params.permit(:age, :relations)
      # yes, I am not requiring user_profile. Just permitting attributes I need. 
    end

The create action builds UserProfile through a parent (has-one and belongs-to association)

    def create
      parent = Parent.create_guest
      parent.build_user_profile(user_profile_params)
      if parent.save 
        # do something 
      else 
        # handle error
      end
    end

Calling params in UserProfiles returns:

    <ActionController::Parameters 
      {"age"=>"23", 
       "relations"=>"3", 
       "subdomain"=>"api", 
       "format"=>:json, 
       "controller"=>"api/v1/user_profiles", 
       "action"=>"create"} 
     permitted: false>

Calling user_profile_params, returns this:

    user_profile_params:
      Unpermitted parameters: subdomain, format
      <ActionController::Parameters 
       {"age"=>"23", 
       "relations"=>"3", } 
      permitted: true>

When a post request comes in, I expect to be able to create user_profile using the whitelisted params in user_profile_params. Instead, the create action in UserProfiles fails with error: Unpermitted parameters: subdomain, format.

This isn't what I expected. I expected user_profile_params to only include the permitted values and ignore all others.

I could add :format and :subdomain to list of permitted attributes but something feels a bit off about that.

Can someone explain what is going on/what I am missing?

Upvotes: 6

Views: 5909

Answers (2)

Damian Demasi
Damian Demasi

Reputation: 81

Expanding on @mrlew's answer, if we have an array, we need to permit the parameters inside the array. Otherwise, the warning message will keep showing.

fake_params_hash = {
    "age"=>"23",
    "relations"=>"3",
    "subdomain"=>"api",
    "format"=>:json,
    "array"=>[
        "sub_item"=>"hello"
    ],
    "controller"=>"api/v1/user_profiles",
    "action"=>"create"
}

permited_params = ActionController::Parameters.new(fake_params_hash).permit(:age, :relations, :array)

#=> Unpermitted parameters: :subdomain, :format, :array

permited_params = ActionController::Parameters.new(fake_params_hash).permit(:age, :relations, array: [:sub_item])

#=> Unpermitted parameters: :subdomain, :format

It took me half a day to realise this in order to solve a bug at work...

Upvotes: 2

luiscrjr
luiscrjr

Reputation: 7278

This message is just a warning, and not an error/exception. If your model it's not being persisted, that's by another reason.

From the strong parameters docs:

Handling of Unpermitted Keys

By default parameter keys that are not explicitly permitted will be logged in the development and test environment. In other environments these parameters will simply be filtered out and ignored.

Additionally, this behaviour can be changed by changing the config.action_controller.action_on_unpermitted_parameters property in your environment files. If set to :log the unpermitted attributes will be logged, if set to :raise an exception will be raised.

You can simulate it in your console (rails c):

fake_params_hash = {
    "age"=>"23", 
    "relations"=>"3", 
    "subdomain"=>"api", 
    "format"=>:json, 
    "controller"=>"api/v1/user_profiles", 
    "action"=>"create"
} 

permited_params = ActionController::Parameters.new(fake_params_hash).permit(:age, :relations)
#=> Unpermitted parameters: subdomain, format <== warning logged to the console
#=> <ActionController::Parameters {"age"=>"23", "relations"=>"3"} permitted: true>


user = User.create(permited_params) #mass assigment with permited params

#check if there are errors
puts user.errors.messages if user.errors.any?

As you can see, this message is not thrown by the User.create, but when .permit is invoked.

Upvotes: 7

Related Questions