How can request params be validated to ensure they include required params and don’t include unsupported params? - ruby

This is particularly in the context of a REST API built with Ruby and Sinatra.
It's easy enough to manually check to make sure that the required params are not nil. And it's easy to iterate through a flat params hash to see if it's allowed in a whitelist.
However, when the params hash also include hashes it becomes more difficult.
One way of handling this I've thought of is converting the params hash to JSON and using a library to validate it against a JSON schema.
I have come across the sinatra-param gem but I haven't had a chance to see if it can validate sub-hashes or check for unsupported params.
Edit: Another possible way, that might make more sense is passing params directly to the model (I'm using DataMapper) and using its validation and errors instead of rewriting validations.

If each of your routes are going to take the same 4 params (IE :one, :two, :three, :four), you could set up a before filter, store an array of those four params as an instance variable in the before (which is accessible to all routes) and use a sexy little method from class Enumerable called all?:
before do
#base_params = [params[:one], params[:two], params[:three], params[:four]]
unless #base_params.all?
redirect '/error_route'
end
end
Enumerable#all? will return true only if all values in your 'collection' are not false or nil. Documentation can be found here for Ruby 1.9
Additionally if you find that you have different sets of params, you can create a hash instead of just an array of #base_params where they keys are the string value of request.request_method:
before do
#base_params = {"GET" => [params[:one], params[:two], params[:three], params[:four]],
"POST" => [params[:five], params[:six], params[:seven]],
"PUT" => [params[:one], params[:five], params[:six]]}
unless #base_params[request.request_method].all?
redirect '/error_route'
end
end

Related

Putting instance method results (with arguments) into active_model_serializers

I'm making a JSON API and want to serialize a recipe record from my database. In the JSON response, I want to include the output of #recipe.is_favorited_by?(current_user) along with some of the recipe's attributes.
I saw from the docs that I could call: render json: #recipe, methods: :is_favorited_by? - but I'm not sure how to pass in current_user as an argument.
What call would I need to make in order to include the results of #recipe.is_favorited_by?(current_user) in the response?
You can add current_user attribute to your model & set it before your json execute.
See stackoverflow question link Ruby to_json :methods arguments

Supporting multiple JSON content types in Grape

I’ve been using Grape to write an Evernote-like API, and have been using Collection+JSON (MIME type "application/vnd.collection+json"). I now want to also support JSON API (mime type "application/vnd.api+json").
(The reason I want to do this is so it will be easier to write an Ember client, since Ember Data has support for JSON API built in. It might make more sense to write client-side JavaScript to solve my problem, but I’m stronger in Ruby than JavaScript.)
My goal is to have the server support either format, and choose which one based on the Accept header. Is this a reasonable thing to do, or does it not make sense to have multiple JSON types? It appears that Grape doesn’t support this. If I just have the wrong idea, then the code below is probably not relevant to that answer.
Here are some relevant pieces of my server code:
class NotesServer < Grape::API
content_type :json, ‘application/json’
content_type :json_api, 'application/vnd.api+json'
formatter :json_api, lambda { |object, env| object.to_json_api }
content_type :collection_json, 'application/vnd.collection+json'
formatter :collection_json, lambda { |object, env| object.to_collection_json }
resource :notes do
desc 'Get a note.'
params do
requires :id, type: Integer, desc: 'Note ID.'
end
route_param :id do
get do
NoteRepresenter.new Note.find(params[:id])
end
end
end
…where NoteRepresenter defines both to_json_api and to_collection_json.
The idea is to use the same Representer in either case, but to call the appropriate method depending on the Accept header.
When I run curl against this with any Accept header, the response has Content-Type: application/json, and “to_json” is called on the representer. If I leave out the content_type :json, ‘application/json’ line, I get a 406 Not Acceptable response every time.
I figured out that in the Grape library, in lib/grape/middleware/formatter.rb, the "format" of a request is considered JSON for any of these MIME types, since the vnd.collection+ or vnd.api+ parts are parsed out of the header. So my question is, is this a matter of Grape not supporting what I’m trying to do, or am I trying to do something that makes no sense?
I found a reasonable way to do this that I’mr reasonably happy with, but I don’t think it totally aligns with how Grape expects you to do things.
I created a custom formatter called JsonFormatter, so my server code looks like this:
class NotesServer < Grape::API
formatter :json, JsonFormatter
# …
end
And the formatter itself:
module JsonFormatter
def self.call(object, env)
case env['HTTP_ACCEPT']
when 'application/vnd.api+json'
object.to_json_api
when 'application/vnd.collection+json'
object.to_collection_json
else
object.to_json
end
end
end
So now either to_json_api or to_collection_api (or to_json) will be called on the presenter object, depending on the Accept header. One issue I haven’t solved yet is that the Content-Type is set to application/json in all cases.

Are find_by_[dynamic] Activerecord methods safe for unsanitized input?

I am using ActiveRecord and Sinatra (probably irrelevant context). ActiveRecord has a dynamic finder method find_by_[column]
I would like to pass a raw parameter string from HTTP directly into one of these methods. I'm pretty sure that ActiveRecord will sanitize the string I pass in. Right? Here's some example code to show what I want to do:
post '/login' do
user = User.find_by_username(params[:username])
...
end
Is it problematic to pass unsanitized http parameters into this kind of ActiveRecord method?
More generally, how should one go about figuring out which ActiveRecord methods sanitize their inputs? I keep reading this site and getting scared! http://rails-sqli.org/
Yes, they are safe. Because the dynamic finders are simply translated into the normal hash form. Find the implementation at github
Note that the new syntax (since version 4) is: find_by(username: params[:username])
To be sure if the user exists you can try something like:
if User.exists? :username => params[:username]
user = User.find_by_username(params[:username])
end
This greatly reduces the chances of SQLi.

How to validate mutual exclusivity of params in Grape API (Ruby)

When defining an API using Grape, there is a very convenient way of validating the presence and type of parameters, e.g.:
params do
requires :param1, type: String
optional :param1, type: Integer
end
However I can't see a convenient way of specifying that two parameters are mutually exclusive. EG it would be something like:
params do
requires :creatureName, type: String
requires
either :scaleType, type: String
or :furType, type: String
end
I'm interested in suggestions for the most convenient way to get around this.
You can also use exactly_one_of:
params do
optional :scale_type
optional :fur_type
exactly_one_of :scale_type, :fur_type
end
at_least_one_of, all_or_none_of are also available.
More about it here
Parameters can be defined as mutually_exclusive, ensuring that they aren't present at the same time in a request.
params do
optional :beer
optional :wine
mutually_exclusive :beer, :wine
end
Warning: Never define mutually exclusive sets with any required params. Two mutually exclusive required params will mean params are never valid, thus making the endpoint useless. One required param mutually exclusive with an optional param will mean the latter is never valid.
Looking for the same thing. Your question got me to open the hood and see if it could be done. Here's the pull request

Local variables in a block that are not passed as parameters [duplicate]

This question already has answers here:
Rails params explained?
(5 answers)
Closed 9 years ago.
I'm trying to understand how to make a variable available to a block that is not passed to the block as a parameter.
For example, how does Sinatra make params hash available?
get '/hello/:name' do
howAmIAccessingThis = params[:name]
end
Where is params coming from? This:
get '/hello/:name' do |params|
#hisName = params[:name]
end
might make sense because params is declared as a block argument, but that's not how it works. Looking through the source I cannot find how the params hash is getting passed to the block without it being a block parameter.
If it is not a local variable or a block variable, then it is a method. I don't know about Sinatra, but there must be a method params defined somewhere.
Using Parameters
Parameters in Sinatra are like everything else--simple and straightforward.
#!/usr/bin/env ruby
require 'rubygems'
require 'sinatra'
get '/hello/:name' do
"Hello #{params[:name]}!"
end
Once you've made this change, you'll need to restart the Sinatra application. Kill it with Ctrl-C and run it again. (There's a way around this, but we'll look at that in a future article.) Now, the parameters are straightforward. We've made an action called /hello/:name. This syntax is imitating what the URLs will look like, so go to http://localhost:4567/hello/Your Name to see it in action.
The /hello portion matches that portion of the URL from the request you made, and :name will absorb any other text you give it and put it in the params hash under the key :name. Parameters are just that easy. There is of course much more you can do with these, including regexp-based parameters, but this is all you'll need in almost every case.
Reference: http://ruby.about.com/od/sinatra/a/sinatra2.htm
EDIT
params values can come from the query string of a GET request, or the form data of a POST request, but there's also a third place they can come from: The path of the URL.
As you might know, Rails uses something called routes to direct requests to their corresponding controller actions. These routes may contain segments that are extracted from the URL and put into params. For example, if you have a route like this:
match 'products/:id', ...
Then a request to a URL like http://example.com/products/42 will set params[:id] to 42
So, whenever an URL GET, POST or Path contains such pattern then params hash is automatically constructed by rails.
Also check the Parameters section(Section 4) here

Resources