Rails 4 Strong parameters for associated model - ruby-on-rails

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

Related

Rails: Using Strong Params as keyword parameters

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.

How to do strong parameters with an array of objects

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

'Unknown attribute' error in controller

I want to make my own simple authentication.
So I made User model and Users controller. And I have a form for sign up
Part of my Users controller.
def create
#user = User.new(user_params)
if #user.save
redirect_to root_path, notice: "You've successfully singed up."
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:username, :password, :password_confirmation)
end
And error:
unknown attribute: password
In my Users table I have username and password_digest for each User. So there're not column like password.
I've just watched Rails Cast episode but it is about Rails 3 and author uses attr_accessible but I've read in Rails 4 we should use strong parameters.
How I can deal with it?
It is likely that you are using has_secure_password and you have not enabled it correctly. has_secure_password adds two virtual attributes to your model: password and password_confirmation. The error unknown attribute could mean that those two attributes are not being set correctly on your model.
You should under no circumstances pass the password_digest as a param since BCrypt and has_secure_password use this column to compute a hashed version of your password attribute. In other words it has no business being in the form.
Make sure you have:
Included the BCrypt gem in your gemfile
Correct included the has_secure_password module in your user model.
Your use of strong_parameters is correct. The problem is in your model, likely to do with has_secure_password, and not strong_paramters. So this line is correct:
def user_params
params.require(:user).permit(:username, :password, :password_confirmation)
end
Strong parameters filter params to avoid mass assignments until they have been whitelisted.
The permit method identifies the list of allowed parameter and must correspond to your model's attributes.
So, in your case you should write
def user_params
params.require(:user).permit(:username, :password_digest)
end

Add attributes to an instance using strong params

The prime example of this feature is when creating a user. Instead of saving 'password' to the database you want to take an instance's password and create a password_hash and password_salt from it.
So if I'm creating a form where a user can be created, how can I have a password field if there's no 'password' attribute?
I think previously this could be solved by using attr_accessor in the user model, but I don't know how to do this with strong params:
def user_params
params.require(:user).permit(:username, :email, :password, :password_confirmation)
end
Just results in a UnknownAttributes error when trying to create a new instance via the user_params:
#user = User.new(user_params)
If you want to add to your strong params, you can use the .merge method
private
def user_params
params.require(:user).permit(:x, :y, :z).merge(hair_color: "brown")
end
Alternatively, if you're looking to manipulate the data on save, you'd probably use the ActiveRecord callbacks, namely before_create:
#app/models/user.rb
Class User < ActiveRecord::Base
before_create :hash_password
private
def hash_password
if password #-> == self.password
#hash the password
#runs after validations
#runs just before DB insert
end
end
end

Rails strong parameters incremental addition

Does anyone know, if with strong_parameters gem, we can incrementally add attributes like below:
def user_params
params[:user].permit(:name, :email)
if current_user.admin?
params[:user].permit(:is_admin)
end
end
Here I am incrementally asking the code to permit :is_admin parameter if the current user is an admin. Shouldn't it just add to the previously permitted list of parameters (:name and :email)?
They way I have done it is to put all my params in a class Like the strong-parameters railscast..
this way I have something like this
class PermittedParams < Struct.new(:params,:admin)
def administrator_attributes
allowed = [:email, :name, :password, :password_confirmation, :password_digest]
if admin.has_any_role? :realm_admin, :system_admin
allowed << :active << :roles
end
allowed
end
.... other models ....
def method_missing(method,*args,&block)
attributes_name = method.to_s + '_attributes'
if respond_to? attributes_name, false
params.require(method).send(:permit, *method(attributes_name).call)
else
super
end
end
end
then in the controller just call #administrator.update_attributes(permitted_params.administrator)
as it is just an array you can build up the array, and then just use * to pass it into the permit.
That's an interesting question. I am not sure, but my gut reaction in this situation would simply be to test it out. Whether it breaks or not, you'll have your answer.
I have a User model with an expiration date parameter that I would like only administrators to be able to set. There is no way to incrementally add parameters to the permit list...so you have to break DRY and do it like this...
if can? :manage, :all
params.require(:user).permit(:first_name, :last_name, :email, :expiration_date)
else
params.require(:user).permit(:first_name, :last_name, :email)
end

Resources