this may be a really n00b question, but if your list of params contains a bunch of stuff that isn't an attribute accessible, ie
params = {"controller"=>"api1/users", "action"=>"create"}
what is the best way to "sanitize" your params so that they only contain the accessible attributes. The current way that I thought of currently is to do :
User._accessible_attributes[:default].entries
that gives me a list of accessible attributes and then only pass those params:
["", "email", "password", "fb_token", "fb_id", "fb_name", "first_name", "last_name", "gender"
Another possible way is to have this:
def clean_params #ANTIPATTERN
params.delete(:controller)
params.delete(:action)
end
but this also feels like an antipattern...
I know that you're supposed to do something like params[:user] to get only the accessible params, but because this is an API, it would be nice to be able to pass things just in the url.
Thanks!
The Rails parameter wrapper will do this for you automatically. That is, it will accept parameters at the top level and group them under, for example, :user for your convenience, filtering out any that are not accessible to the User model. Internally it uses accessible_attributes, similar to what you've done. People who use your API will not need to group attributes -- rails will do it before it hands the params to your controller action.
By default it's turned on for JSON requests, but you can expand that by editing initializers/wrap_parameters.rb. Or you can adjust the behavior on a per-controller basis using the wrap_parameters method in your controller.
The rails scheme of parameter sanitizing is likely to change in 4.0, trending away from the model and toward the controller. You may want to watch development of the strong_parameters gem which could be a preview of things to come.
You could do it this way... This will only sense in the parameters you want to in the controller. credit: dhh's gist
class UserController < ApplicationController
respond_to :html
def create
respond_with User.create(user_params)
end
private
def user_params
params[:user].slice(:email, :first_name, :last_name)
end
end
Related
I need to allow for customization of json output per account. One person may like the standard json response. Another may wish to have the exact same feed except they need the "name" field called "account_name" or something.
So with a standard user they may be happy with
#model.to_json(only: [:name, :title, :phone])
Another user may wish to see their json response as
#model.to_json(only: [:name (AS ACCOUNT_NAME) , :title, :phone])
I have found solutions that override the as_json/to_json in the model but those seem to be solutions that effect everyone. How can I change them just on a per access/per account basis?
I think in your case, it is better to push the logic to the view layer to make the code clean by using Jbuilder.
so instead of override to_json method you can do something like below:
# app/views/users/show.json.jbuilder
json.model_name do
json.title #current_user.title
json.phone #current_user.phone
if current_user.type_account_name? # need to implement this logic somewhere in your app
json.account_name #current_user.name
else
json.name #current_user.name
end
end
update
the controller looks something like this
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
end
I am having trouble with strong parameters in my custom controller. I understand how to use strong parameters in a new or update action. However, I can't seem to figure out if this is an unsecure use of the params[] hash or not in my custom action.
My view redirects to a controller with an order id and an operation number:
link_to 'Confirm', confirmpayment_confirm_path(order: order, operacion: order.operacion), :data => { confirm: 'Are you sure?' }
My confirmpayment controller is as follows:
class ConfirmpaymentController < ApplicationController
before_action :authenticate_user!
def lookup
authorize! :lookup, :confirmpayment
#orders=Order.where(:status => 'PENDING')
end
def confirm
authorize! :confirm, :confirmpayment
#order=Order.find(params[:order])
#order.payment_id = params[:operacion]
#order.confirm_payment_date = DateTime.now()
#order.save
end
def order_params
params.require(:order).permit(:order, :operacion)
end
end
The question is:
I am not using order_params anywhere in my confirm action, since this is not a new order. I am using the parameter to find the proper order and confirm it. Is this secure? Or am I missing something?
So to clear a few things up.
Strong Parameters is responsible for the allowed parameters which are passed to your database. It should prevent the users to modify attributes in the database which they aren't allowed to modify.
For example:
You have the following table with columns:
User:
- firstname
- lastname
- email
- password_digest
- role (user, admin)
You probably want to prevent normal users to change their role. But if you pass a parameters hash as it is to the database, he could also add a role key with value to the hash. Your application would accept it. Strong parameters checks the hash and prevent the change.
In your example above, Strong Parameters brings no advantages. You assign the values directly to the appropriate table columns. It isn't possible to modify any other attributes in the database for the user. If you don't have any other methods in your controller, you could remove the entire #order_params. Strong Parameters just raises an exception if you would try to pass a hash directly through.
However I would recommend you to search for the payment in the database before you assign it. If you assign payment_id directly you have no guarantee that payment exists. So first check for the payment and if you found it assign it to the order.
Here is how I would have done it:
class PaymentConfirmationController < ApplicationController
before_action :authenticate_user!
before_action :authorize_user! # To DRY up your code
def lookup
#orders = Order.where(:status => 'PENDING')
end
def confirm
#order = Order.find(params[:order_id])
#payment = Payment.find(params[:operation_id])
# You should catch the exceptions if an order or payment wasn't found
#order.payment = #payment
#order.payment_confirmation_date = Time.now()
#order.save
end
private
def authorize_user!
authorize! :confirm, :confirmpayment
end
end
I haven't tested the code but it should work.
Here are the Docs of Strong Parameters. They describe everything in more detail.
I hope this helps!
Happy coding :)
About the way of using parameter, I think there is nothing wrong with it. But about the security problem, you may want to think about the case that user can change any Order information by just change the order param to something that doesn't belong to him.
In that case, you will want to limit the query to Order, make he can only confirm the order that belongs to him.
Strong Parameters: prevent accidentally exposing that which shouldn't be exposed. They are generally used when you create or update a model, this to avoid entering parameters have not been allowed.
I have some suggestions:
English preferably encoding: operacion to operation
Check style
in your code ConfirmpaymentController to class
ConfirmPaymentController
you can see: best practices and style prescriptions for Ruby on Rails 4 :)
As title says, why does Rails prefer to use the #params variable inside of a Controller action when you are responding to the action instead of passing the individual parameters through the function arguments when we call the function?
Other frameworks use this (i.e, ASP MVC) and I was just wondering if there was a reason for that design decision, because it doesn't seem very intuitive.
Ie. Why does Rails do
def index
name = params[:name]
end
Instead of
def index(name)
end
The point is, most of the actions in a controller handles the view REST-fully. The params comes from the user's browser when they interact with the page or send a new variable request to the page.
These requests are variable, and Rails makes it uniform by maintaining the parameters in params hash. If the following GET requests arrive:
http://localhost:3000/products?color=red&quality=best
the params hash will automatically be populated as {'color' => 'red', 'quality' => 'best'}. Rails doesn't expect your action to manually handle the parameters.
Similarly, consider you are getting a POST request from a page where a user filled a form. In that scenario, the params obtain the parameters which are composed with form helpers inside views.
Though in hyptothetical case you are dealing with general methods instead of actions, such as below, you will have to do it by passing arguments.
def show
if params['color'] == 'red'
#product = obtain_product('red')
else
#,..
end
end
def obtain_product(color)
Product.where('color = ?', color).first
end
Hope it is clear. :)
#kidorrails has a great answer, and I wanted to add to it:
If you wanted to pass the params to each method directly, it would go against the #1 Rails convention - keep it DRY. By having a separate params hash, you not only have access to all the params you want, but you can access them through as many methods as you need
For example, take strong_params:
#controller
def new
#model = Model.new
end
def create
#model = Model.new(strong_params)
#model.save
end
private
def strong_params
params.require(:model).permit(:your, :params)
end
As #apneadiving mentioned, the params hash is created in another part of the stack, meaning it's available over all the methods required. It's most efficient & versatile way to do it IMO
questions_controller.rb
def index
#questions = Question.all(app_params)
end
private
def app_params
params.require(:questions).permit(:question, :answer)
end
end
question.rb
class Question < ActiveRecord::Base
end
I am completely new to ruby-on-rails. I was following a guide and it said I should take care of some "loopholes" or "security issues" and it used attr_accessible, but on Rails 4, they suggest strong parameters, so now I'm trying to use them. I'm confused on how to define the :questions params, because I'm currently getting an error saying that :questions param is not found.
:questions is pretty much something that I will define myself as the web developer.
So for example, I will define questions = "How are you?", "What is your name?". I'm basically starting very simply. I want questions that I have created to be displayed on my webpage. Ultimately, I plan to make a website what is basically a list of questions and, with answer options. After the user clicks "submit" I want to store the information into my database.
Am I supposed to even be requiring this as a param? I'm completely lost..
Do you have a dump of the params we could look at? They are shown when your app encounters an error, and typically shows you the params array which rails will pass through
Strong Params In Rails 4
Strong Params allow you to allow certain parameters for use in the controller, protecting against any malicious assignment client-side. They replaced attr_accessible in Rails 4.0
Strong Params is only for user-submitted content, as it's designed to protect the params hash. To that end, it's mostly used with the create and find functions:
class PeopleController < ActionController::Base
# Using "Person.create(params[:person])" would raise an
# ActiveModel::ForbiddenAttributes exception because it'd
# be using mass assignment without an explicit permit step.
# This is the recommended form:
def create
Person.create(person_params)
end
# This will pass with flying colors as long as there's a person key in the
# parameters, otherwise it'll raise an ActionController::MissingParameter
# exception, which will get caught by ActionController::Base and turned
# into a 400 Bad Request reply.
def update
redirect_to current_account.people.find(params[:id]).tap { |person|
person.update!(person_params)
}
end
private
# Using a private method to encapsulate the permissible parameters is
# just a good pattern since you'll be able to reuse the same permit
# list between create and update. Also, you can specialize this method
# with per-user checking of permissible attributes.
def person_params
params.require(:person).permit(:name, :age)
end
end
params.require
The params.require function works by taking this params hash:
params{:question => {:question => "1", :answer => "5"}}
That's why people asked what your params hash looks like, because the require function can only work if the :question hash is present.
Possible Solutions For You
Question.all(app_params)
Regardless of what you're trying to achieve, don't use all. The where function is better for receiving an array of data based on certain values. I believe all is depreciated anyway.
def index
#questions = Question.where("value = ?", variable)
end
What data is being passed?
I will define questions = "How are you?", "What is your name?"
This is okay, but typically in rails, you'd call data by using an ID in the database. If you're defining these questions in a form, you'd use the strong params system; but you'd need a form to submit the data to
Further Additions
The rails way is to keep all your data in a database, and use the application to manipulate that data, either by showing it, or allowing people to input more.
The "params" variables are basically there to help the rails controllers & models accept & process data from end users, and consequently allow you to keep the system growing. Instead of having to write custom code to accommodate all sorts of different data, the params give you a rigid structure to work with. Here is a good explaination of how MVC (and params) works for you: How does an MVC system work?
I think you're getting confused with how your app should work
Your "questions" should be stored in a questions table / model, and can be accessed by calling their ID's with the find function. This code would be like this:
#app/controllers/questions_controller.rb
def show
#question = Question.find(params[:id])
end
If you want to add new questions, you'll be best to add them to the questions table, like this:
#app/controllers/questions_controller.rb
def new
#question = Question.new
end
def create
#question = Question.new(question_params)
#question.save
end
private
def question_params
params.require(:question).permit(:question)
end
#app/views/questions/new.html.erb
<%= form_for #question do |f| %>
<%= f.text_field :question %>
<% end %>
This will give you a central store of your questions, which you'll then be able to access when you need them, either with a helper or with your ".all" call :)
Give it a shot with question (singular):
params.require(:question).permit(:text, :answer)
Assuming question is your model and text (which I made up) is the wording of the question.
I am dealing with a very simple RESTful Rails application. There is a User model and I need to update it. Rails coders like to do:
if #user.update_attributes(params[:user])
...
And from what I understand about REST, this URL request should work:
curl -d "first_name=tony&last_name=something2&v=1.0&_method=put" http://localhost:3000/users/1.xml
However, it's quite obvious that will not work because each URL parameter will be parsed to the variable "params" and not "params[:user]"
I have a hackish fix for now, but I wanted to know how people usually handle this.
Thanks
It's just a matter of how Rails parses parameters. You can nest parameters in a hash using square brackets. Something like this should work:
curl -d "user[first_name]=tony&user[last_name]=something2&v=1.0&_method=put" http://localhost:3000/users/1.xml
This should turn into
{:user=>{:last_name=>"something", :first_name=>"tony"}}
in your params hash. This is how Rails form helpers build the params hash as well, they use the square brackets in the form input tag name attribute.
It's a tradeoff; You can have slightly ugly urls, but very simple controller/models. Or you can have nice urls but slightly ugly controller/models (for making custom parsing of parameters).
For example, you could add this method on your User model:
class User < ActiveRecord::Base
#class method
def self.new_from_params(params)
[:action, :method, :controller].each{|m| params.delete(m)}
# you might need to do more stuff nere - like removing additional params, etc
return new(params)
end
end
Now on your controller you can do this:
class UsersController < ApplicationController
def create
#handles nice and ugly urls
if(params[:user]) #user=User.new(params[:user])
else #user = User.new_from_params(params)
end
if(#user.valid?)
... etc
end
end
end
This will handle your post nicely, and also posts coming from forms.
I usually have this kind of behaviour when I need my clients to "copy and paste" urls around (i.e. on searches that they can send via email).