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>
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With