Prevent certain properties from being updated? - ruby-on-rails

In rails, when updating a model, how do you prevent certain properties of the model from being updated when using a call like:
#user.update_profile params[:user]
Since anyone can just create a form input with a name like 'password', how can you filter the set of properties that you are allowing to be updatable?
Is this what attr_XXX is for?

You're looking for attr_accessible. It lets you specify which attributes can be set through mass-updating (like update_attributes), but you'll still be able to set the attributes "manually" (ie #user.attribute = ...).
For more information, see The importance of attr_accessible in Ruby on Rails.

You're looking for attr_protected to black list any attributes you don't want altered in a bulk update.
Throw it in your model and give it a list of attribute symbols to blacklist.
class User < ActiveRecord::Base
attr_protected :password
end
Alternatively you can use attr_accessible to take the white list approach and only the attributes given can be updated when updating the entire record at once. Every other attribute will be protected.
N.B Protected attributes can still be overwritten if it's directly assigned to as in
#user.password = "not secure"

Related

Shortcut for including strong parameters in Rails 4 : without listing all fields

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

Mongoid: How to prevent undefined fields from being created by mass assignment?

Here's the code:
class M
include Mongoid::Document
field :name
end
params = { name: "foo", age: 20 }
M.create(params)
#=> #<M name: "My Name", age: 20>
Notice that age wasn't defined, yet it was saved.
This is problematic (potentially a source of DoS) because a malicious user can add any parameters in POST and unknown fields with a large string can sneak in. (e.g. name=foo&bogus=#{'x'*1000000})
So far, I couldn't find anything but attr_accessible, but it's not really great for Mongoid as you have to maintain the same field names in both field and attr_accessible all the time, in all models. Not DRY.
I think the attr_accessible API is great for ActiveRecord, because there a. you don't explicitly define fields in the models (DRY) and b. it's guaranteed there's no chance that a nonexistent field gets saved to RDB. But for Mongoid, I think there should be a better solution than attr_accessible.
Note that there's a global config setting allow_dynamic_fields but it's not about mass assignment so it's out of the scope in this discussion, however I think it should actually be a per-model macro and should also take care of mass-assignment.
How are you dealing with this problem?
I'm always using attr_accessible in models. I rarely found myself including all fields as accessible. Usually there are always a few fields that shouldn't be accessible for mass assignment. If you often need to include every attribute and you're concerned about duplication:
attr_accessible *fields.keys
What I have done to solve this issue, is use a before save callback in my model:
set_callback(:save, :before) do |doc|
(doc.attributes.keys - fields.keys).each { |f| doc.unset(f) }
end
This way, even if there are extra attributes they get removed before being saved.

Dynamic scope for accessing Model Attributes

I'm currently using the mass assignment security baked into rails 3 to scope what level of users can update about their model. For example this code allows me to protect attributes based on the user level.
class Customer
attr_accessor :name, :credit_rating
attr_accessible :name
attr_accessible :name, :credit_rating, :as => :admin
end
I would like to be able to use this same idea for which attributes appear when I do a find. For example I would like to be able to say
Customer.all.as(:admin)
and get back the credit rating. Compare this to doing
Customer.all
and getting back all the attributes except the credit_rating
Is this something rails supports and I've missed?
attr_accessible is used to filter incoming attributes on mass assignment. This is a convenience method created so that a developer does not need to manually clean the incoming hash of params, something he does not control.
When displaying information a developer is in full control of what he/she desires to show, so there seems to be no reason to limit the read functionality.
However, rails allows you to "select" the attributes you desire in a query: see http://guides.rubyonrails.org/active_record_querying.html#selecting-specific-fields
You could easily create a scope with the name admin that would limit the selected values.
If you do not desire to have the full models, but only the values, you could use the generated sql. e:g.
ActiveRecord::Base.connection.select_values(Customer.select('name').to_sql)

Using attr_accessor and attr_accessible on the same field

What happens in the background with the following code?
class User < ActiveRecord::Base
attr_accessor :name
attr_accessible :name
end
Hint: When instantiating the class, will it be persisted to the database? Why or why not?
attr_accessor is ruby code and is used when you do not have a column in your database, but still want to show a field in your forms. The only way to allow this is to attr_accessor :fieldname and you can use this field in your View, or model, if you wanted, but mostly in your View.
attr_accessible allows you to list all the columns you want to allow Mass Assignment, as andy eluded to above. The opposite of this is attr_protected which means this field i do NOT want anyone to be allowed to Mass Assign to. More then likely it is going to be a field in your database that you don't want anyone monkeying around with. Like a status field, or the like.
In most cases, you don't need to use attr_accessor if the field is a column in the users table in your database. ActiveRecord will figure it out for you.
attr_accessible simply allows to field to be assigned via mass assignment (e.g., with update_attributes). This is good for security purposes. More information from the MassAssignmentSecurity API docs.
Thanks everyone for quick answers!
Your answers combined gave me the pieces I needed to understand this puzzle, I think.
(In a related problem, I was getting a lot of nil errors like "Object doesn’t support #inspect", and "undefined method ‘keys’ for nil:NilClass". I managed to solve it now, by removing the att_accessor field altogether.)
By experimenting with this particular case, this is what I've found out:
Actually, the :name field won't be persisted to the database.
user = User.new(:name=>"somename")
Will only set the attribute on the object, but not persist the :name column to the database. Like the following 'rails console' output shows:
> user
=> <User id: nil, created_at: nil, updated_at: nil>
> user.save
=> true
> user
=> <User id:1, created_at: 2011-01-19 12:37:21, updated_at: 2011-01-19 12:37:21>
I assume this is because *the setter made by attr_accessor will override ActiveRecord's setter* (which takes care of the database persistence). You can still retrieve the value from the :name field from the object though, like this:
> user.name
=> "somename"
So, in conclusion, I've learnt that using attr_accessor on fields might lead to them not being persisted to the database. And while I thought attr_accessible describes fields in the database that should be accessible from the outside, it doesn't seem to make a difference in this case.
Since it inherits ActiveRecord, it will be persisted when you call the save method (but not when it is instantiated).
If you don't have any attributes for that model, I assume ActiveRecord will simply save a new row in the database (i.e. your object will only have a persisted id). This makes sense, as you might later add attributes to your User model, and the persisted instances should still be retrievable.

Mass assign for attr_accessible :association_attributes in special cases

In the User model I have two accepts_nested_attributes_for: :details (which is has_one association) and :membership_orders (has_many).
For :details I have:
attr_accessible :details_attributes
But for the membership_orders I can't have so simple accessor, because I want to protect it from the normal user, but make it accessible for the admin.
It's possible to do with attribute-permissions plugin (github.com/Fingertips/attribute-permissions/tree/master), but I think it's not the finest solution.
Can you tell me how I can add special expression for the attr_accessible, or maybe filter out those attributes using before_validation (or what-else).
You can read about this problem by this link: blog.smartlogicsolutions.com/2009/02/24/rails-23-nested-object-forms-im-not-crazy-about-them/
Thanks.
You could certainly filter them out before validation. Another option is to switch to attr_protected and specify the fields you want protected instead of the ones you want open. That could be a simple switch or a more involved one, depending on the size of your models.

Resources