If I have a user
def user_params
params.require(:user).permit(:name, :age)
end
I got that down. I want to batch create users. So a user can fill out a list (theoretically endless) of users, they would come in as:
[{name: "name", age: 12},{name: "name", age: 22},{name: "name", age: 32}]
Question is, how do I use strong parameters for that? I know that I can just loop through the array and create the records, I get that. My understanding is that strong params are a generally good idea, safety wise.
What are strong params protecting me from? What would I be opening myself up to here, if I just looped over the array of users? How can I do it properly, either with strong params, or an alternate method?
The entire point of strong parameters (introduce in rails 4) with the goal of protecting applications from mass assignment vulnerabilities. Like for example, lets say you had a User model and it had a admin attribute. If you were using mass assignment in theory someone could slip in a value for the admin attribute if you did not filter it out some how; see below
class UserController < ApplicationController
def create
#{name: 'Joe', score: 7, title: 'Mr', admin: true} params hash
User.create(params)
end
end
Now if some how a user of your app passed in these values they just made themselves and admin and can do as they please. So thats why you would use strong params to do this.
class UserController < ApplicationController
def create
User.create(user_params)
end
def user_params
params.require(:name).permit(:title, :score) #noticed admin is not allowed
end
end
Now to create multiple records with strong params you could do this
class UserController < ApplicationController
def create
user_params[:users].each do |u|
User.create(u)
end
end
def user_params
params.permit(:users, array: [:name, :age])
end
end
Related
I am not sure if I understand the concept of strong parameters correctly. I should use strong parameters to params that I will use only to edit some data? Or I should use them for every params I want to get in controller? For example I want to get data between two dates, so I need date1 and date2 as params. Should I use here strong params or not?
The easiest way to understand when you should use strong parameters is to understand what a mass assignment volunerability is. In Rails 3 you could do the following:
class CreateUsers < ActiveRecord::Migration[3.0]
def change
create_table :users do |t|
t.string :email
t.string :encrypted_password
t.boolean :admin
t.timestamps
end
end
end
class UserController < ApplicationController
def create
#user = User.new(params[:user])
if #user.save
redirect_to #user
else
render :new
end
end
end
Here we are just passing a "hash" (its actually an ActionController::Parameters instance) straight into the model. All a malicous user has to do here is request:
POST /users?users[admin]=1
And they have created an admin account. In 2012 Egor Homakov famously exploited one such loophole in Github to commit to the Rails repository.
Such an attack is trivial to perform with cURL or by using the web inspector to manipulate a form.
If we whitelist which attributes the user should be able to pass:
class UserController < ApplicationController
def create
#user = User.new(
params.require(:user)
.permit(:email, :password, :password_confirmation)
)
if #user.save
redirect_to #user
else
render :new
end
end
end
Then this avoids the vulnerability - strong parameters is really just a simple DSL for slicing and dicing nested hash like structures. What changed in Rail 4 is that when you pass a n instance of ActionController::Parameters to a model an exception is raised unless calling #permitted? on the parameters object returns true. This avoids a mass assignment vulnerability from occuring simply due to programmer laziness or ignorance.
It does not sanitize your inputs in any other way. Like for example it won't prevent SQL injection or remote code execution if you treat user input carelessly.
You don't need strong parameters if you're passing parameters one by one like in this very contrived example:
class UserController < ApplicationController
def create
#user = User.new do |u|
u.email = params[:user][:email]
u.password = params[:user][:password]
u.password_confirmation = params[:user][:password_confirmation]
end
if #user.save
redirect_to #user
else
render :new
end
end
end
Let's say I have a User Model with a class method create_with_info. Currently if I want to password the params into the method using keyword parameters, It will be something like this.
# user_controller.rb
def create_with_info
User.create_with_info(**user_info_params)
end
private
def user_info_params
params.require([:name, :age, :email])
params.permit(:name, :age, :email).to_h.symbolize_keys
end
# user.rb
def self.create_with_info(name:, age:, email:)
# do something
end
I'm not sure is it the correct way to use keyword parameters in controller or is there a better way to handle? using to_h.symbolize_keys is annoying for me.
I have a rails 4 app.
When creating permitted params in a controller where there is a belongs_to association with another model, do you need to include the foreign key in the permitted params, so that it can be updated when the record is saved, or is that automatic?
It's NOT automatic.
In Rails 4, you have to permit the attribute in order to be able to mass assign its value. The foreign key of the other model is an attribute of your current model that you're trying to update. Without permitting that, you can't update it's value.
The foreign key is not automatic, the associated object is:
This means the following is true:
#app/controllers/your_controller.rb
class YourController < ApplicationController
def create
#item = Item.new new_params
#associated = Associated.find x
#item.associated = #associated #-> this always works & will save
#item.save
end
private
def new_params
params.require(:item).permit(:name, :etc) #-> foreign_key would have to be explicitly defined here if associated_id was passed from a form
end
end
This should give you some perspective on what you can do with your objects.
Update
If you want to assign a post to the current user each time, you'd be able to use the following:
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def create
#post = Post.new post_params
#post.user = current_user # -> however you identify the user
#post.save
end
end
I do have My users model in Rails4 application and I have Defined
def user_params
params.require(:user).permit(:email)
end
but I am also storing users address in a separate address table and I am filling up email and address both from a single form so how do I add address parameters as well in users strong parameters permit method.
Like so:
def user_params
params.require(:user).permit(:email, address: [:address_attribute])
end
Take a look at THIS post, I think it is pretty good at explaining strong parameters.
Strong parameters should look like this:
def user_params
params.require(:user).permit(:email, addresses_attributes: [:field1, :field2,..])
end
And also make sure that
user.rb
accepts_nested_attributes_for :addresses
Most Rails tutorials show how to populate a model class via the params hash like so:
class UsersController < ApplicationController
def create
#user = User.create(params[:user])
# more logic for saving user / redirecting / etc.
end
end
This works great if all the attributes in your model are supposed to be strings. However, what happens if some of the attributes are supposed to be ints or dates or some other type?
For instance, let's say the User class looks like this
class User < ActiveRecord::Base
attr_accessible :email, :employment_start_date, :gross_monthly_income
end
The :email attribute should be a string, the :employment_start_date attribute should be a date, and the :gross_monthly_income should be a decimal. In order for these attributes to be of the correct type, do I need to change my controller action to look something like this instead?
class UsersController < ApplicationController
def create
#user = User.new
#user.email = params[:user][:email]
#user.employment_start_date = params[:user][:employment_start_date].convert_to_date
#user.gross_monthly_income = params[:user][:gross_monthly_income].convert_to_decimal
# more logic for saving user / redirecting / etc.
end
end
According to the ActiveRecord documentation, the attributes should automatically be typecasted based on the column types in the database.
I would actually add a before_save callback in your users model to make sure that the values you want are in the correct format i.e.:
class User < ActiveRecord::Base
before_save :convert_values
#...
def convert_values
gross_monthly_income = convert_to_decimal(gross_monthly_income)
#and more conversions
end
end
So you can just call User.new(params[:user]) in your controller, which follows the motto "Keep your controllers skinny"