Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 5 allowing a JSON field through strong parameters not working

I'm trying to let through a deeply nested JSON field in my model (document => field_instance => value). I was using an empty hash due to a misunderstanding of the documentation.

permit! can do what I need but I am trying to avoid simply doing params.fetch(:document).permit! due to the massive security hole this opens. So how can I permit any structures of any type under the doubly nested JSON value 'parameter' only?

I am testing with a single string under value called text, and am getting 'Unpermitted parameter: text'

Each instance_field has a specific type which has a list of required parameters, but without a way of being specific for each instance_field in the document, I've opted just to allow all parameters under that JSON field.

Here is my document_params method:

params.fetch(:document)
  .permit(:structure_id, :field_instances_attributes => [
    :value, 
    :document_id, 
    :field_id, 
    :value_attributes => {}
  ])

So, what am I doing wrong here?

Or, even better: each field_instance has a type that knows the exact structure the field's value expects. Can I be specific about the fields allowed under value for each field_instance?

Related logs:

service_1  |   Parameters: {"utf8"=>"Ô£ô", "authenticity_token"=>" -- censored --", "document"=>{"structure_id"=>"1", "field_instances_attributes"=>[{"document_id"=>"0", "field_id"=>"1", "value_attributes"=>{"text"=>"asdf"}}]}, "commit"=>"Create Document"}
service_1  | Unpermitted parameter: text
service_1  | Unpermitted parameter: text
service_1  | #<FieldInstance id: nil, field_id: 1, document_id: nil, value: nil, created_at: nil, updated_at: nil>
like image 700
user1456632 Avatar asked Oct 29 '22 06:10

user1456632


1 Answers

It's actually pretty simple to build a params hash in multiple steps, but it's a little non-obvious.

Here's what I do:

def document_params
  @document_params ||= params.require(:document).permit(:structure_id, field_instance_atributes: %i[document_id field_id]).tap do |doc|
    doc[:value] = params[:document].require(:value).permit!
  end
end

If the value object sanitization is really complicated, perhaps write that as its own helper and use it here.

The important part is the use of require: it returns the inner value as a new object, and so manipulations against it don't affect params. You can go back to the well, so to speak, as many times as you need.

Caveat: require will throw ActionController::ParameterMissing if the key is not found.

like image 129
Adam Lassek Avatar answered Nov 09 '22 14:11

Adam Lassek