Project

General

Profile

Download (5.49 KB) Statistics
| Branch: | Tag: | Revision:
require 'oauth'
require 'cgi'

module Katello
class HttpResource
class NetworkException < StandardError
end

class RestClientException < StandardError
attr_reader :service_code, :code
def initialize(params)
super params[:message]
@service_code = params[:service_code]
@code = params[:code]
end
end

include Katello::Concerns::FilterSensitiveData

class_attribute :consumer_secret, :consumer_key, :ca_cert_file, :prefix, :site, :default_headers,
:ssl_client_cert, :ssl_client_key

attr_reader :json

def initialize(json = {})
@json = json
end

def [](key)
@json[key]
end

def []=(key, value)
@json[key] = value
end

REQUEST_MAP = {
get: Net::HTTP::Get,
post: Net::HTTP::Post,
put: Net::HTTP::Put,
patch: Net::HTTP::Patch,
delete: Net::HTTP::Delete
}.freeze

class << self
REQUEST_MAP.keys.each do |key|
define_method(key) do |*args|
issue_request(
method: key,
path: args.first,
headers: args.length > 1 ? args.last : nil,
payload: args.length > 2 ? args[1] : nil # non-GET method signatures use payload as the second argument, keeping headers as the last element
)
end
end

def logger
fail NotImplementedError
end

def process_response(resp)
logger.debug "Processing response: #{resp.code}"
logger.debug filter_sensitive_data(resp.body)
return resp unless resp.code.to_i >= 400
parsed = {}
message = "Rest exception while processing the call"
service_code = ""
status_code = resp.code.to_s
begin
parsed = JSON.parse resp.body
message = parsed["displayMessage"] if parsed["displayMessage"]
service_code = parsed["code"] if parsed["code"]
rescue => error
logger.error "Error parsing the body: " << error.backtrace.join("\n")
if %w(404 500 502 503 504).member? resp.code.to_s
logger.error "Remote server status code " << resp.code.to_s
raise RestClientException, {:message => error.to_s, :service_code => service_code, :code => status_code}, caller
else
raise NetworkException, [resp.code.to_s, resp.body].reject { |s| s.blank? }.join(' ')
end
end
fail RestClientException, {:message => message, :service_code => service_code, :code => status_code}, caller
end

def issue_request(method:, path:, headers: {}, payload: nil)
logger.debug("Resource #{method.upcase} request: #{path}")
logger.debug "Headers: #{headers.to_json}"
begin
logger.debug "Body: #{filter_sensitive_data(payload.to_json)}"
rescue JSON::GeneratorError, Encoding::UndefinedConversionError
logger.debug "Body: Error: could not render payload as json"
end

client = rest_client(REQUEST_MAP[method], method, path)
args = [method, payload, headers].compact

process_response(client.send(*args))
rescue RestClient::Exception => e
raise_rest_client_exception e, path, method.upcase
rescue Errno::ECONNREFUSED
service = path.split("/").second
raise Errors::ConnectionRefusedException, _("A backend service [ %s ] is unreachable") % service.capitalize
end

# re-raise the same exception with nicer error message
def raise_rest_client_exception(e, a_path, http_method)
msg = "#{name}: #{e.message} #{e.http_body} (#{http_method} #{a_path})"
e.message = msg
fail e
end

def join_path(*args)
args.inject("") do |so_far, current|
so_far << '/' if (!so_far.empty? && so_far[so_far.length - 1].chr != '/') || current[0].chr != '/'
so_far << current.strip
end
end

# Creates a RestClient::Resource class with a signed OAuth style
# Authentication header added to the request headers.
def rest_client(http_type, method, path)
# Need full path to properly generate the signature
url = self.site + path
params = { :site => self.site,
:http_method => method,
:request_token_path => "",
:authorize_path => "",
:access_token_path => ""}

params[:ca_file] = self.ca_cert_file unless self.ca_cert_file.nil?
# New OAuth consumer to setup signing the request
consumer = OAuth::Consumer.new(self.consumer_key,
self.consumer_secret,
params)

# The type is passed in, GET/POST/PUT/DELETE
request = http_type.new(url)

# Sign the request with OAuth
consumer.sign!(request)
# Extract the header and add it to the RestClient
added_header = {'Authorization' => request['Authorization']}

options = {
:headers => added_header,
:open_timeout => SETTINGS[:katello][:rest_client_timeout],
:timeout => SETTINGS[:katello][:rest_client_timeout]
}
options[:ssl_ca_file] = self.ca_cert_file unless self.ca_cert_file.nil?
options[:ssl_client_cert] = self.ssl_client_cert unless self.ssl_client_cert.nil?
options[:ssl_client_key] = self.ssl_client_key unless self.ssl_client_key.nil?

RestClient::Resource.new(url, options)
end

def hash_to_query(query_parameters)
"?#{URI.encode_www_form(query_parameters)}"
end
end
end
end
(3-3/6)