Difference between attr_accessible and strong parameters - ruby-on-rails-4

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. :)

Related

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.

Rails 4.2+: deep nesting resources when it's required to display data from entire resource chain

I'm familiar with the guideline not to nest more than 1 level deep, and I understand the various discussions about having the shortest URLs possible both in and out of code.
Most of the StackOverflow questions and Googling I've done answer this by presenting use cases where one doesn't have to access all elements in the entire chain.
But what do you do when you have to access the parent resources further up the chain for every page you're working with?
/{ACCOUNT_SLUG}/applications/{APPLICATION_UUID}/borrower/employments/{UUID}
When dealing with a single employment record, I have to display account-specific information take from the parent account object via the ACCOUNT_SLUG, as well as application information. And technically, my borrower is also a DB query, but since there is only 1 for each application, I don't have to add an id/slug in the URL.
General advice is to do something like:
/employments/{UUID}
But if I do that, then in my controller code (and elsewhere) I still have to do:
#employment = Employment.find_by(uuid: params[:uuid])
#account = #employment.borrower.application.account
So I have to walk the entire parent association chain and execute those association DB queries. How does one resolve this kind of situation where the deep nested association has to be kept, from the first parent to the last child?
1: Add association attributes to all the children
class Employment
belongs_to :borrower
belongs_to :application
belongs_to :account
end
Now I've got a bunch of associations going on everywhere, just to walk a clear chain. This seems like "dependency hell" to me?...
2: Go with deep nested routes; Github does it
I've noticed Github actually employs deep nested routes:
github.com/{USERNAME}/{REPO}/settings/hooks
While not as deeply-nested as my use case, they still nest everything underneath the username and repo, and if you list out their verbose URL it is:
/github.com/accounts/{USERNAME}/repos/{REPO_NAME}/settings/hooks
Is anyone aware if Github has a method of optimizing this deep nesting, or are they just looking up the account and repo with every request and biting the DB query overhead (probably not that big a deal...)?
3: Deep nest the routes, but make your own URL helpers to keep it clean
Using /{ACOUNT_SLUG}/applications/{APPLICATION_UUID}/borrower/employments/{UUID}, the URL helper would look something like:
account_applications_borrower_employments_path(#account, #application, #borrower, #employment)
This could be cleaned up using some helpers:
def borrower_employment_path(#employment)
#borrower = #employment.borrower
#application = #borrower.application
#account = #application.account
account_applications_borrower_employments_path(#account, #application, #borrower, #employment)
end
Very detailed question. Kinda late to respond, but in the past when the problem of nested resources has bitten me, it usually ends up that I need to revisit the way I relationally modeled my database.
Of the possible solutions you suggested, number 1 is probably the best considering the opinionated nature of the Rails framework. Rails associations are very powerful and leveraging them is useful as your app scales.
Addressing concerns about dependencies between your models:
Without seeing your schema, I can only guess, but your modeling could look like this:
Class Account
has_many :applications
end
Class Application
belongs_to :account
has_one :borrower
has_one :employment
end
Class Borrower
belongs_to :application
end
Class Employment
belongs_to :application
has_one :account, through: :application
end
This is my best guess from the dissecting the info above. With this modeling, you would be able to do this in a controller action:
def action_name
#employment = Employment.find_by(uuid: params[:uuid])
#account = #employment.account
end
You would be able to add extra associations, say, if you wanted to do:
#employment.borrower
by adding:
class Employment
belongs_to :application
has_one :account, through: :application
has_one :borrower, through: :application
end
I always give this a good read every once and while just to keep it fresh:
http://guides.rubyonrails.org/association_basics.html#the-types-of-associations
Rails has generally discouraged heavily nested routing, despite Github's implementation of their application:
http://guides.rubyonrails.org/routing.html#nested-resources

rails 4 and thoughtbot/clearance - adding fields to the user model

I am a relative newbie so would really appreciate any assistance.
I'm using Rails 4.2, with the Clearance gem for authentication. I'm hoping someone could describe the best practise for over-riding the controllers to include custom attributes on the sign_up form.
I've read a lot of suggestions with varied advice, many of which are from previous versions of rails that do not use strong_parameters.
If anyone could provide a brief breakdown of the methods I need to be over-riding (user_params/user_from_params/etc) I would be most grateful. I can get things functioning by defining a new 'new' method that just includes #user = User.new, and the a new 'user_params' method using .permit, but I'm concerned about the default code I'm bypassing in user_from_params.
Any advice on best practise here would be fantastic!
Thanks
First, extend Clearance::UsersController and override #user_params to permit the new attribute(s):
# app/controllers/users_controller.rb
class UsersController < Clearance::UsersController
private
def user_params
params[:user].permit(:email, :password, :your_custom_attribute)
end
end
Then, update your routes file to use your new controller:
# config/routes.rb
resources :users, controller: :users, only: :create
I'm working when I have time on updating the docs for Clearance, but right now the simplest thing to do is to inspect the Clearance users controller to see the various methods that can be overridden.
The default implementation of user_from_params is a bit odd now because Clearance 1.x still supports Rails 3.x, so it was written for a time when we used attr_accessible rather than strong parameters.
I'd probably do something like:
def user_params
# Use strong params to do what you need
end
def user_from_params
User.new(user_params)
end
You may need to massage user_params a bit so that whatever you do there is still valid for the new action, as user_from_params is still used there. I've opened an issue in Clearance to see if we can improve that aspect before 2.0.

When are Strong Parameters used?

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.

Rails 4 sharing form partials and strong parameters

I have a contact form partial that I include in two different forms on my app. I was wondering if there is a better approach to strong parameters than duplicating the permitted params in each respective controller?
Users controller:
def user_params
params.require(:user).permit(:name, :email, contact_attributes: [:city, :state])
end
User Applications controller:
def user_application_params
params.require(:user_application).permit(:gender, :birthdate,
user_attributes: [contact_attributes: [:city, :state]])
end
So ideally this code would be in one place, I'm not sure how to achieve this though.
First, let me say, whitelisting your all params automatically is a hack that might be ok in some situations but could cause some substantial security risks in others and exposes you to risk as your codebase evolves (so it's never really ok). Here's a much better way to solve the problem that is about as easy and should work in just about all situations.
Instead, write a controller concern. This is just a module that goes into your app/controllers/concerns directory.
so create app/controllers/concerns/shared_contact_permitted_attributes.rb as follows:
module SharedContactPermittedAttributes
def shared_contact_permitted_attributes
contact_attributes: [:city, :state]
end
end
Then in whatever controller you want it to show up in, add include SharedContactPermittedAttributes at the top and use the method defined in the module in your strong params. For example:
def user_params
params.require(:user).permit(:name, :email, shared_contact_permitted_attributes)
end
Then you're done!
What I ended up doing was permitting all parameters and blacklisting certain ones. The blacklist params is a lot shorter than the whitelist.
params[:profile].permit!.except(user_attributes: [role_ids: []])
This allowed me to easily reuse certain form partials.