When are Strong Parameters used? - ruby-on-rails-4

I understand strong parameters are used in cases where we are creating an object and putting it into our database. For example,
User.create(params[:user]) would have to be User.create(params.require(:user).permit(:name, :email, :password).
This is standard and simple to understand, however, are strong parameters required when updating a column or a few attributes in a model?
current_user.update_attributes(params[:user]). Does that have to be current_user.update_attributes(params.require(:user).permit(:name, :email, :password).
Lastly, I don't think it is needed for this case:
current_user.update_column(authentication_token: nil), but would it have needed to be updated if instead we had params = { authentication_token: nil }, and did current_user.update_column(params)?

Any time you pass an instance of ActionController::Parameters to one of the mass assignment apis (new, create, update_attributes, update etc.) you need to permit the appropriate fields.
In a controller the params method returns an instance of ActionController::Parameters, as are any hashes contained within it, so you need to use permit.
If you do
params = { foo: bar }
record.update_attributes(params) or
record.update_attributes(foo: bar)
Then you're passing a normal hash and so you don't need to use permit

Are strong parameters required when updating a column or a few
attributes in a model?
Yes, if the model is being updated with values from the end-user. Never trust the user input.
Suppose the current_user model has a 'role_id' column, which could be 1 for super user, 2 for normal user and 3 for guest. If you don't sanitize the parameters, the end-user could easily forge a request to gain privileges and compromise your application security.
Regarding your last question, you're right. You don't need strong parameters to update the record with values you already know.

I experimented on Rails 4 environment with both update and update_attributes method calls, and I received identical errors for both method calls: ActiveModel::ForbiddenAttributesError in PeopleController#update . In the controller I used #person.update(params[:person]) and #person.update_attributes(params[:person]); so that’s why it says PeopleController in the error message.
Based on the API documentation, it looks like in Rails 4 update_attributes is alias for update. So I guess update_attributes does the same thing as update method in Rails 4:
update_attributes(attributes) public
Alias for ActiveRecord::Persistence#update
Therefore, both update and update_attributes methods have to use strong parameters to update database. I also tested update_attributes method with strong parameters: #person.update_attributes(person_parameters) and it worked
updated
About update_attribute and update_column methods. I just tested those for the very first time through a controller, and with those methods you don’t need to use strong parameters inside of controller ( which was a bit surprise to me), even when you are using params (user provided values). So with update_attribute and update_column methods you can update database without using strong parameters.

Related

Not possible to use shorthand route handlers if RestSerializer is used? (ember-cli-mirage)

I set up a simple Ember Twiddle to show you my error that is occurring when trying to update a model.
It's considerable that I'm using ember-cli-mirage for mocking the data.
According to the docs, I created a shorthand route that should handle the PUT request.
It does, but with the error: Your handler for the url /api/shops/1 threw an error: Cannot convert undefined or null to object
When using the JSONAPISerializer, everything is working with shorthands (mirage/config.js) and I'm able to update models, but in my case I have to use the RESTSerializer with serialized IDs in the responses.
The request payload when I'm sending the model's attrs are without Id at the end of the property name, f.e.:
// attrs object in PUT request
{
name: "Shop 1",
city: "1" // belongsTo relationship,
}
Now Mirage is trying to find those properties on the respective database model that has to be updated, but cannot find it, because in the database it's cityId and not just city...
I also found this issue report and it’s working, but I was hoping I could avoid something like this. As far as I can remember, in previous versions of ember-cli-mirage (v0.1.x) it was also not needed to override the normalize method in the serializer to be able to make use of the RestSerializer with serializedIds…
My question is:
Is there a way to stick to shorthand route handlers only, or do I really have to write a helper or other custom solution only because I have to use the RestSerializer?
That would be really sad, but at least I would know then.
Thanks for your support!
Short answer: it looks like you need the custom serializer for now until the bug fix for it is merged.
Long answer: that issue looks to be an issue that occurred in the 0.2 -> 0.3 upgrade for Mirage, likely because of underlying DB changes made in Mirage. It'll probably get fixed, but for now you'll need to work around it.

Rails 4+: do not retrieve sensitive attributes from DB by default

This a contrived example but I hope it demonstrates my question. No, I'm not storing passwords in plaintext, etc; this is just an example.
User.create(name: "John", password: "sensitive")
User.all => [{User: name: "John", password: "sensitive"}]
When I retrieve data from the DB, I do not want the password attribute returned by default, such that:
User.all => [{User: name: "John"}]
I assumed ActiveRecord would have a method in the model I could override to establish the default attributes to select, but I can't find such a method.
By default, Active Record loads all the attributes. There is no concept of "sensitive" attribute. In fact, as long as you don't dump the instance anywhere (e.g. using inspect), or you don't print out the attribute, there is really not a lot security issues associated with fetching that data (of course, assuming you have a secure connection).
Perhaps you should explain what is the issue that is bringing you to believe that not loading that attribute would be the best solution to the problem.
It's also important to remember that several Rails defaults will loop over all attributes, whenever you try to use features such as to_json. My suggestion is to avoid relying on those features, but always explicitly whitelist the attributes you want to use or export. That would prevent leakage of sensible details.
Last but not least, you can try to workaround the Rails defaults by using a method that enumerates all the attributes, without the ones you consider "sensible"
Model.select(Model.column_names - %w( password other )).all
select applies to any query method:
Model.select(Model.column_names - %w( password other )).find(54)
Model.select(Model.column_names - %w( password other )).first

How to get a list of permitted params for a specific controller action

The title of the questions pretty much describes what I need to do. It is basically the same as this question, which never received an answer:
Rails 4: get list of permitted attributes (strong parameters) from a controller
Similar to the example provided in that question, it would be the equivalent to something like this, if it existed:
account_update_permitted_params = AccountController.permitted_params(:update)
You basically can't do that due to the nature of strong parameters.
What you define in some_resource_attributes is meant to filter parameters hash of the request. If you look at the method definition, you will see params.require(:some_resource).permit.. - it operates on the params object, which is only present during a request.
So having such method seems to be of very little use.
If you really want to programatically access your whitelisted attributes in the some_resource_attributes, you can go with:
class ResourceController < ApplicationController
LIST = %i(foo bar baz)
private
def resource_attributes
params.require(:resource).permit(*LIST)
end
end
ResourceController::LIST
#=> [:foo, :bar, :baz]
But I dot see a point in this since you can just open controller's code and check it.

Doctrine specify logical name for JoinColumn [duplicate]

I'm working on an events site and have a one to many relationship between a production and its performances, when I have a performance object if I need its production id at the moment I have to do
$productionId = $performance->getProduction()->getId();
In cases when I literally just need the production id it seems like a waste to send off another database query to get a value that's already in the object somewhere.
Is there a way round this?
Edit 2013.02.17:
What I wrote below is no longer true. You don't have to do anything in the scenario outlined in the question, because Doctrine is clever enough to load the id fields into related entities, so the proxy objects will already contain the id, and it will not issue another call to the database.
Outdated answer below:
It is possible, but it is unadvised.
The reason behind that, is Doctrine tries to truly adhere to the principle that your entities should form an object graph, where the foreign keys have no place, because they are just "artifacts", that come from the way relational databases work.
You should rewrite the association to be
eager loaded, if you always need the related entity
write a DQL query (preferably on a Repository) to fetch-join the related entity
let it lazy-load the related entity by calling a getter on it
If you are not convinced, and really want to avoid all of the above, there are two ways (that I know of), to get the id of a related object, without triggering a load, and without resorting to tricks like reflection and serialization:
If you already have the object in hand, you can retrieve the inner UnitOfWork object that Doctrine uses internally, and use it's getEntityIdentifier() method, passing it the unloaded entity (the proxy object). It will return you the id, without triggering the lazy-load.
Assuming you have many-to-one relation, with multiple articles belonging to a category:
$articleId = 1;
$article = $em->find('Article', $articleId);
$categoryId = $em->getUnitOfWork()->getEntityIdentifier($article->getCategory());
Coming 2.2, you will be able to use the IDENTITY DQL function, to select just a foreign key, like this:
SELECT IDENTITY(u.Group) AS group_id FROM User u WHERE u.id = ?0
It is already committed to the development versions.
Still, you should really try to stick to one of the "correct" methods.

Difference between attr_accessible and strong parameters

I have just been doing a bit of reading on attr_accessor, attr_accessible and strong parameters at a few different locations:
Difference between attr_accessor and attr_accessible
How is attr_accessible used in Rails 4?
http://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html
And I am looking at mass assignment:
http://code.tutsplus.com/tutorials/mass-assignment-rails-and-you--net-31695
I can't get my head around the difference between attr_accessible and strong parameters. I am not 100% confident in my understanding of the subjects mentioned above so I could be missing something simple but I know they do a similar job.
However, what is the difference between attr_accessible and strong parameters? Are they just a different name for the same thing? Why did we move from one to the other?
Any info is appreciated.
attr_accessible has been deprecated in Rails 4 in favor of Strong Parameters.
Both are different approaches to the mass assignment problem but Strong Parameters is more flexible.
In example, you have an Usermodel with the attributes email:string and is_admin:boolean. You wish to allow the users to modify their email through a form but not the is_admin field.
In Rails 3 you should do:
attr_accesible :email
With this approach it's not possible for an user to modify is_admin because that attribute is protected.
One of the good things of Strong Parameters is that you could do the following in your controller:
def user_params
if current_user.admin?
params.require(:user).permit(:email, :is_admin)
else
params.require(:user).permit(:email)
end
end
This way one admin user will be able to modify is_admin while a normal user won't.
This is just one example and not the best way to grant administrative permissions to an user but it's quite illustrative.
The main advantage of Strong Parameters is that they are defined in the controller and can be dynamically assigned in run time. attr_accessible was a more static and monolithic way to whitelist attributes.
On the other hand attr_accessor is completely different thing and still can be used in Rails 4, in example, if you need one attribute in your model that it's not necessary to persist or to be written into the database but you require it in a form. Think about:
attr_accessor :has_accepted_legal_terms
It's a Ruby method that can be used to declare an attribute of your model that is not related to the database, an attribute of a Class or PORO (plain old ruby object).
Strong parameters and attr_accessible are two different ways of adding security protections to the Rails "mass assignment" feature. Strong Parameters is the way that is prescribed by the current version of Rails.
"Mass assignment" is a convenient shorthand in Rails that allows you to set many properties of a model in a single statement.
For example, imagine you have a #user that you want to update with data from a form submission. Without mass assignment, you'd have to write tedious code like this:
#user.first_name = params[:user][:first_name]
#user.last_name = params[:user][:last_name]
#user.phone_number = params[:user][:phone_number]
...
#user.save
And on and on for every form field.
With mass assignment, all of that code becomes a single line:
#user.update(params[:user])
But, this is full of security holes. Since params contains whatever data was submitted by the browser, a malicious user could add data to that submission that you did not expect. For example, they could add is_admin=1 to the parameters. If you have an is_admin database column, then mass assignment just let the user upgrade to an administrator. Yikes!
This is where Strong Parameters comes in. With Strong Parameters, Rails will raise a ActiveModel::ForbiddenAttributesError if you try to do the naïve update(params[:user]). Instead, you need to be explicit about what parameters you expect from the browser submission, using the require and permit helpers that Strong Parameters provides. Like this:
def user_params
# Note that :is_admin is not permitted!
params.require(:user).permit(:first_name, :last_name, :phone_number)
end
...
#user.update(user_params)
I can't speak for the maintainers of Rails, but I like Strong Parameters because it is flexible. If I have multiple actions in the users controller that expect different parameters, I can easily describe using permit the parameters that should be allowed.
Or if I have different user roles that are allowed to update different attributes, then I can easily model those permissions. As #CV-Gate mentioned, you can even change these permissions at runtime, which is a powerful.
In short, the flexibility of Strong Parameters is due to the fact that you can define that user_params method anywhere, and however you like. You have the full power of Ruby and OO concepts to make it work the way you want.
What about attr_accessible?
Without going into too much detail (since this feature is no longer supported by Rails): instead of using the permit method, you would do something similar using an attr_accessible macro in the model itself, like this:
class User < ActiveRecord::Base
attr_accessible :first_name, :last_name, :phone_number
...
end
So for simple cases, it works very similar to Strong Parameters; you just define the list of attributes in a different place.
However, since attr_accessible is strongly coupled with the definition of the model class, you lose a lot of flexibility. What if you have two different controller actions that need to do mass assignment for the same User model? Now you are stuck.
attr_accessor
The attr_accessor macro is built into Ruby and has nothing to do with Rails, mass assignment, or security. It just happens to have a similar name. :)