How do I do Rails Basic Authorization with RestClient? - ruby-on-rails

I am trying to post a request to a REST service (HP ALM 11 REST API fwiw) using rest-client and keep getting the Unauthorized response. Could be I am not following the docs right but also I am not sure I am doing the headers properly. So far my googling for RestClient has been fruitless. Any help would be appreciated:
Code:
#alm_url = "http://alm_url/qcbin/"
#user_name = "username"
#user_password = "password"
authentication_url = #alm_url + "rest/is-authenticate"
resource = RestClient::Resource.new authentication_url
resource.head :Authorization => Base64.encode64(#user_name) + ":" + Base64.encode64(#user_password)
response = resource.get
#response = RestClient.get authentication_url, :authorization => #username, #user_password
Rails.logger.debug response.inspect
Based on this SO question I also tried the following without success:
#alm_url = "http://alm_url/qcbin/"
#user_name = "username"
#user_password = "password"
authentication_url = #alm_url + "rest/is-authenticate"
resource = RestClient::Resource.new authentication_url, {:user => #user_name, :password => #user_password}
response = resource.get
#response = RestClient.get authentication_url, :authorization => #username, #user_password
Rails.logger.debug response.inspect
Documentation:
Client sends a valid Basic Authentication header to the authentication
point.
GET /qcbin/authentication-point/authenticate Authorization: Basic
ABCDE123
Server validates the Basic authentication headers, creates a new
LW-SSO token and returns it as LWSSO_COOKIE_KEY.

Okay... so first it helps if I go to the right URL:
authentication_url = #alm_url + "rest/is-authenticate"
Which should read:
authentication_url = #alm_url + "authentication-point/authenticate"
Secondly, it helps if I read the docs for RestClient rather than just look at the readme. The example under Instance Method Details helped a lot.
My code now looks like:
#alm_url = "http://alm_url/qcbin/"
#user_name = "username"
#user_password = "password"
authentication_url = #alm_url + "authentication-point/authenticate"
resource = RestClient::Resource.new(authentication_url, #user_name, #user_password)
response = resource.get
Rails.logger.debug response.inspect
EDIT:
Wow I really over-thought this. I could have gone with:
response = RestClient.get "http://#{#user_name}:#{#user_password}#alm_url/qcbin/authentication-point/authenticate"

Related

FTX.com REST API POST Authentication FAILS with Ruby on Rails and net/https

Hoping for some help as this one has me baffled...
I created a user account and API credentials at FTX.com.
They have an interesting Auth setup which is detailed here: https://docs.ftx.com/?python#authentication
They only provide code examples for python, javascript and c#, but I need to implement the integration on a RoR app.
Here's a link which also provides an example for both GET and POST calls: https://blog.ftx.com/blog/api-authentication/
I'm using:
ruby '3.0.1'
gem 'rails', '~> 6.1.4', '>= 6.1.4.1'
also,
require 'uri'
require 'net/https'
require 'net/http'
require 'json'
I got the authentication working for GET calls as follows:
def get_market
get_market_url = 'https://ftx.com/api/markets/BTC-PERP/orderbook?depth=20'
api_get_call(get_market_url)
end
def api_get_call(url)
ts = (Time.now.to_f * 1000).to_i
signature_payload = "#{ts}GET/api/markets"
key = ENV['FTX_API_SECRET']
data = signature_payload
digest = OpenSSL::Digest.new('sha256')
signature = OpenSSL::HMAC.hexdigest(digest, key, data)
headers = {
'FTX-KEY': ENV['FTX_API_KEY'],
'FTX-SIGN': signature,
'FTX-TS': ts.to_s
}
uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
http.read_timeout = 1200
http.use_ssl = true
rsp = http.get(uri, headers)
JSON.parse(rsp.body)
end
This works great and I get the correct response:
=>
{"success"=>true,
"result"=>
{"bids"=>
[[64326.0, 2.0309],
...
[64303.0, 3.1067]],
"asks"=>
[[64327.0, 4.647],
...
[64352.0, 0.01]]}}
However, I can't seem to authenticate correctly for POST calls (even though as far as I can tell I am following the instructions correctly). I use the following:
def create_subaccount
create_subaccount_url = 'https://ftx.com/api/subaccounts'
call_body =
{
"nickname": "sub2",
}.to_json
api_post_call(create_subaccount_url, call_body)
end
def api_post_call(url, body)
ts = (Time.now.to_f * 1000).to_i
signature_payload = "#{ts}POST/api/subaccounts#{body}"
key = ENV['FTX_API_SECRET']
data = signature_payload
digest = OpenSSL::Digest.new('sha256')
signature = OpenSSL::HMAC.hexdigest(digest, key, data)
headers = {
'FTX-KEY': ENV['FTX_API_KEY'],
'FTX-SIGN': signature,
'FTX-TS': ts.to_s
}
uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
http.read_timeout = 1200
http.use_ssl = true
request = Net::HTTP::Post.new(uri, headers)
request.body = body
response = http.request(request)
JSON.parse(response.body)
end
Also tried passing headers via request[] directly:
def api_post_call(url, body)
ts = (Time.now.to_f * 1000).to_i
signature_payload = "#{ts}POST/api/subaccounts#{body}"
key = ENV['FTX_API_SECRET']
data = signature_payload
digest = OpenSSL::Digest.new('sha256')
signature = OpenSSL::HMAC.hexdigest(digest, key, data)
uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
http.read_timeout = 1200
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['FTX-KEY'] = ENV['FTX_API_KEY']
request['FTX-SIGN'] = signature
request['FTX-TS'] = ts.to_s
request.body = body
response = http.request(request)
JSON.parse(response.body)
end
This is the error response:
=> {"success"=>false, "error"=>"Not logged in: Invalid signature"}
My feeling is the issue is somewhere in adding the body to signature_payload before generating the signature via HMAC here..?:
signature_payload = "#{ts}POST/api/subaccounts#{body}"
Thinking this because, if I leave out #{body} here, like so:
signature_payload = "#{ts}POST/api/subaccounts"
the response is:
=> {"success"=>false, "error"=>"Missing parameter nickname"}
I have tried several iterations of setting up the POST call method using various different net/https examples but have had no luck...
I have also contacted FTX support but have had no response.
Would truly appreciate if anyone has some insight on what I am doing wrong here?
try this headers
headers = {
'FTX-KEY': ENV['FTX_API_KEY'],
'FTX-SIGN': signature,
'FTX-TS': ts.to_s,
'Content-Type' => 'application/json',
'Accepts' => 'application/json',
}
Here's a working example of a class to retrieve FTX subaccounts. Modify for your own purposes. I use HTTParty.
class Balancer
require 'uri'
require "openssl"
include HTTParty
def get_ftx_subaccounts
method = 'GET'
path = '/subaccounts'
url = "#{ENV['FTX_BASE_URL']}#{path}"
return HTTParty.get(url, headers: headers(method, path, ''))
end
def headers(*args)
{
'FTX-KEY' => ENV['FTX_API_KEY'],
'FTX-SIGN' => signature(*args),
'FTX-TS' => ts.to_s,
'Content-Type' => 'application/json',
'Accepts' => 'application/json',
}
end
def signature(*args)
OpenSSL::HMAC.hexdigest(digest, ENV['FTX_API_SECRET'], signature_payload(*args))
end
def signature_payload(method, path, query)
payload = [ts, method.to_s.upcase, "/api", path].compact
if method==:post
payload << query.to_json
elsif method==:get
payload << ("?" + URI.encode_www_form(query))
end unless query.empty?
payload.join.encode("UTF-8")
end
def ts
#ts ||= (Time.now.to_f * 1000).to_i
end
def digest
#digest ||= OpenSSL::Digest.new('sha256')
end
end

Sending HTML email using gmail API in ruby

I am creating a ruby script and it should do the above. Over the day I was trying to crack I way to send an HTML email to a selected number of emails addresses. There is no clear documentation on how I should do, So please I will appreciate you helping.
Here is my code, The script is successfully authorizing a user and picking the code to access his/her gmail account. Now I want to send the HTML email on behalf of that user.
require 'rubygems'
require 'google/api_client'
require 'launchy'
CLIENT_ID = 'my_app_Id_on_gmail_developers_console'
CLIENT_SECRET = 'the_secret_key'
OAUTH_SCOPE = 'https://mail.google.com/'
REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'
# Create a new API client & load the Google Drive API
client = Google::APIClient.new(:application_name => 'Ruby Gmail sample',
:application_version => '1.0.0')
gmail = client.discovered_api('gmail', "v1")
# Request authorization
client.authorization.client_id = CLIENT_ID
client.authorization.client_secret = CLIENT_SECRET
client.authorization.scope = OAUTH_SCOPE
client.authorization.redirect_uri = REDIRECT_URI
uri = client.authorization.authorization_uri
Launchy.open(uri)
# Exchange authorization code for access token
$stdout.write "Enter authorization code: "
client.authorization.code = gets.chomp
client.authorization.fetch_access_token!
#testing if it is working well by counting the emails.
#emails = client.execute(
api_method: gmail.users.messages.list,
parameters: {
userId: "me"},
headers: {'Content-Type' => 'application/json'}
)
count = #emails.data.messages.count
puts "you have #{count} emails "
# Pretty print the API result
jj #emails.data.messages
how can I do this? is there a way I can an external html file which is the email file to be sent. then I can sent this file using the script?
I partially accept the answer above since you can send an email through STMP pretty easily but with the gmail API it's even easier. According your code it should looks like this:
message = Mail.new
message.date = Time.now
message.subject = 'Supertramp'
message.body = "<p>Hi Alex, how's life?</p>"
message.content_type = 'text/html'
message.from = "Michal Macejko <michal#macejko.sk>"
message.to = 'supetramp#alex.com'
service = client.discovered_api('gmail', 'v1')
result = client.execute(
api_method: service.users.messages.to_h['gmail.users.messages.send'],
body_object: {
raw: Base64.urlsafe_encode64(message.to_s)
},
parameters: {
userId: 'michal#macejko.sk'
},
headers: { 'Content-Type' => 'application/json' }
)
response = JSON.parse(result.body)
For multi-part email with the attachment:
message = Mail.new
message.date = Time.now
message.subject = 'Supertramp'
message.from = "Michal Macejko <michal#macejko.sk>"
message.to = 'supetramp#alex.com'
message.part content_type: 'multipart/alternative' do |part|
part.html_part = Mail::Part.new(body: "<p>Hi Alex, how's life?</p>", content_type: 'text/html; charset=UTF-8')
part.text_part = Mail::Part.new(body: "Hi Alex, how's life?")
end
open('http://google.com/image.jpg') do |file|
message.attachments['image.jpg'] = file.read
end
Just my input. I was able to create a script that emailed html to multiple users in about 100 lines. Without using an api. You need to look into using smtp. It is very simple. You define a server for it to use and then you use it's "send_message" method. Here's a link to a good site! GOOD SITE
I can't post my whole code here for security reasons however this should get you started
class Email_Client
attr_accessor :message_contents, :subject
def initialize(sender_name, receiver_name, sender_email, receiver_email)
#sender_name = sender_name
#receiver_name = receiver_name
#sender_email = sender_email
#receiver_email = receiver_email
end
def send_html
message = <<MESSAGE
From: #{#sender_name} <#{#sender_email}>
To: #{#receiver_name} <#{#receiver_email}>
MIME-Version: 1.0
Content-type: text/html
Subject: #{subject}
#{message_contents}
MESSAGE
Net::SMTP.start('SeRvEr_HeRe') do |smtp|
smtp.send_message message,
#sender_email,
#receiver_email
end
end

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

How to optimize this Ruby code that fetches web content?

We have a Rails 3.2.12 app that fetches data from a partner API by making POST requests. However, we're finding that our code seems to be a bottleneck, as it takes longer to process requests than expected. Can we do anything to speed up the code?
Should we use another XML parser? Is there a faster way to post SSL requests in Ruby?
Thanks!
def ced( ad )
# Set vars
u = 'e'
pw = 'p'
ad = ad.join ','
url = 'https://r.e.com/interface.asp?ResponseType=XML&Command=BC' + '&UID=' + u + '&PW=' + pw + '&DL=' + ad
results = []
# Invoke API command
uri = URI.parse url
http = Net::HTTP.new uri.host, uri.port
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
req = Net::HTTP::Get.new uri.request_uri
resp = http.request req
# Parse API response
resp_xml = Nokogiri.XML resp.body
resp_xml.remove_namespaces!
num_errors = resp_xml.at_xpath('//ErrCount').content
# Any errors?
if num_errors != '0'
error = true
# No errors, process domains
else
resp_xml.xpath('//CheckResult').each do |elem|
results << {
:domain => elem.xpath('D').text.downcase,
:status => case elem.xpath('RRPCode').text
when '210' then 'a'
when '211' then 't'
else
error = true
'error'
end
}
end
end
<truncated>
end

Facebook FQL Query with Ruby

I'm trying to do a simple GET with ruby to the Facebook fql.query method without success.
The url is basically structured like this:
https://api.facebook.com/method/fql.query?query=SELECT total_count FROM link_stat WHERE url = "http://twitter.com/"&format=json
I've read in a few posts here on StackOverflow about how to make those requests, but even tho I keep getting:
/usr/lib/ruby/1.8/net/http.rb:560:in `initialize': getaddrinfo: Name or service not known (SocketError)
On the first line of http_get function.
def http_get(domain,path,params)
return Net::HTTP.get(domain, "#{path}?".concat(params.collect { |k,v| "#{k}=#{CGI::escape(v.to_s)}" }.join('&'))) if not params.nil?
return Net::HTTP.get(domain, path)
end
def getFacebookStats(url)
params = {
:query => 'SELECT total_count FROM link_stat WHERE url = "' + url + '"',
:format => 'json'
}
http = http_get('https://api.facebook.com', '/method/fql.query', params)
puts http
end
The http call accepts a host, not a URL:
def http_get(domain,path,params)
path = unless params.blank
path + "?" + params.collect { |k,v| "#{k}=#{CGI::escape(v.to_s)}" }.join('&')
else
path
end
request = Net::HTTP.get(domain, path)
end
def get_facebook_stats(url)
params = {
:query => 'SELECT total_count FROM link_stat WHERE url = "' + url + '"',
:format => 'json'
}
http = http_get('api.facebook.com', '/method/fql.query', params)
puts http
end
Please do not use camel case on method names on Ruby.
If you want to make HTTPS calls, you will have to use a different call:
require 'net/http'
require 'net/https'
http = Net::HTTP.new('somehost.com', 443)
http.use_ssl = true
path = '/login.html'
resp, data = http.get(path, nil)

Resources