Class: Mastercard::Common::Connector

Inherits:
Object
  • Object
show all
Defined in:
lib/mastercard_api/common/connector.rb

Constant Summary collapse

NONCE_LENGTH =
8
VALID_CHARS =
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
OAUTH_START_STRING =
'OAuth '
ERROR_STATUS_BOUNDARY =
300
EMPTY_STRING =
""
POST =
"POST"
GET =
"GET"
DELETE =
"DELETE"
PUT =
"PUT"
UTF_8 =
"UTF-8"
EQUALS =
"="
AMP =
'&'
COLON_2X_BACKSLASH =
"://"
MESSAGE =
"Message"
HTTP_CODE =
"HttpCode"
SSL_CA_CER_PATH_LOCATION =
'data/Certs/EnTrust/cacert.pem'
USER_AGENT =
'MC Open API OAuth Framework v1.0-Ruby'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(consumer_key, private_key) ⇒ Connector

consumer_key = Key provided by MasterCard upon registering

private_key = private key object


45
46
47
48
49
50
51
# File 'lib/mastercard_api/common/connector.rb', line 45

def initialize(consumer_key, private_key)
  @consumer_key = consumer_key
  @private_key = private_key
  @signature_base_string = ''
  @auth_header = ''
  @signed_signature_base_string = ''
end

Instance Attribute Details

#auth_headerObject

Returns the value of attribute auth_header.



17
18
19
# File 'lib/mastercard_api/common/connector.rb', line 17

def auth_header
  @auth_header
end

#signature_base_stringObject

Returns the value of attribute signature_base_string.



16
17
18
# File 'lib/mastercard_api/common/connector.rb', line 16

def signature_base_string
  @signature_base_string
end

#signed_signature_base_stringObject

Returns the value of attribute signed_signature_base_string.



18
19
20
# File 'lib/mastercard_api/common/connector.rb', line 18

def signed_signature_base_string
  @signed_signature_base_string
end

Instance Method Details

#add_headers(url, request, request_method, oauth_params, body = nil) ⇒ Object

Method to build the OAuth headers.



123
124
125
126
127
128
129
130
131
132
133
# File 'lib/mastercard_api/common/connector.rb', line 123

def add_headers(url, request, request_method, oauth_params, body=nil)
  @auth_header = build_auth_header_string(url, request_method, oauth_params)
  request['Authorization'] = auth_header
  request['User-Agent'] = USER_AGENT
  if body != nil
    request['content-type'] = 'application/xml;charset=UTF-8'
    body_length = body.length
    request['content-length'] = body_length.to_s
  end
  request
end

#build_auth_header_string(url, request_method, oauth_params) ⇒ Object

Method to build the auth header for the HTTP request.

oauth_params - full populated oauth parameters


138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/mastercard_api/common/connector.rb', line 138

def build_auth_header_string(url, request_method, oauth_params)
  temp_params = Mastercard::Common::OAuthParameters.new
  oauth_params_key_arr = oauth_params.params.keys.sort
  oauth_params_key_arr.each do |p|
    temp_params.add_parameter(p, oauth_params.send(p)) if p != :realm
  end
  generate_signature_base_string(url, request_method, temp_params) 
  sign(oauth_params)
  header = ''
  oauth_params_key_arr = oauth_params.params.keys.sort
  oauth_params_key_arr.each do |p|
    header << p.to_s << '="' << oauth_params.send(p) << '",' if oauth_params.send(p)
  end
  header = '' << OAUTH_START_STRING << header
  #remove trailing comma
  header = header[0...header.length-1]
end

#check_response(response) ⇒ Object



206
207
208
209
210
# File 'lib/mastercard_api/common/connector.rb', line 206

def check_response(response)
  if response.code.to_i >= ERROR_STATUS_BOUNDARY
    raise 'Response Code: ' << response.code.to_s << "\n" << response.body
  end
end

#connect(url, request_method, oauth_params, body = nil) ⇒ Object

Method to connect via HTTP. This method subsequesntly builds and signs the Oauth

Header


93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/mastercard_api/common/connector.rb', line 93

def connect(url, request_method, oauth_params, body=nil)
  uri = URI.parse(url)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true
  http.ca_path = SSL_CA_CER_PATH_LOCATION
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE #todo VERIFY_PEER

  if request_method.upcase == 'GET'
    request = Net::HTTP::Get.new(uri.request_uri)
  elsif request_method.upcase == 'PUT'
    request = Net::HTTP::Put.new(uri.request_uri)
  elsif request_method.upcase == 'POST'
    request = Net::HTTP::Post.new(uri.request_uri)
  elsif request_method.upcase == 'DELETE'
    request = Net::HTTP::Delete.new(uri.request_uri)
  else
    raise 'Unsupported HTTP Action.'
  end

  if body != nil
    add_headers(url, request, request_method, oauth_params, body)
    request.body = body
  else
    add_headers(url, request, request_method, oauth_params)
  end
  http.request(request)
end

#do_request(url, request_method, body = nil, oauth_params = nil) ⇒ Object

Method to perform a request. Only Connector subclasses should access this method.

url - URL to connect to, includes query string parameters
body - XML body to send to MasterCard for PUT/POST/DELETE
oauth_params - often nil, may be populated for testing


71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/mastercard_api/common/connector.rb', line 71

def do_request(url, request_method, body = nil, oauth_params = nil)
  if @consumer_key == nil
    raise 'Consumer Key may not be nil.'
  end

  if @private_key == nil
    raise 'Private Key may not be nil.'
  end
  if oauth_params == nil
    oauth_params = oauth_parameters_factory
  end
  if body != nil && body.length > 0
    oauth_params = generate_body_hash(body, oauth_params)
  end
  response = connect(url, request_method, oauth_params, body)
  check_response(response)
  response.body
end

#generate_body_hash(body, oauth_params) ⇒ Object

if a body is present for sending to server, needs hashed as part of OAuth spec



213
214
215
216
217
218
219
# File 'lib/mastercard_api/common/connector.rb', line 213

def generate_body_hash(body, oauth_params)
  if body != nil
    oauth_body_hash = Digest::SHA1.base64digest(body)
    oauth_params.add_parameter(:oauth_body_hash, oauth_body_hash)
  end
  oauth_params
end

#generate_nonceObject

unique identifier for given timestamp (in seconds)



222
223
224
225
226
227
228
# File 'lib/mastercard_api/common/connector.rb', line 222

def generate_nonce
  str = ''
  for i in 1..NONCE_LENGTH
    str << VALID_CHARS[SecureRandom.random_number(VALID_CHARS.length)]
  end
  @oauth_nonce = str
end

#generate_signature_base_string(url, request_method, oauth_params) ⇒ Object

Method to generate the signature base string from the url, HTTP request method, and oauth_params

url - URL, including query string parameters, to access
request_method - case-insensitive HTTP request method, e.g. GET, POST, PUT, DELETE
oauth_params - populated oauth parameters object


175
176
177
# File 'lib/mastercard_api/common/connector.rb', line 175

def generate_signature_base_string(url, request_method, oauth_params)
  @signature_base_string = CGI.escape(request_method.upcase) << AMP << CGI.escape(normalize_url(url)) << AMP << CGI.escape(normalize_parameters(url, oauth_params))
end

#generate_timestampObject

number of seconds since epoch



230
231
232
# File 'lib/mastercard_api/common/connector.rb', line 230

def generate_timestamp
  @oauth_timestamp = Time.now.to_i.to_s
end

#normalize_parameters(url, oauth_params) ⇒ Object

The signature base string must be in lexical order prior to signing. This method does that required work.

url - url to access
oauth_params - OAuthParameters object used for building Signature Base String


200
201
202
203
204
# File 'lib/mastercard_api/common/connector.rb', line 200

def normalize_parameters(url, oauth_params)
  oauth_params_hash = oauth_params.params
  CGI.parse(URI.parse(url).query).map{|k,v| oauth_params_hash[k.to_sym] = v[0]} if url.include? "?"
  URI.encode(oauth_params_hash.keys.sort.map{|k| "#{k.to_s}=#{oauth_params_hash[k]}"}.join("&"))
end

#normalize_url(url) ⇒ Object

Method to return “core” URL for signature base string generation. somesite.com:8080?blah becomes somesite.com, for example

url - URL to normalize


182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/mastercard_api/common/connector.rb', line 182

def normalize_url(url)
  tmp = url.clone
  # strip query string section
  idx = tmp.index('?')
  if idx != nil
    tmp = tmp[0..idx-1]
  end
  # strip port
  if tmp.rindex(':') != nil && tmp.rindex(':') > 5 # implies port is given
    tmp = tmp[0..tmp.rindex(':')-1]
  end
  tmp
end

#oauth_parameters_factoryObject

Method to generate base OAuth params



55
56
57
58
59
60
61
62
63
# File 'lib/mastercard_api/common/connector.rb', line 55

def oauth_parameters_factory
  oparams = Mastercard::Common::OAuthParameters.new
  oparams.add_parameter(OAUTH_CONSUMER_KEY, @consumer_key)
  oparams.add_parameter(OAUTH_NONCE, generate_nonce)
  oparams.add_parameter(OAUTH_TIMESTAMP, generate_timestamp)
  oparams.add_parameter(OAUTH_SIGNATURE_METHOD, "RSA-SHA1")
  oparams.add_parameter(OAUTH_VERSION, "1.0")
  oparams
end

#sign(oauth_params) ⇒ Object

Method to sign the signature base string using the private key.



158
159
160
161
162
163
164
165
166
167
168
# File 'lib/mastercard_api/common/connector.rb', line 158

def sign(oauth_params)
  if @signature_base_string == nil || @signature_base_string.length == 0
    raise 'Signature Base String May Not Be Null.'
  end
  digest = OpenSSL::Digest::SHA1.new
  @signed_signature_base_string =  CGI.escape(Base64.encode64(@private_key.sign(digest,@signature_base_string)))
  @signed_signature_base_string.gsub!('+','%20')
  @signed_signature_base_string.gsub!('*','%2A')
  @signed_signature_base_string.gsub!('~','%7E')
  oauth_params.add_parameter("oauth_signature" , @signed_signature_base_string)
end