How to decode ion_binary of Amazon QLDB in ruby - ruby-on-rails

I was playing with QLDB of Amazon with ruby and used the aws-sdk-qldb and aws-sdk-qldbsession gem. I was getting result as IonBinary. But not able to decode and parse it.
Following is the code
cred = Aws::Credentials.new(ENV['AWS_ACCESS_KEY'], ENV['AWS_SECRET_ACCESS_KEY'])
qldb_session_client = Aws::QLDBSession::Client.new(region: 'ap-southeast-1', credentials: cred)
start_session_req = Aws::QLDBSession::Types::StartSessionRequest.new
start_session_req.ledger_name = 'ledger-test'
command_request = Aws::QLDBSession::Types::SendCommandRequest.new
command_request.start_session = start_session_req
resp = qldb_session_client.send_command(command_request)
session_token = resp.start_session.session_token
command_request = Aws::QLDBSession::Types::SendCommandRequest.new
command_request.session_token = session_token
command_request.start_transaction = Aws::QLDBSession::Types::StartTransactionRequest.new
resp = qldb_session_client.send_command(command_request)
transaction_id = resp.start_transaction.transaction_id
command_request = Aws::QLDBSession::Types::SendCommandRequest.new
command_request.session_token = session_token
command_request.execute_statement = Aws::QLDBSession::Types::ExecuteStatementRequest.new
command_request.execute_statement.transaction_id = transaction_id
command_request.execute_statement.statement = 'select * from testing'
resp = qldb_session_client.send_command(command_request)
now if I use the following code
resp.execute_statement.first_page.values[0]
I get
{:ion_binary=>"\xE0\x01\x00\xEA\xEE\x9A\x81\x83\xDE\x96\x87\xBE\x93\x89firstname\x88lastname\xDE\x9D\x8A\x8Dtesting first\x8B\x8Ctesting last", :ion_text=>"[FILTERED]"}
I am not able to decode this ion_binary using binary parser.

Decoding the stream is likely to be done with Aws::EventStream::Decoder#decode. That said, somewhat like below should work.
Aws::EventStream::Decoder.new.decode(
StringIO.new(result[:ion_binary])
)

Related

Why does HERE oauth2 token request api return 401300 with Rails but works fine with Postman?

When calling the HERE authentication service (https://account.api.here.com/oauth2/token) from one of the controllers of the RoR APP (Rails 5.0.6/ruby 2.6.1) I get a 401: "401300 Signature mismatch. Authorization signature or client credential is wrong"
The Key, secret, Authorization header, content type, request body etc ... are the same as the ones used by Postman.
Postman always returns a 200 OK but the rails app systematically returns "401"
Any suggestions on what the problem is?
def fetch_new_token
# URL
api_url = 'https://account.api.here.com/oauth2/token'
# VERSION
api_version='1.0'
# GRANT TYPE
api_grant_type_for_req_body='grant_type=client_credentials'
#KEY
api_access_key_id = CGI.escape(ENV['my_access_key_id'])
#SECRET
api_access_key_secret = CGI.escape(ENV['my_access_key_secret'])
#NONCE
draft_api_nonce= [('a'..'z'), ('A'..'Z')].map(&:to_a).flatten
api_nonce=(0...20).map { draft_api_nonce[rand(draft_api_nonce.length)] }.join
#TMESTAMP
api_timestamp = (Time.now).strftime('%s')
#NORMALIZED URL
api_url_normalized = CGI.escape(api_url)
#SIGNING METHOD
api_signature_method= CGI.escape('HMAC-SHA256')
#OAUTH PARAMETERS BASE STRING
api_parameters_string=('consumer_key='+api_access_key_id+'&nonce='+api_nonce+'&signature_method='+api_signature_method+'&timestamp='+api_timestamp+'&'+'version=1.0')
#ENCODED BASE STRING
api_normalized_string = 'POST&'+api_url_normalized+'&'+api_grant_type_for_req_body+CGI.escape('&'+api_parameters_string)
#SIGNNG KEY
api_signing_key = api_access_key_secret+'&'
#SIGNATURE
digest = OpenSSL::Digest.new('sha256')
api_signature = OpenSSL::HMAC.hexdigest(digest, api_normalized_string, api_signing_key)
# convert the HASHING result to a URL ENCODED base64 string.
api_signature_encoded = (Base64.strict_encode64(api_signature))
# AUTHORIZATION STRING - ESCAPED
api_authorization_string = ('OAuth consumer_key="'+api_access_key_id+'",signature_method="'+api_signature_method+'",timestamp="'+CGI.escape(api_timestamp)+'",nonce="'+CGI.escape(api_nonce)+'",version="'+CGI.escape(api_version)+'",signature="'+CGI.escape(api_signature_encoded)+'"')
# FARADAY OBJECT
connect_token_request = Faraday.new(url: 'https://account.api.here.com') do |faraday|
faraday.response :logger, nil, bodies: true
faraday.request :json
faraday.headers['Accept'] = 'application/json'
faraday.headers['Content-Type'] = 'application/x-www-form-urlencoded'
faraday.headers['Authorization'] = api_authorization_string
faraday.adapter Faraday.default_adapter
end
# FARADAY POST
response_token_request= connect_token_request.post('/oauth2/token', 'grant_type=client_credentials' )
# CHECK THE RESULT
puts response_token_request.body
#json = JSON.parse(response_token_request.body)
req_status = #json['httpStatus']
puts "The status returned in the body is:::: #{req_status}"
puts "===== ///// ======"
puts "===== ///// ======"
req_error_code = #json['errorCode']
puts "The ERROR CODE returned in the body is:::: #{req_error_code}"
end
I don't know RoR but I had the same problem in Javascript and this script solved my problem:
const axios = require('axios')
const cryptoJS = require('crypto-js');
const btoa = require('btoa');
exports.getToken = (app_key, app_secret) => {
let url = "https://account.api.here.com/oauth2/token";
let key = encodeURI(app_key);
let secret = encodeURI(app_secret);
let nonce = btoa(Math.random().toString(36)).substring(2, 13);
let timestamp = Math.floor(Date.now()/1000);
let normalizedUrl = encodeURIComponent(url);
let signing_method = encodeURI("HMAC-SHA256");
let sig_string = "oauth_consumer_key="
.concat(key)
.concat("&oauth_nonce=")
.concat(nonce)
.concat("&oauth_signature_method=")
.concat(signing_method)
.concat("&oauth_timestamp=")
.concat(timestamp)
.concat("&").concat("oauth_version=1.0");
let normalised_string = "POST&".concat(normalizedUrl).concat("&").concat(encodeURIComponent(sig_string));
let signingKey = secret.concat("&");
let digest = cryptoJS.HmacSHA256(normalised_string, signingKey);
let signature = cryptoJS.enc.Base64.stringify(digest);
let auth = 'OAuth oauth_consumer_key="'
.concat(key)
.concat('",oauth_signature_method="')
.concat(signing_method)
.concat('",oauth_signature="')
.concat(encodeURIComponent(signature))
.concat('",oauth_timestamp="')
.concat(timestamp)
.concat('",oauth_nonce="')
.concat(nonce)
.concat('",oauth_version="1.0"')
return axios({
method: 'post',
url: url,
data: JSON.stringify({grantType: "client_credentials"}),
headers: {
'Content-Type': "application/json",
'Authorization': auth
}
});
}

why I use AsyncHTTPClient timeout?

#tornado.web.authenticated
#tornado.web.asynchronous
#tornado.gen.coroutine
def post(self):
try:
files_body = self.request.files['file']
except:
error_msg = u"failed to upload file"
error_msg = self.handle_error(error_msg)
self.finish(dict(is_succ=False, error_msg=error_msg))
return
file_ = files_body[0]
filename = file_['filename']
# asynchronous request, obtain OCR info
files = [('image', filename, file_['body'])]
fields = (('api_key', config.OCR_API_KEY), ('api_secret', config.OCR_API_SECRET))
content_type, body = encode_multipart_formdata(fields, files)
headers = {"Content-Type": content_type, 'content-length': str(len(body))}
request = tornado.httpclient.HTTPRequest(config.OCR_HOST, method="POST", headers=headers, body=body,
validate_cert=False, request_timeout = 30)
try:
response = yield tornado.httpclient.AsyncHTTPClient().fetch(request)
except Exception, e:
logging.error(u'orc timeout {}'.format(e))
error_msg = u"OCR timeout"
error_msg = self.handle_error(error_msg)
self.finish(dict(is_succ=False, error_msg=error_msg))
return
if not response.error and response.body:
data = json.loads(response.body)
self.extra_info(data)
result = dict(is_succ=True, error_msg=u"", data=data)
else:
result = dict(is_succ=False, error_msg=u"request timeout", data={})
self.finish(result)
as the code shown, I want to write an api to handle id-card picture upload, and post a request to third part interface for getting the information of id-card.
This api can run well on my PC,however it timeouts on Testing Server. I cannot figure out where the problem is.

rails saml how to decrypt xml?

I'm doing an SAML AUTHENTIFICATION, and when I received the xml, a part of it is ecrypted. This is the part I need (contains name, email etc.)
For that I have a private key to decrypt it, but I don't have any idea how to do that.
I am here:
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
response.settings = set_settings
doc = Nokogiri::XML(response.response)
document = XMLSecurity::Document.new(doc)
### NOT USED HERE
formated_cert = OneLogin::RubySaml::Utils.format_cert(CONFIG_CERTIFICATE)
cert = OpenSSL::X509::Certificate.new(formated_cert)
formated_private_key = OneLogin::RubySaml::Utils.format_private_key(CONFIG_PRIVATE_KEY)
private_key = OpenSSL::PKey::RSA.new(formated_private_key)
### NOT USED HERE
ret = doument.decrypt!(settings) rescue nil # PROBLEME HERE, DONT WORK
def set_settings
settings = OneLogin::RubySaml::Settings.new
...
settings.security[:digest_method] = XMLSecurity::Document::SHA1
settings.security[:signature_method] = XMLSecurity::Document::SHA1
...
settings.certificate = CONFIG_CERTIFICATE
settings.private_key = CONFIG_PRIVATE_KEY
end
and so, ret is suposed to be a decrypted xml that I can use, but it always stays at nil (rescue nil, to avoid 500)
I use OneLogin::RubySaml and XMLSecurity
but I have no idea what I've done wrong,
anybody ?
finally, I succeeded to fix the problem :
Here the solution:
response.settings = saml_settings
enc_key = REXML::XPath.first(response.document, "//xenc:EncryptedKey//xenc:CipherData/xenc:CipherValue").text
enc_value = REXML::XPath.first(response.document, "//xenc:EncryptedData/xenc:CipherData/xenc:CipherValue").text
private_key = OpenSSL::PKey::RSA.new(CONFIG_PRIVATE_KEY)
data_key = private_key.private_decrypt(Base64.decode64(enc_key), OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
actual_output = decrypt_cipher_data(data_key, enc_value)
clean_output = actual_output.slice(0..(actual_output.index('</Assertion>') + '</Assertion>'.length-1))
so clean_output is the final xml decrypted, ready to use
Because I can't comment on your question #ReggieB, here's where the source of #F4ke's answer I think https://gist.github.com/sheeley/7044243
For the decryption part
def decrypt_cipher_data(key_cipher, cipher_data)
cipher_data_str = Base64.decode64(cipher_data)
mcrypt_iv = cipher_data_str[0..15]
cipher_data_str = cipher_data_str[16..-1]
cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
cipher.decrypt
cipher.padding = 0
cipher.key = key_cipher
cipher.iv = mcrypt_iv
result = cipher.update(cipher_data_str)
result << cipher.final
end
enc_key = REXML::XPath.first(response.document, "//xenc:EncryptedKey//xenc:CipherData/xenc:CipherValue").text
enc_value = REXML::XPath.first(response.document, "//xenc:EncryptedData/xenc:CipherData/xenc:CipherValue").text
private_key = OpenSSL::PKey::RSA.new(CONFIG_PRIVATE_KEY)
data_key = private_key.private_decrypt(Base64.decode64(enc_key), OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
actual_output = decrypt_cipher_data(data_key, enc_value)
clean_output = actual_output.slice(0..(actual_output.index('</Assertion>') + '</Assertion>'.length-1))

Wrong Number of Arguments

I understand what causes the wrong number of arguments error but my code doesn't pass any parameters to initialize any of the classes so I'm not sure at all why my code is giving me this error. I'm also pretty new to Ruby on Rails so that doesn't help things. My code is below:
def create_google_file
#products = Product.find(:all)
file = File.new('dir.xml','w')
doc = REXML::Document.new
root = REXML::Element.new "rss"
root.add_attribute("xmlns:g", "http://base.google.com/ns/1.0")
root.add_attribute("version", "2.0")
channel = REXML::Element.new "channel"
root.add_element channel
title = REXML::Element.new "title"
title.text = "Sample Google Base"
channel.add_element title
link = REXML::Element.new "link"
link.text = "http://base.google.com/base/"
channel.add_element link
description = REXML::Element.new "description"
description.text = "Information about products"
channel.add_element description
#products.each do |y|
item = channel.add_element("item")
id = item.add_element("g:id")
id.text = y.id
title = item.add_element("title")
title.text = y.title
description = item.add_element("description")
description.text = y.description
googlecategory = item.add_element("g:google_product_category")
googlecategory.text = y.googlecategory
producttype = item.add_element("g:product_type")
producttype.text = y.producttype
link = item.add_element("link")
link.text = y.link
imglink = item.add_element("g:image_link")
imglink.text = y.imglink
condition = item.add_element("condition")
condition.text = y.condition
availability = item.add_element("g:availability")
availability.text = y.availability
price = item.add_element("g:price")
price.text = y.price "USD"
gtin = item.add_element("g:gtin")
gtin.text = y.gtin
brand = item.add_element("g:brand")
brand.text = y.brand
mpn = item.add_element("g:mpn")
mpn.text = y.mpn
expirationdate = item.add_element("g:expiration_date")
expirationdate.text = y.salepricedate
end
doc.add_element root
file.puts doc
file.close
end
The error I'm getting is:
ArgumentError in ProductsController#create_google_file
wrong number of arguments (1 for 0)
At the request of the poster, I am putting my comments in to an answer:
Based purely on the consistency of the other lines, but without knowing which line is actually failing, it may be this part: price.text = y.price "USD". Is y.price a method that takes in a parameter? Is it defined as def price(type) or something? If not, if it doesn't take any parameters, then it's because you're not supposed to send any parameters to that method. It looks like it's just a getter.
#FranklinJosephMoormann As I suspected, that's the line. Were you trying to make a string like "4.50 USD"? Then you probably wanted: price.text = "#{y.price} USD". That will take the result of y.price and put it in a string, and allow you to keep typing more in the string. It's called string interpolation.

Upload image to twitter using update_with_media - Lua

I am trying to post an image to twitter using update_with_media.json.
The following is a working code to update tweet with statuses/update.json
local url = "http://api.twitter.com/1/statuses/update.json"
local consumer_key = ""
local consumer_secret = ""
local token = ""
local token_secret = ""
local post_data =
{
oauth_consumer_key = consumer_key,
oauth_nonce = get_nonce(),
oauth_signature_method = "HMAC-SHA1",
oauth_token = token,
oauth_timestamp = get_timestamp(),
oauth_version = '1.0',
oauth_token_secret = token_secret
}
post_data["status"] = "Hello Twitter!"
post_data = oAuthSign(url, "POST", post_data, consumer_secret)
r,c,h = http.request
{
url = url,
method = "POST",
headers =
{
["Content-Type"] = "application/x-www-form-urlencoded",
["Content-Length"] = string.len(rawdata)
},
source = ltn12.source.string(post_data),
sink = ltn12.sink.table(response)
}
Now how do I modify the above code to upload images?
The url would be "http://api.twitter.com/1/statuses/update_with_media.json" and
headers["Content-Type"] would be "multipart/form-data"
But where and how do I specify the image to be uploaded?
Ok I got this working some time back..
This post by velluminteractive provides a good library to deal with twitter.
Admittedly the code is for a game engine called Corona SDK. But it shouldn't be too hard for others to use by removing Corona-specific elements from it.

Resources