Ruby on Rails: how to use OAuth2::AccessToken.post? - ruby-on-rails

OAuth2::AccessToken.post() method is specified like this in the documentation:
(Object) post(path, opts = {}, &block)
I'm trying to pass some arguments, but seems that I*m doing it wrong:
response = token.post('/oauth/create.js', {:title => "title", :description => "desc"})
The parameters are never reaching the method, values are always nil. So, what is the correct way of using the post method with arguments? And what is that &block?
I'm also getting WARNING: Can't verify CSRF token authenticity. This might be contributing to the problem as well. The case is that I'm using OAuth api from the outside of the app. OAuth 2 is implemented via Doorkeeper gem.
Update: The CSRF warning is gone now after I defined scopes. Also I manage to use this post() method with arguments by providing the as part of the url: "?title=test&...". Still would be nice to know how to use this method as documented.

The body in a POST or PUT is accessed via the options body param. No documentation on this. Had to look in the oauth client code itself to discover this:
https://github.com/intridea/oauth2/blob/ebe4be038ec14b3496827d29cb224235e1c9f468/lib/oauth2/client.rb
Your example, with correct body would be:
response = token.post('/oauth/create.js', {body: {:title => "title", :description => "desc"}})

You can use the block to pass parameters to post request:
token.post('/oauth/create.js') do |request|
request.params['title'] = "something"
end
OAuth2 gem uses faraday, the request object is a faraday request, so you might want to check other ways to pass parameters along with the request
faraday gem => https://github.com/lostisland/faraday

Related

Rails 5: Attempting to generate a URL from non-sanitized request parameters - How to find params to whitelist

I'm using Rails 5 and this link
<%= link_to 'Pdf', payments_path(params.merge(format: :pdf)), :target => "_blank" %>
causes:
Attempting to generate a URL from non-sanitized request parameters! An
attacker can inject malicious data into the generated URL, such as
changing the host. Whitelist and sanitize passed parameters to be
secure.
I have seen few questions on this issue already and how is the .merge that causes this.
For a while I just used params.permit! to avoid to face the problem but obviously that's not a solution.
So I understand I have to whitelist necessary params.
Isn't enough to create the usual:
def whatever_params
params.require(:whatever).permit(.....)
end
and whitelist all necesssary params?
I'm new to rails and so far I whitelisted params for forms, so regarding POST parameteres. In that case I just include params used in form fields. But I understand this is regarding params on url, so query string parameters. So is this regarding params passed to url from ransack or will_paginate (gems I'm using)? This confuses me..
How do I exactly check which params need to be whitelisted to avoid to receive that error?
1.Yes. It's enough to create simple method like whatever_params and use params.require(:whatever).permit(..) or params.permit(...)
2.Move all params that you use to whitelist. For example, you have GET request with tons of params but use only some of them and they are optional:
def my_params
params.permit(:category, :name, :age) #params that you use
end

Verify if a request is GET / POST

I am using the twitter gem for ruby and need to send a POST request to users/lookup endpoint.
As per the gem source code documentation(https://github.com/sferik/twitter/blob/4e8c6dce258073c4ba64f7abdcf604570043af71/lib/twitter/rest/users.rb), the request should be POST by default, unless I pass :get :
#option options [Symbol, String] :method Requests users via a GET request instead of the standard POST request if set to ':get'.
def users(*args)
arguments = Twitter::Arguments.new(args)
request_method = arguments.options.delete(:method) || :post
flat_pmap(arguments.each_slice(MAX_USERS_PER_REQUEST)) do |users|
perform_with_objects(request_method, '/1.1/users/lookup.json', merge_users(arguments.options, users), Twitter::User)
end
end
I am calling it as follows:
users = #client.users(twitter_screen_names_arr, [:method, :post])
However, I am not sure if this is actually resulting in a POST request / a GET request.
How can I make sure if this is a POST/GET? I would like to print the request that is being made to get a clarity on what actually gets sent.
Thanks!
As you can see from the code it uses POST by default. This behavior is also specified with RSpec.
You can invoke the users method like this:
#client.users(twitter_screen_names_arr, :method => :post)
or simply
#client.users(twitter_screen_names_arr)
since POST is the default request method.
If you don’t trust the code or the specs, you could run the request through a proxy to verify this behavior manually.

How to build a proxy controller and faraday

I have to built an angular js application as a client to consume the api.
The problem is that the api doesn't support jsonp calls.
So I've created a rails application that makes the calls to the api and returns the content.
I'm using the faraday gem
Right now I have a method for each call to the api. But since every method only creates a request, triggers the request and returns the content.
I'm was wandering if I can create a proxy controller that creates the request based on what it receives then creates an request with faraday and returns the result. Something like this :
def proxy_request
if request.method_symbol == :get || request.method_symbol == :delete
line 7: response = faraday_conn.run_request(request.method_symbol, request.fullpath, nil, request.headers)
elsif request.method_symbol == :post || request.method_symbol == :put || request.method_symbol == :patch
response = faraday_conn.run_request(request.method_symbol, request.fullpath, request.body.read, request.headers)
end
render :text => response.body, :status => response.status, :content_type => response.headers["Content-Type"]
end
This is not working. What I'm doing wrong ?
It always fails with
NoMethodError (undefined method `strip' for #<StringIO:0x3436848>):
app/controllers/api_proxy_controller.rb:7:in `proxy_request'
The backtrace is incomplete (some frameworks, e.g. Rails/RSpec, may hide lines coming from outside your project by default). The error actually came from Net::HTTP (net/http/header.rb:17), at least in my case (Ruby 2.1, Rails 4.0.2 and the default Faraday adapter). It expects all values in headers to be strings (or at least respond to strip).
One workaround is to exclude any header with a non-string value. Depending on what you want to do, this may or may not work for you, e.g. something like:
faraday_conn.run_request(
request.method_symbol,
request.fullpath,
request.body.read,
request.headers.select{|k,v| v.respond_to?(:strip)}
)
Note that, since this is in a Rails controller, request.headers returns an instance of ActionDispatch::Http::Headers which quacks like a Hash but not actually a Hash (you can call to_h on it to convert it to a hash).

Create record example for Quickbooks Online and Intuit Anywhere using Ruby and httparty?

Can someone post an example of creating a record in quickbooks online / intuit anywhere, using ruby and httparty?
I am working on an integration to a ruby on rails app using intuit anywhere, and am running into an issue with my POST request when attempting to create a new record. I have been able to successfully retrieve data (customers) using a POST command that doesn't require XML data in the body of the request, but am running into trouble when trying to create new records that have required fields that need to be passed in XML in the body of the request.
I get the same flavor of error in any entity for which I try to create a record for: an invalid or missing required field. It seems to me that the XML in the body (where the data for the required fields is added) is either being ignored (incorrect formatting?) or is not being attached.
I was hoping the someone else familiar with ruby could post an example of a record creation using httparty. If I could see how to correctly pass the XML using httparty, I can fix my problem myself.
I have been using the customer.com example (https://code.intuit.com/integration/viewvc/viewvc.cgi/IntuitAnywhere-Ruby/customer.com/?root=intuitanywhere&system=exsy1003) mostly as posted, with a few irrelevant modifications needed to get it to work in Rails 3.1. I am using the data pull and handling provided in the example, which looks like a pretty standard API wrapper built using httparty.
I am using a pull similar to the one found in the company_controller customers method. Here are two different ways I have tried submitting the XML:
#########################################
#Example 1 - XML
e = #company.intuit_token.post("https://qbo.intuit.com/qbo1/resource/account/v2/#{#company.realm}",
{ :body =>
"<Account xmlns:ns2=\"http://www.intuit.com/sb/cdm/qbo\" xmlns=\"http://www.intuit.com/sb/cdm/v2\">
<Name>Test Account 2</Name>
<Desc>Test Account</Desc>
<Subtype>Savings</Subtype>
<AcctNum>5001</AcctNum>
<OpeningBalanceDate>2010-05-14</OpeningBalanceDate>
</Account>",
:headers => {
"Content-Type" => "application/xml"
}}
)
#########################################
#Example 2 - hash
e = #company.intuit_token.post("https://qbo.intuit.com/qbo1/resource/account/v2/#{#company.realm}",
{ :body => {
:Account => {
:Name => "Loan Account 2",
:Desc => "Loac Account 2",
:Subtype => "Savings",
:AcctNum => "5001",
:OpeningBalanceDate => "2011-04-22"
}
},
:headers => {
"Content-Type" => "application/xml"
}}
)
I incorrectly assumed the customer.com example provided by intuit was using the httparty gem to make the POST call, so I was using the wrong syntax. They are actually using the OAuth gem's POST call, who's syntax can be found here: http://oauth.rubyforge.org/rdoc/classes/OAuth/AccessToken.html
I also had to modify the headers to get the Intuit Anywhere service to accept the XML body. Here is the code that finally worked for me to create a record in quickbooks online using intuit anywhere:
e = #company.intuit_token.post("https://qbo.intuit.com/qbo1/resource/account/v2/#{#company.realm}", "<Account xmlns:ns2=\"http://www.intuit.com/sb/cdm/qbo\" xmlns=\"http://www.intuit.com/sb/cdm/v2\"><Name>Test Account </Name><Desc>Test Account</Desc><Subtype>Savings</Subtype><AcctNum>5002</AcctNum><OpeningBalanceDate>2010-05-14</OpeningBalanceDate></Account>", {"Content-Type" => "application/xml", "standalone" => "yes", "encoding" => "UTF-8"})

Rails form => URL => JSON => Save params

This is basically what I want to do, with the params given in a form, I want to do a GET/POST request to a site, this site expects an specific URL like http://site.com/user=XXX&size=XXX and it will give me back a JSON, I want to parse/save the data from this JSON into my rails app when the form is submitted.
I am totally lost with this manner, anything would be very appreciated.
Rails Form Data => Build the URL => Do a GET/Post request => Catch JSON => Parse => Save
for rest api you can use activeresource in your application
http://api.rubyonrails.org/classes/ActiveResource/Base.html
if it's something very specific you can use Net::Http to make requests and then parse json to ruby objects by yourself.
Examples of using http://www.rubyinside.com/nethttp-cheat-sheet-2940.html
for decoding json you can use
Json or ActiveSupport::JSON.decode or this https://github.com/flori/json
I guess you want to do a request to another not your site to get the response. So you can install curb gem (the curl wrapper in ruby). Then use it to make the request on another site and parse json with standart RoR tools like Json to hash.
From http://www.rubyinside.com/nethttp-cheat-sheet-2940.html you get you can do the following:
at the top of your file add:
require "net/http"
require "uri"
require 'json'
then in your controller or helper:
#set the uri
uri = URI.parse("http://my.site.com/uri")
#set the post params and get the respons
response = Net::HTTP.post_form(uri, {"first_param" => "my param", "second_param" => "another param"})
#get the json info
data = JSON.parse(response.body)
#set result to an ActiveRecord (maybe there is a better way to do this, I guess it depends on the response you get
#something = Mymodel.new
#something.name = data["name"]
...
#something.save
Hope it helps!

Resources