Using BOSS api with rails - ruby-on-rails

I am trying to use yahoo's BOSS api with rails.
Controller:
class WelcomeController < ApplicationController
def index
require 'bossman'
BOSSMan.application_id = "api key"
boss = BOSSMan::Search.web("prospect park", :count => 5, :filter => "-hate")
puts "Matches:"
puts
boss.results.each { |result| puts "#{result.title} [#{result.url}]" }
end
end
In gem file I have include
gem 'gemcutter'
gem 'bossman','~> 0.4.1'
gem 'fakeweb'
gem 'spec'
gem 'activesupport'
When I run the application, I get the following error:
No such file or directory - getaddrinfo
Extracted source (around line #6):
BOSSMan.application_id = ""
boss = BOSSMan::Search.images("brooklyn dumbo", :dimensions => "large") #Line 6
boss.results.map { |result| result.url }
end

After long hours of debugging I finally managed to get the boss api to work via rails. It's not really polished, but my guess is everybody should be able to work with it.
This is how:
require 'oauth_util.rb'
require 'net/http'
def get_response(buckets, query_params)
parsed_url = URI.parse("https://yboss.yahooapis.com/ysearch/#{buckets}" )
o = OauthUtil.new
o.consumer_key = YAHOO_KEY
o.consumer_secret = YAHOO_SECRET
o.sign(parsed_url,query_params)
Net::HTTP.start( parsed_url.host, parsed_url.port, :use_ssl => true ) { | http |
req = Net::HTTP::Get.new ("/ysearch/#{buckets}?format=json&q=#{query_params}")
req['Authorization'] = o.header
response = http.request(req)
return response.read_body
}
end
Using my altered Verison of oauth_util.rb
# A utility for signing an url using OAuth in a way that's convenient for debugging
# Note: the standard Ruby OAuth lib is here http://github.com/mojodna/oauth
# License: http://gist.github.com/375593
# Usage: see example.rb below
require 'uri'
require 'cgi'
require 'openssl'
require 'base64'
class OauthUtil
attr_accessor :consumer_key, :consumer_secret, :token, :token_secret, :req_method,
:sig_method, :oauth_version, :callback_url, :params, :req_url, :base_str
def initialize
#consumer_key = ''
#consumer_secret = ''
#token = ''
#token_secret = ''
#req_method = 'GET'
#sig_method = 'HMAC-SHA1'
#oauth_version = '1.0'
#callback_url = ''
#time
end
# openssl::random_bytes returns non-word chars, which need to be removed. using alt method to get length
# ref http://snippets.dzone.com/posts/show/491
def nonce
Array.new( 5 ) { rand(256) }.pack('C*').unpack('H*').first
end
def percent_encode( string )
# ref http://snippets.dzone.com/posts/show/1260
return URI.escape( string, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]") ).gsub('*', '%2A')
end
# #ref http://oauth.net/core/1.0/#rfc.section.9.2
def signature
key = percent_encode( #consumer_secret ) + '&' + percent_encode( #token_secret )
# ref: http://blog.nathanielbibler.com/post/63031273/openssl-hmac-vs-ruby-hmac-benchmarks
digest = OpenSSL::Digest.new( 'sha1' )
hmac = OpenSSL::HMAC.digest( digest, key, #base_str )
# ref http://groups.google.com/group/oauth-ruby/browse_thread/thread/9110ed8c8f3cae81
Base64.encode64( hmac ).chomp.gsub( /\n/, '' )
end
# sort (very important as it affects the signature), concat, and percent encode
# #ref http://oauth.net/core/1.0/#rfc.section.9.1.1
# #ref http://oauth.net/core/1.0/#9.2.1
# #ref http://oauth.net/core/1.0/#rfc.section.A.5.1
def query_string
pairs = []
#params.sort.each { | key, val |
pairs.push( "#{ percent_encode( key ) }=#{ percent_encode( val.to_s ) }" )
}
pairs.join '&'
end
def header
'OAuth oauth_version="1.0",oauth_nonce="'+#nonce_now+'",oauth_timestamp="'+#time+'",oauth_consumer_key="'+#consumer_key+'",oauth_signature_method="'+#sig_method+'",oauth_signature="'+percent_encode(signature)+'"'
end
# organize params & create signature
def sign( parsed_url, query_param )
#time=Time.now.to_i.to_s
#nonce_now=nonce
#params = {
'format' => 'json',
'oauth_consumer_key' => #consumer_key,
'oauth_nonce' => #nonce_now,
'oauth_signature_method' => #sig_method,
'oauth_timestamp' => #time,
'oauth_version' => #oauth_version,
'q'=> query_param
}
# if url has query, merge key/values into params obj overwriting defaults
#if parsed_url.query
# #params.merge! CGI.parse( parsed_url.query )
#end
# #ref http://oauth.net/core/1.0/#rfc.section.9.1.2
#req_url = parsed_url.scheme + '://' + parsed_url.host + parsed_url.path
# create base str. make it an object attr for ez debugging
# ref http://oauth.net/core/1.0/#anchor14
#base_str = [
#req_method,
percent_encode(req_url),
# normalization is just x-www-form-urlencoded
percent_encode(query_string)
].join( '&' )
# add signature
return self
end
end

You can try and include Socket lib at the top of your controller.
require 'socket'
this seems like some socket issue with BOSS.

Related

ruby-jwt encode, decode with RS256 algorithm

It seems like we have always initialize and use the same private key when encoding and decoding a token in RSA256 algorithm:
payload = {:data => 'test'}
rsa_private = OpenSSL::PKey::RSA.generate 2048
rsa_public = rsa_private.public_key
token = JWT.encode payload, rsa_private, 'RS256'
# eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ0ZXN0IjoiZGF0YSJ9.c2FynXNyi6_PeKxrDGxfS3OLwQ8lTDbWBWdq7oMviCy2ZfFpzvW2E_odCWJrbLof-eplHCsKzW7MGAntHMALXgclm_Cs9i2Exi6BZHzpr9suYkrhIjwqV1tCgMBCQpdeMwIq6SyKVjgH3L51ivIt0-GDDPDH1Rcut3jRQzp3Q35bg3tcI2iVg7t3Msvl9QrxXAdYNFiS5KXH22aJZ8X_O2HgqVYBXfSB1ygTYUmKTIIyLbntPQ7R22rFko1knGWOgQCoYXwbtpuKRZVFrxX958L2gUWgb4jEQNf3fhOtkBm1mJpj-7BGst00o8g_3P2zHy-3aKgpPo1XlKQGjRrrxA
puts token
decoded_token = JWT.decode token, rsa_public, true, { :algorithm => 'RS256' }
# Array
# [
# {"data"=>"test"}, # payload
# {"alg"=>"RS256"} # header
# ]
puts decoded_token
But what is the best way t do that in a Rails 5.1 app ?
I figured out how to fix that.
So I have a class JsonWebTokenwith 2 class methods: encodeand decodedefined as follows:
class JsonWebToken
ALGO = 'RS256'
class << self
def encode(payload, exp = 2.hours.from_now)
# set expiry to 2 hours from creation time
payload[:exp] = exp.to_i
JWT.encode(payload, private_key, ALGO)
end
def decode(token)
body = JWT.decode(token, private_key.public_key, true, algorithm: ALGO)[0]
HashWithIndifferentAccess.new body
# rescue from expiry exception
rescue JWT::ExpiredSignature, JWT::VerificationError => e
# raise custom error to be handled by custom handler
raise ExceptionHandler::ExpiredSignature, e.message
end
private
def private_key
#rsa_private ||= OpenSSL::PKey::RSA.generate 2048
end
end
end
I just use another private static method to generate an rsa private key if needed.

How can remove http(s), www(n) and public suffixes in ruby?

Input => Expected Output
https://mail.google.com.au => mail.google
http://www.google.in => google
https://www9.calendar.google.co.uk => calendar.google
https://www12.stage.calendar.google.co.uk => stage.calendar.google
www.blog.botreetechnologies.com => blog.botreetechnologies
Update
t = URI.parse 'http://www.google.com'
t.host
#=> "www.google.com"
URI.split 'http://www.google.com'
#=> ["http", nil, "www.google.com", nil, nil, "", nil, nil, nil]
uri = URI.parse("http://www.google.co.uk")
#=> #<URI::HTTP http://www.google.co.uk>
domain = PublicSuffix.parse(uri.host)
#=> #<PublicSuffix::Domain:0x00000003c538e0 #sld="google", #tld="co.uk", #trd="www">
domain.sld
#=> "google"
uri = URI.parse("http://www.mail.google.co.uk")
#=> #<URI::HTTP http://www.mail.google.co.uk>
domain = PublicSuffix.parse(uri.host)
#=> #<PublicSuffix::Domain:0x00000002e97bc0 #sld="google", #tld="co.uk", #trd="www.mail">
domain.sld
#=> "google"
%w[http://www.example.com/page http://blog.example.com/page].each do |u|
puts URI.parse(u).host.sub(/^www\./, '')
end
# example.com
# blog.example.com
uri = URI.parse("www.pinkpoodles.com.au")
#=> #<URI::Generic www.pinkpoodles.com.au>
uri.host
#=> nil
I can't think of a "one-liner", but something like this would work:
require 'uri'
require 'public_suffix'
def simple_host(uri)
uri = URI(uri)
uri = URI("http://#{uri}") unless uri.scheme
domain = PublicSuffix.parse(uri.host)
trd = domain.trd
if trd
trd = trd.split('.')
trd.shift if trd.first.start_with?('www')
end
[*trd, domain.sld].join('.')
end
simple_host('https://mail.google.com.au') #=> "mail.google"
simple_host('http://www.google.in') #=> "google"
simple_host('https://www9.calendar.google.co.uk') #=> "calendar.google"
simple_host('https://www12.stage.calendar.google.co.uk') #=> "stage.calendar.google"
simple_host('www.blog.botreetechnologies.com') #=> "blog.botreetechnologies"
Not a job for one line. But here's a function that does some string manipulation that at least satisfies the test cases you have provided.
def foo(url)
url = url.split("//").last.split(".")
url = url.each do |word|
if word.include?("http") || word.include?("www") || word.length < 3
url.delete(word)
end
end
if url.length > 1
if url.length >= 3 && url[2].length > 3
url = [url[0], url[1], url[2]].join('.')
else
url = [url[0], url[1]].join(".")
end
else
url = url.first
end
end
foo 'http://www.google.in'
# => 'google'
foo 'https://www9.calendar.google.co.uk'
# => 'calendar.google'
foo 'https://mail.google.com.au'
# => 'mail.google'
foo 'https://www12.stage.calendar.google.co.uk'
# => 'stage.calendar.google'
foo 'www.blog.botreetechnologies.com'
# => 'blog.botreetechnologies'
Here how I fixed that then. On a toe so pasting quickly
def filename(website_domain)
domain = website_domain.starts_with?('http') ? website_domain : "https://#{website_domain}"
uri = URI.parse domain
suffix = PublicSuffix.parse(uri.host)
name = uri.host.sub(/^www\d*\./i, '').sub(/\.#{suffix.tld}/, '')
"#{name}.filtered.xml"
end

how to use the bitbucket API with ruby and access token

I am trying to use the bitbucket API. I have successfully got the flow working where I am able to retrieve the access token and access token secret. After that, I have not been able to get anything to work. I can't find any example on to get this to work with Ruby. The closest I think I've found is this link:
https://gist.github.com/erikeldridge/383159
However in this example, he doesn't add the user's token and token secret, so I've updated it, here's his utility code:
# A utility for signing an url using OAuth in a way that's convenient for debugging
# Note: the standard Ruby OAuth lib is here http://github.com/mojodna/oauth
# License: http://gist.github.com/375593
# Usage: see example.rb below
require 'uri'
require 'cgi'
require 'openssl'
require 'base64'
class OauthUtil
attr_accessor :consumer_key, :consumer_secret, :token, :token_secret, :req_method,
:sig_method, :oauth_version, :callback_url, :params, :req_url, :base_str
def initialize
#consumer_key = ''
#consumer_secret = ''
#token = ''
#token_secret = ''
#req_method = 'GET'
#sig_method = 'HMAC-SHA1'
#oauth_version = '1.0'
#callback_url = ''
end
# openssl::random_bytes returns non-word chars, which need to be removed. using alt method to get length
# ref http://snippets.dzone.com/posts/show/491
def nonce
Array.new( 5 ) { rand(256) }.pack('C*').unpack('H*').first
end
def percent_encode( string )
# ref http://snippets.dzone.com/posts/show/1260
return URI.escape( string, Regexp.new("[^# {URI::PATTERN::UNRESERVED}]") ).gsub('*', '%2A')
end
# #ref http://oauth.net/core/1.0/#rfc.section.9.2
def signature
key = percent_encode( #consumer_secret ) + '&' + percent_encode( #token_secret )
# ref: http://blog.nathanielbibler.com/post/63031273/openssl-hmac-vs-ruby-hmac-benchmarks
digest = OpenSSL::Digest::Digest.new( 'sha1' )
hmac = OpenSSL::HMAC.digest( digest, key, #base_str )
# ref http://groups.google.com/group/oauth-ruby/browse_thread/thread/9110ed8c8f3cae81
Base64.encode64( hmac ).chomp.gsub( /\n/, '' )
end
# sort (very important as it affects the signature), concat, and percent encode
# #ref http://oauth.net/core/1.0/#rfc.section.9.1.1
# #ref http://oauth.net/core/1.0/#9.2.1
# #ref http://oauth.net/core/1.0/#rfc.section.A.5.1
def query_string
pairs = []
#params.sort.each { | key, val |
pairs.push( "#{ percent_encode( key ) }=#{ percent_encode( val.to_s ) }" )
}
pairs.join '&'
end
# organize params & create signature
def sign( parsed_url )
#params = {
'oauth_consumer_key' => #consumer_key,
'oauth_nonce' => nonce,
'oauth_signature_method' => #sig_method,
'oauth_timestamp' => Time.now.to_i.to_s,
'oauth_version' => #oauth_version
}
# if url has query, merge key/values into params obj overwriting defaults
if parsed_url.query
#params.merge! CGI.parse( parsed_url.query )
end
# #ref http://oauth.net/core/1.0/#rfc.section.9.1.2
#req_url = parsed_url.scheme + '://' + parsed_url.host + parsed_url.path
# create base str. make it an object attr for ez debugging
# ref http://oauth.net/core/1.0/#anchor14
#base_str = [
#req_method,
percent_encode( req_url ),
# normalization is just x-www-form-urlencoded
percent_encode( query_string )
].join( '&' )
# add signature
#params[ 'oauth_signature' ] = signature
return self
end
end
and here's my modified code:
require 'oauth_util.rb'
require 'net/http'
o = OauthUtil.new
o.consumer_key = MY_CONSUMER_KEY
o.consumer_secret = MY_SECRET
o.token = ACCESS_TOKEN_RETURNED_FROM_BB
o.token_secret = ACCESS_TOKEN_SECRET_RETURNED_FROM_BB
url = 'https://bitbucket.org/api/1.0/user'
parsed_url = URI.parse( url )
Net::HTTP.start( parsed_url.host ) { | http |
req = Net::HTTP::Get.new "#{ parsed_url.path }?#{ o.sign(parsed_url).query_string }"
response = http.request(req)
print response.read_body
}
Sadly, all I get is
301 Moved Permanently
301 Moved Permanently
nginx/1.5.10
Anyone have any luck using the BB API in Ruby after getting an access token back? Thanks for any help,
Kevin
EDIT:
There is a bitbucket API wrapper gem that has authentication built-in.
ORIGINAL:
I'm wondering if the "301 moved permanently" error is caused by the code making an http request instead of https.
without ssl:
result = Net::HTTP.get(URI.parse('http://bitbucket.org/api/1.0/user'))
# this returns 301 Moved Permanently
But when I used ssl instead (without the oauth headers/params) I got 401 Unauthorized.
with ssl:
1.9.3-p194 :063 > uri = URI.parse('https://bitbucket.org/api/1.0/user')
=> #<URI::HTTPS:0x007f846c5822d8 URL:https://bitbucket.org/api/1.0/user>
1.9.3-p194 :064 > http = Net::HTTP.new(uri.host, uri.port)
=> #<Net::HTTP bitbucket.org:443 open=false>
1.9.3-p194 :065 > http.use_ssl = true
=> true
1.9.3-p194 :066 > request = Net::HTTP::Get.new(uri.request_uri)
=> #<Net::HTTP::Get GET>
1.9.3-p194 :067 > response = http.request(request)
=> #<Net::HTTPUnauthorized 401 UNAUTHORIZED readbody=true>

Twitter auth with devise and twitter api

I manually requesting a request token from twitter and pass the callback to the default page for a device, but get an error
Started GET "/users/auth/twitter/callback?device=mobile&oauth_token=mVpOFb1ruczKw7LzbgQYX73nq81hiw5OEBSOpob5rJk&oauth_verifier=WzBwpFdf7rYDH4DDWNbIfYPkHrIUzam9Ld6vskQrzNA" for 127.0.0.1 at 2014-02-03 18:00:03 +0400
omniauth: (twitter) Authentication failure! invalid_credentials: OAuth :: Unauthorized, 401 Unauthorized
If I log in through Devise, all without errors. Instructions took here. Why is this happening?
class Api::TwitterController < ApplicationController
def get_auth_token
consumer_key = OAUTH_KEYS[Rails.env]['twitter']['client_id'] # Obtainable from your destination site's API admin panel
consumer_secret = OAUTH_KEYS[Rails.env]['twitter']['secret_key'] # As above
callback_url = user_omniauth_callback_url(:twitter, device: :mobile)
method = 'POST'
uri = 'https://api.twitter.com/oauth/request_token'
params = set_params(consumer_key)
params['oauth_callback'] = url_encode(callback_url)
params['oauth_signature'] = url_encode(sign(consumer_secret + '&', signature_base_string(method, uri, params)))
token_data = parse_string(request_data(header(params), uri, method))
auth_token, auth_token_secret = [token_data['oauth_token'], token_data['oauth_token_secret']] # save these values, they'll be used again later
redirect_to "https://api.twitter.com/oauth/authorize?oauth_token=#{auth_token}"
end
private
# where parse_string is simply
def parse_string(str)
ret = {}
str.split('&').each do |pair|
key_and_val = pair.split('=')
ret[key_and_val[0]] = key_and_val[1]
end
ret
end
def set_params(consumer_key)
params = {
'oauth_consumer_key' => consumer_key, # Your consumer key
'oauth_nonce' => generate_nonce, # A random string, see below for function
'oauth_signature_method' => 'HMAC-SHA1', # How you'll be signing (see later)
'oauth_timestamp' => Time.now.getutc.to_i.to_s, # Timestamp
'oauth_version' => '1.0' # oAuth version
}
end
def generate_nonce(size=7)
Base64.encode64(OpenSSL::Random.random_bytes(size)).gsub(/\W/, '')
end
def signature_base_string(method, uri, params)
# Join up the parameters into one long URL-safe string of key value pairs
encoded_params = params.sort.collect{ |k, v| url_encode("#{k}=#{v}") }.join('%26')
# Join the above with your method and URL-safe destination URL
method + '&' + url_encode(uri) + '&' + encoded_params
end
# I'm a PHP developer primarily, hence the name of this function!
def url_encode(string)
CGI::escape(string)
end
# where sign is:
def sign(key, base_string)
digest = OpenSSL::Digest::Digest.new('sha1')
hmac = OpenSSL::HMAC.digest(digest, key, base_string)
Base64.encode64(hmac).chomp.gsub(/\n/, '')
end
# where header is:
def header(params)
header = "OAuth "
params.each do |k, v|
header += "#{k}=\"#{v}\", "
end
header.slice(0..-3) # chop off last ", "
end
def request_data(header, base_uri, method, post_data=nil)
url = URI.parse(base_uri)
http = Net::HTTP.new(url.host, 443) # set to 80 if not using HTTPS
http.use_ssl = true # ignore if not using HTTPS
if method == 'POST'
# post_data here should be your encoded POST string, NOT an array
resp, data = http.post(url.path, post_data, { 'Authorization' => header })
else
resp, data = http.get(url.to_s, { 'Authorization' => header })
end
resp.body
end
end
Problem solved, it was necessary to add some data in the session
auth_token, auth_token_secret = [token_data['oauth_token'], token_data['oauth_token_secret']]
session['oauth'] ||= {}
session['oauth']['twitter'] ||= {}
session['oauth']['twitter']['request_token'] = auth_token
session['oauth']['twitter']['request_secret'] = auth_token_secret
session['oauth']['twitter']['callback_confirmed'] = true

Ruby, forming API request without implicitly stating each parameter

I'm trying to make a request to a web service (fwix), and in my rails app I've created the following initializer, which works... sorta, I have two problems however:
For some reason the values of the parameters need to have +'s as the spaces, is this a standard thing that I can accomplish with ruby? Additionally is this a standard way to form a url? I thought that spaces were %20.
In my code how can I take any of the options sent in and just use them instead of having to state each one like query_items << "api_key=#{options[:api_key]}" if options[:api_key]
The following is my code, the trouble area I'm having are the lines starting with query_items for each parameter in the last method, any ideas would be awesome!
require 'httparty'
module Fwix
class API
include HTTParty
class JSONParser < HTTParty::Parser
def json
JSON.parse(body)
end
end
parser JSONParser
base_uri "http://geoapi.fwix.com"
def self.query(options = {})
begin
query_url = query_url(options)
puts "querying: #{base_uri}#{query_url}"
response = get( query_url )
rescue
raise "Connection to Fwix API failed" if response.nil?
end
end
def self.query_url(input_options = {})
#defaults ||= {
:api_key => "my_api_key",
}
options = #defaults.merge(input_options)
query_url = "/content.json?"
query_items = []
query_items << "api_key=#{options[:api_key]}" if options[:api_key]
query_items << "province=#{options[:province]}" if options[:province]
query_items << "city=#{options[:city]}" if options[:city]
query_items << "address=#{options[:address]}" if options[:address]
query_url += query_items.join('&')
query_url
end
end
end
For 1)
You API provider is expecting '+' because the API is expecting in a CGI formatted string instead of URL formatted string.
require 'cgi'
my_query = "hel lo"
CGI.escape(my_query)
this should give you
"hel+lo"
as you expect
for Question 2) I would do something like
query_items = options.keys.collect { |key| "#{key.to_s}=#{options[key]}" }
def self.query_url(input_options = {})
options = {
:api_key => "my_api_key",
}.merge(input_options)
query_url = "/content.json?"
query_items = []
options.each { |k, v| query_items << "#{k}=#{v.gsub(/\s/, '+')}" }
query_url += query_items.join('&')
end
I'm a developer at Fwix and wanted to help you with your url escaping issue. However, escaping with %20 works for me:
wget 'http://geoapi.fwix.com/content.xml?api_key=mark&province=ca&city=san%20francisco&query=gavin%20newsom'
I was hoping you could provide me with the specific request you're making that you're unable to escape with %20.

Resources