class BlockChyp::BlockChypClient

base class for the blockchyp generated blockchyp client

Attributes

api_key[R]
bearer_token[R]
gateway_host[RW]
gateway_timeout[RW]
https[RW]
offline_cache_enabled[RW]
offline_fixed_key[R]
route_cache[RW]
route_cache_location[RW]
route_cache_ttl[RW]
signing_key[R]
terminal_connect_timeout[RW]
terminal_timeout[RW]
test_gateway_host[RW]

Public Class Methods

new(api_key, bearer_token, signing_key) click to toggle source
# File lib/blockchyp_client.rb, line 18
def initialize(api_key, bearer_token, signing_key)
  @api_key = api_key
  @bearer_token = bearer_token
  @signing_key = signing_key
  @gateway_host = 'https://api.blockchyp.com'
  @test_gateway_host = 'https://test.blockchyp.com'
  @https = false
  @route_cache_location = File.join(Dir.tmpdir, '.blockchyp_route')
  @route_cache_ttl = 60
  @gateway_timeout = 20
  @terminal_timeout = 120
  @terminal_connect_timeout = 5
  @offline_cache_enabled = true
  @route_cache = {}
  @offline_fixed_key = 'cb22789c9d5c344a10e0474f134db39e25eb3bbf5a1b1a5e89b507f15ea9519c'
end

Public Instance Methods

compute_hmac(tsp, nonce) click to toggle source
# File lib/blockchyp_client.rb, line 63
def compute_hmac(tsp, nonce)
  canonical_string = api_key + bearer_token + tsp + nonce

  OpenSSL::HMAC.hexdigest('SHA256', CryptoUtils.hex2bin(signing_key), canonical_string)
end
decrypt(cipher_text) click to toggle source
# File lib/blockchyp_client.rb, line 244
def decrypt(cipher_text)
  return cipher_text if cipher_text.nil? || cipher_text.empty?

  tokens = cipher_text.split(':')

  return cipher_text if tokens[0].nil? || tokens[1].nil?

  iv = Base64.decode64(tokens[0])
  cp = Base64.decode64(tokens[1])

  decipher = OpenSSL::Cipher::AES256.new(:CBC)
  decipher.decrypt
  decipher.key = derive_offline_key
  decipher.iv = iv

  decipher.update(cp) + decipher.final
end
derive_offline_key() click to toggle source
# File lib/blockchyp_client.rb, line 262
def derive_offline_key
  Digest::SHA256.digest offline_fixed_key + signing_key
end
encrypt(plain_text) click to toggle source
# File lib/blockchyp_client.rb, line 233
def encrypt(plain_text)
  return plain_text if plain_text.nil? || plain_text.empty?

  cipher = OpenSSL::Cipher::AES256.new(:CBC)
  cipher.encrypt
  cipher.key = derive_offline_key
  iv = cipher.random_iv

  Base64.encode64(iv) + ':' + Base64.encode64(cipher.update(plain_text) + cipher.final)
end
evict(terminal_name) click to toggle source
# File lib/blockchyp_client.rb, line 274
def evict(terminal_name)
  route_cache.delete(api_key + terminal_name)

  offline_cache = read_offline_cache
  offline_cache.delete(api_key + terminal_name)
  File.write(route_cache_location, route_cache.to_json)
end
gateway_request(method, path, request = nil, relay = false) click to toggle source
# File lib/blockchyp_client.rb, line 166
def gateway_request(method, path, request = nil, relay = false)
  uri = resolve_gateway_uri(path, request)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = uri.instance_of?(URI::HTTPS)
  timeout = get_timeout(request, relay ? terminal_timeout : gateway_timeout)
  http.open_timeout = timeout
  http.read_timeout = timeout

  req = get_http_request(method, uri)

  req['User-Agent'] = user_agent
  unless request.nil?
    json = request.to_json
    req['Content-Type'] = 'application/json'
    req['Content-Length'] = json.length
    req.body = json
  end

  headers = generate_gateway_headers
  headers.each do |key, value|
    req[key] = value
  end

  response = http.request(req)

  if response.is_a?(Net::HTTPSuccess)
    JSON.parse(response.body, symbolize_names: true)
  else
    raise response.message
  end
end
generate_error_response(msg) click to toggle source
# File lib/blockchyp_client.rb, line 75
def generate_error_response(msg)
  {
    success: false,
    error: msg,
    responseDescription: msg
  }
end
generate_gateway_headers() click to toggle source
# File lib/blockchyp_client.rb, line 50
def generate_gateway_headers
  nonce = CryptoUtils.generate_nonce
  tsp = CryptoUtils.timestamp

  sig = compute_hmac(tsp, nonce)

  {
    'Nonce' => nonce,
    'Timestamp' => tsp,
    'Authorization' => 'Dual ' + bearer_token + ':' + api_key + ':' + sig
  }
end
get_http_request(method, uri) click to toggle source
# File lib/blockchyp_client.rb, line 140
def get_http_request(method, uri)
  case method
  when 'GET'
    Net::HTTP::Get.new(uri.request_uri)
  when 'PUT'
    Net::HTTP::Put.new(uri.request_uri)
  when 'POST'
    Net::HTTP::Post.new(uri.request_uri)
  end
end
get_timeout(request, default) click to toggle source
# File lib/blockchyp_client.rb, line 328
def get_timeout(request, default)
  if request.nil? || !request.key?(:timeout) || request[:timeout].zero?
    return default
  end

  request[:timeout]
end
read_offline_cache() click to toggle source
# File lib/blockchyp_client.rb, line 309
def read_offline_cache
  if File.file?(route_cache_location)

    config_file = File.open(route_cache_location)
    content = config_file.read

    return JSON.parse(content, symbolize_names: true)
  end
  {}
end
request_route_from_gateway(terminal_name) click to toggle source
# File lib/blockchyp_client.rb, line 266
def request_route_from_gateway(terminal_name)
  route = gateway_request('GET', '/api/terminal-route?terminal=' + CGI.escape(terminal_name))
  if !route.nil? && !route[:ipAddress].empty?
    route[:exists] = true
  end
  route
end
resolve_gateway_uri(path, request) click to toggle source
# File lib/blockchyp_client.rb, line 69
def resolve_gateway_uri(path, request)
  url = request.nil? || !request[:test] ? gateway_host : test_gateway_host

  URI.parse(path.nil? ? url : url + path)
end
resolve_terminal_route(terminal_name) click to toggle source
# File lib/blockchyp_client.rb, line 198
def resolve_terminal_route(terminal_name)
  route = route_cache_get(terminal_name, false)

  if route.nil?
    route = request_route_from_gateway(terminal_name)
    if route.nil?
      return route
    end

    ttl = Time.now.utc + (route_cache_ttl * 60)
    route_cache_entry = {}
    route_cache_entry[:route] = route
    route_cache_entry[:ttl] = ttl
    @route_cache[api_key + terminal_name] = route_cache_entry
    update_offline_cache(route_cache_entry)
  end
  route
end
resolve_terminal_uri(route, path) click to toggle source
# File lib/blockchyp_client.rb, line 151
def resolve_terminal_uri(route, path)
  url = if https
          'https://'
        else
          'http://'
        end
  url += route[:ipAddress]
  port = if https
           ':8443'
         else
           ':8080'
         end
  URI.parse(url + port + path)
end
route_cache_get(terminal_name, stale) click to toggle source
# File lib/blockchyp_client.rb, line 282
def route_cache_get(terminal_name, stale)
  route_cache_entry = @route_cache[api_key + terminal_name]

  if route_cache_entry.nil? && offline_cache_enabled
    offline_cache = read_offline_cache
    route_cache_entry = offline_cache[@api_key + terminal_name]
  end

  if route_cache_entry
    route = route_cache_entry[:route]
    tx_creds = route[:transientCredentials]
    tx_creds[:apiKey] = decrypt(tx_creds[:apiKey])
    tx_creds[:bearerToken] = decrypt(tx_creds[:bearerToken])
    tx_creds[:signingKey] = decrypt(tx_creds[:signingKey])
    route[:transientCredentials] = tx_creds
    route_cache_entry[:route] = route

    raw_ttl = route_cache_entry[:ttl]

    ttl = raw_ttl.instance_of?(Time) ? raw_ttl : Time.parse(route_cache_entry[:ttl])

    if stale || Time.new < ttl
      route_cache_entry[:route]
    end
  end
end
route_terminal_request(method, terminal_path, gateway_path, request) click to toggle source
# File lib/blockchyp_client.rb, line 83
def route_terminal_request(method, terminal_path, gateway_path, request)
  unless request.key?(:terminalName)
    return gateway_request(method, gateway_path, request)
  end

  route = resolve_terminal_route(request[:terminalName])
  if !route
    return generate_error_response('Unkown Terminal')
  elsif route[:cloudRelayEnabled]
    return gateway_request(method, gateway_path, request, relay: true)
  end

  terminal_request(method, route, terminal_path, request, true)
end
terminal_request(method, route, path, request, open_retry) click to toggle source
# File lib/blockchyp_client.rb, line 98
def terminal_request(method, route, path, request, open_retry)
  uri = resolve_terminal_uri(route, path)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = uri.instance_of?(URI::HTTPS)
  timeout = get_timeout(request, terminal_timeout)
  http.open_timeout = timeout
  http.read_timeout = timeout

  tx_creds = route[:transientCredentials]

  wrapped_request = {
    apiKey: tx_creds[:apiKey],
    bearerToken: tx_creds[:bearerToken],
    signingKey: tx_creds[:signingKey],
    request: request
  }

  req = get_http_request(method, uri)

  req['User-Agent'] = user_agent
  json = wrapped_request.to_json
  req['Content-Type'] = 'application/json'
  req['Content-Length'] = json.length
  req.body = json

  begin
    response = http.request(req)
  rescue Net::OpenTimeout, Errno::EHOSTDOWN, Errno::EHOSTUNREACH, Errno::ETIMEDOUT, Errno::ENETUNREACH
    if open_retry
      evict(route[:terminalName])
      route = resolve_terminal_route(route[:terminalName])
      return terminal_request(method, route, path, request, false)
    end
    raise
  end
  if response.is_a?(Net::HTTPSuccess)
    JSON.parse(response.body, symbolize_names: true)
  else
    raise response.message
  end
end
update_offline_cache(route_cache_entry) click to toggle source
# File lib/blockchyp_client.rb, line 217
def update_offline_cache(route_cache_entry)
  if offline_cache_enabled
    offline_cache = read_offline_cache
    offline_entry = route_cache_entry.clone
    route = route_cache_entry[:route].clone
    tx_creds = route[:transientCredentials].clone
    tx_creds[:apiKey] = encrypt(tx_creds[:apiKey])
    tx_creds[:bearerToken] = encrypt(tx_creds[:bearerToken])
    tx_creds[:signingKey] = encrypt(tx_creds[:signingKey])
    route[:transientCredentials] = tx_creds
    offline_entry[:route] = route
    offline_cache[api_key + route[:terminalName]] = offline_entry
    File.write(route_cache_location, offline_cache.to_json)
  end
end
user_agent() click to toggle source
# File lib/blockchyp_client.rb, line 320
def user_agent
  if defined? VERSION
    "BlockChyp-Ruby/#{VERSION}"
  else
    'BlockChyp-Ruby'
  end
end