Rejecting extra url parameters from params hash in rails - ruby-on-rails

I am building an API using rails that can take quite a few parameters. Ideally, if the user includes an extra, unnecessary paramater in the request by accident which does not make the request ambiguous I would like to process it as normal. This obviously prevents me from simply writing
ApiRequest.new(params)
since an UnknownAttributeError will be thrown if there is anything extra in the params hash. Is there a simple way of rejecting the extra attributes or will I have to write a method to manually validate the request before creating a new object. Also, would this be considered bad practise for an API, should I be responding with HTTP 400 if this occurs?
Thanks,
Tom

Thanks for the suggestion Sergio, I ended up adding the following initialize method in my ApiRequest class which allows me to mass assign regardless of what is in the hash. If anyone uses this just make sure you ensure attributes are attr_protected if they need to be.
def initialize(params)
params.delete_if {|k| !self.respond_to? "#{k}="}
super
end

Related

Why we usually dont use strong parameters for http GET

I've seen strong parameters used for write HTTP verbs,
for example in rails
User.new( params.require(:user).permit(:name) )
but usually you wont see this kind of param whitelisting for HTTTP GET, why? i imagine it must be because of writes are more sensitive, but can't think of specific explanation.
Strong parameters were added to make sure that no model attributes would be updated which you did not specifically allow. If a user is able to change his/her website URL, you probably don't want him/her to change the role_id attribute, which would be trivial if you did not sanitize the input.
GET requests are typically not used to create or update models.

Rails 5 Params / Strong Params issue

I have a RESTful API (ActionController::API) service that accepts a list of params. The RESTful service takes a JSON body request (with Content-Type of application/json). When I debug the controller/action (right after the action def) and take a peak at the params list, it appears as follows:
<ActionController::Parameters {"given_name"=>"Mark", "subdomain"=>"development", "controller"=>"user", "action"=>"create", "user"=>{"given_name"=>"Mark"}} permitted: false>
EDIT
All the request has in it (when it is passed to the controller/action -- using POSTman):
{"given_name":"Mark"}
Notice that the object contains the given_name params twice. Is this normal behavior? I did not pass a "user" object (json object) to the controller/action? What is the permitted flag?
When I try to use (right now I'm just testing RESTful call and assigning any values that the user object will except... no validations have been programmed yet):
user = User.new(params)
I get the error:
#<ActiveModel::ForbiddenAttributesError: ActiveModel::ForbiddenAttributesError>
So, I've looked everywhere for the reasoning behind this (why is there a "user" key in the params list? What is the purpose of the permitted flag? Why am I getting an error when I try to assign params)?
EDIT
After doing some testing, I change the controller name from "user_controller" to "tester_controller" and setup the routes to point to the renamed controller.
It seems the "user" object in the params list above has changed to "tester". So why does the param list contain an "object" with all the passed params with the name of the controller? If that's the case, why is it needed?
Any help would be greatly appreciated.
By default Rails in API mode wraps JSON request parameters into a hash guessing its name from controller class. That's why changing the controller from User to Tester changes "object" name. You can read details here.
So if you don't need this "object" in your params just remove :json from :format array in config\initializers\wrap_parameters.rb. Or you can use fine-grained control at the controller level as described above.
You need to specify which attributes are acceptable for mass-assignment in your controller.
def create
#user = User.new(params.require(:user).permit(:given_name))
end
This prevents malicious users from making request posts that alter attributes internal to your application, like role in the case of a user object.
As mentioned above, a better explanation can be found in the guide referring to strong parameters.
You can't pass params to constructor, because params always contain :action and :controller keys. Attributes for new objects should be put in hash under key that identify model you want to create e.g. :user. I suggest you to consult rails guides, especially chapter 7 of "Form Helpers" guide.
If you want to learn more about strong parameters there is a chapter in rails guides for that too :)

What's the absolute path to current request object in Rails environment?

I'm searching for the current request of class Rack::Request to find the params. Suppose I've spawned a debugger in my model, I don't want to send a new request, but still find my params.
I couldn't find any class attributes, that would store current request, which is reasonable.
I don't know how to find any instances of ApplicationController or Rack::Server, which might contain the info.
Also, peaking into the log is considered too much effort, so I'd like the effort to be concentrated on finding the request object, not telling me to grep/search through log.
In hopes of being able to be lazy,
Love Dzhon.
It's possible I'm misunderstanding your question, but from within a controller you can simply access a request object to get its details, and params to get the params.
ItemController
def show
#page_variable = request.inspect + params.inspect
end
end
If you want to make the request object available to your models you can create a class accessor and store it at the beginning of any action (via a before_filter in the application controller) for example. More details why here.

What is the best way to give a model access to params hash in Rails?

This appears to be a violation of MVC, so I'll explain what I am trying to do:
My model makes a call to an API, and the URI of the API depends on the params hash. (The params hash stores the URL of the Rails app).
I created a module to mix into the model (because the model has nothing to do with the API call), but haven't figured out how to get the params into the module.
If possible, I would like to initialize the module with the params hash, but don't know where to do it. before_create on the model would work, but that is in the model.rb file which doesn't know about the params.
Couple ideas, depending on your needs:
Pass the parameters to the model with each call, if they change that much. You could do a class method or instance method -
Model.api_call params[:field]
#model.api_call params[:field]
Save the parameters as class variables in the model:
Model.set_parameters(params)
def self.set_parameters(params)
##params = params
end
# access it in methods with ##params

Encrypting Parameters across controllers

I need to pass a parameter from one method in a controller to another.
From my understanding I have to pass the parameters as a GET exposing it in the url string.
What is the best way to encrypt the data so no one can see what is actually getting passed in the string? Also, is there a way to pass it via POST or is my original understanding correct?
I haven't used RoR, but in the web world, this problem is solved with sessions. Using sessions you can store the parameters on the server and avoid sending sensitive data with GET or POST (both are insecure).
The Ruby on Rails Security Guide looks like a great read related to this.
I suggest you abstract your code into lib/ so that you don't have to call additional methods. Instead of making a new HTTP request, just put the code in a central place and call it from there.
class MyController < ApplicationController
def index
MyLibrary::Thing.do_stuff
end
def show
MyLibrary::Thing.do_stuff
end
end
# lib/my_library/thing.rb
module MyLibrary
module Thing
def self.do_stuff
# do stuff!
end
end
end
That way you can access the same code in multiple actions, without doing extra HTTP requests.

Resources