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
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.
I am surprised at no one suggested this:
params.require(:lever).permit(:name,:lever_benefit_attributes => {})
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
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.
try
params.require(:lever).permit(:name, leave_benefit_attributes: LeaveBenefit.attribute_names.collect { |att| att.to_sym })
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
Related
I'd not claim myself to be an expert in Rails by any stretch. One of the things that confuses me is Strong Parameters, and I've not found any really straightforward tutorials on it, with the majority of the search results dominated by hits on the rails documentation which, whilst normally accurate, I don't find in any way easy to read and can't be considered a tutorial. The github for strong params also doesn't appear to cover this.
Say I have an entity called "Resource".
class ResourcesController < ApplicationController
...
def create
#resource = Resource.new(resource_params)
if #resource.save
...
respond_with(#resource.level)
else
...
end
end
def update
if #resource.update(resource_params)
...
respond_with(#resource.level)
else
...
end
end
...
def resource_params
params.require(:resource).permit(:name, :url, :description, :level_id)
end
end
Assume I have a scaffolded form which displays the fields for name, url, description and level_id. All of the fields are mandatory. I don't know how to amend the resource_params function to ensure that name, url and level_id are mandatory when updating (or creating) a resource, but that description is optional (but should still be permitted).
I've tried removing description from the require line, and adding it on a separate line as params.permit(:description) but that has not made any difference, the field is still mandatory in the form.
Any assistance on this would be welcomed!
As I said, this is nothing to do with strong parameters. You just need to remove required: true for that field to make it optional.
I think you should think about :on
http://guides.rubyonrails.org/active_record_validations.html#on
validates :email, uniqueness: true, on: :create
It enables validations only on specified action.
Strong parameters are used to ensure that no other data can pass into your object, that no1 can insert not wanted data. For example if you have entity User and it has field admin that if it is true, the user has admin role, it can not be set using the create or update method inside your controller, because some1 can send any kind of parameters to your controller, and you want to filter them (Allow only specific parameters).
This does not mean if you put something inside strong parameters that it is required for create, update or whatever. Strong parameters only are there to filter the data, and allow only certain parts of your entity to be settable through this service or REST. If you send parameters that are not in strong params defined, in your server console/log there will be unpermited parameters: list of the parameters.
Strong parameters has me very confused. I'm writing a form to create several records at once. They are passed in params as an array of attributes:
{ :appointments => [ { :field1 => 'value1'
, :field2 => 'value2'
}
, # next record
]
}
Then in the controller I would like to do something like
params[:appointments].each do |a|
app = Appointment.create! a
end
But I run into lots of trouble with strong parameters, in the form of ForbiddenAttributeErrors. I've tried using appointment_params and whitelisting attributes, but with no luck. I can't find any good documentation matching my use case. They all assume the array of records should be nested below some owner record but this is not the case here.
Any help would be appreciated.
Make sure you are white listing your array in addition to the actual model attributes.
It seems like you have used the scaffolded version of the params.require method and not have updated that method when you changed your controller to deal with an array of appointments rather than one appointment at a time.
Something like this should work:
params.require(:appointment).permit(:field1, :field2, appointments: [:field1, field2])
or
params.require(:appointments).permit(:field1, :field2)
Not sure exactly what the rest of yoru code looks like, but it seems like you're not permitting the array itself, the above code samples attempt to white list what I would assume that attribute might be named.
If you are only using the attributes to create a new Appointment record, you can use the following
Appointment.create!(params.permit(applications: [:field1, :field2])[:applications])
If you really want to iterate over the array, you can do
params[:appointments].each do |a|
app = Appointment.create!(a.permit(:field1, :field2))
end
I am starting with Rails 4. Had came across to the new security feature strong parameters related to permitting parameter in a controller.
http://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html
This is fine, but we need to list down all the fields from the models. Is there a easy way by which listing fields down the is not required.
Thanks.
One shortcut you can do if you have say around 20 columns in your model and you don't need to specify all columns inside permit in your controller then it as:
params.require(:person).permit!
This will permit all the attributes from your model
Here's a quick "getting started" tip regarding cases where you have "lots" of fields for your model Foo... rather then whitelist them all, auto-generate a list then remove the ones you know should not be mass-assigned.
In rails console you can get a symbol list of all fields very easily, copy/paste it to the permit(...) method, then remove the ones that should not be mass-assigned like :id, :ctreated_at, :admin, etc
Foo.attribute_names.map(&:to_sym).sort
> [:address, :admin, :created_at, :id, :name, :updated_at]
This takes only a few seconds longer than using the .permit! approach, but gives you a better starting point from a security point of view.
Strong Parameters were introduced in Rails 4:
It provides an interface for protecting attributes from end-user
assignment. This makes Action Controller parameters forbidden to be
used in Active Model mass assignment until they have been whitelisted.
Basically, it means only certain param values will be sent through your controller to the model (thus allowing you more control over which data is handled by Rails)
DRY
If you're wanting to use strong params for multiple controllers, or just want to DRY up the process, we use this:
#app/controllers/application_controller.rb
private
#Strong Params
def permitted_params
#resource = self.resource_class
#model = "#{#resource}".downcase.to_sym
attributes = #resource.attribute_names + %w(custom items here)
params.permit(#model => attributes)
end
I would like to have a more systematic solution for myself to avoid mass assignment.
A typical situation is to remove id or user_id from params (submitted automatically via form) and replace it with current_user.id internally (in MyController#create).
A solution I can think of is to create object from params hash, then update_attributes (of parent and child objects) to replace sensitive attributes with internal values:
#user = User.create(:params[:user])
#user.update_attributes(:id => current_user.id)
#user.profile.update_attributes(:user_id => current_user.id)
#user.preference.update_attributes(:user_id => current_user.id)
Is there a shorter/more DRY way to say this?
If preference, profile etc. are child objects of user (created via build method), how can I write a method to look for their foreign keys for user and automatically replace them with the value I passed to parent?
Thank you.
This is what attr_protected and attr_accessible (documentation) are for. attr_protected will give you blacklist protection, while attr_accessible will protect using a whitelist.
While calling update_attributes right after a mass assignment would work, you're better off using the built in ways of protecting mass assignments as it won't require duplication of code every time you do a mass assignment on a model.
I've done this in an earlier project by using:
#user = User.create(params[:user]) do |user|
user.id = current_user.id
end
Would this work for you?
An alternative is to check out the docs and search for the :as for role based attributes.
I've been reading up on rails security concerns and the one that makes me the most concerned is mass assignment. My application is making use of attr_accessible, however I'm not sure if I quite know what the best way to handle the exposed relationships is. Let's assume that we have a basic content creation/ownership website. A user can have create blog posts, and have one category associated with that blog post.
So I have three models:
user
post: belongs to a user and a category
category: belongs to user
I allow mass-assignment on the category_id, so the user could nil it out, change it to one of their categories, or through mass-assignment, I suppose they could change it to someone else's category. That is where I'm kind of unsure about what the best way to proceed would be.
The resources I have investigated (particularly railscast #178 and a resource that was provided from that railscast), both mention that the association should not be mass-assignable, which makes sense. I'm just not sure how else to allow the user to change what the category of the post would be in a railsy way.
Any ideas on how best to solve this? Am I looking at it the wrong way?
UPDATE: Hopefully clarifying my concern a bit more.
Let's say I'm in Post, do I need something like the following:
def create
#post = Post.new(params[:category])
#post.user_id = current_user.id
# CHECK HERE IF REQUESTED CATEGORY_ID IS OWNED BY USER
# continue on as normal here
end
That seems like a lot of work? I would need to check that on every controller in both the update and create action. Keep in mind that there is more than just one belongs_to relationship.
Your user can change it through an edit form of some kind, i presume.
Based on that, Mass Assignment is really for nefarious types who seek to mess with your app through things like curl. I call them curl kiddies.
All that to say, if you use attr_protected - (here you put the fields you Do Not want them to change) or the kid's favourite attr_accessible(the fields that are OK to change).
You'll hear arguments for both, but if you use attr_protected :user_id in your model, and then in your CategoryController#create action you can do something like
def create
#category = Category.new(params[:category])
#category.user_id = current_user.id
respond_to do |format|
....#continue on as normal here
end
OK, so searched around a bit, and finally came up with something workable for me. I like keeping logic out of the controllers where possible, so this solution is a model-based solution:
# Post.rb
validates_each :asset_category_id do |record, attr, value|
self.validates_associated_permission(record, attr, value)
end
# This can obviously be put in a base class/utility class of some sort.
def self.validates_associated_permission(record, attr, value)
return if value.blank?
class_string = attr.to_s.gsub(/_id$/, '')
klass = class_string.camelize.constantize
# Check here that the associated record is the users
# I'm leaving this part as pseudo code as everyone's auth code is
# unique.
if klass.find_by_id(value).can_write(current_user)
record.errors.add attr, 'cannot be found.'
end
end
I also found that rails 3.0 will have a better way to specify this instead of the 3 lines required for the ultra generic validates_each.
http://ryandaigle.com/articles/2009/8/11/what-s-new-in-edge-rails-independent-model-validators