pingpp 2.0.8 → 2.0.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/data/ca-certificates.crt +5 -5075
- data/lib/pingpp/version.rb +1 -1
- data/lib/pingpp.rb +34 -38
- metadata +2 -3
- data/lib/pingpp/certificate_blacklist.rb +0 -51
data/lib/pingpp/version.rb
CHANGED
data/lib/pingpp.rb
CHANGED
@@ -3,8 +3,9 @@
|
|
3
3
|
require 'cgi'
|
4
4
|
require 'set'
|
5
5
|
require 'openssl'
|
6
|
-
require '
|
6
|
+
require 'rest-client'
|
7
7
|
require 'json'
|
8
|
+
require 'base64'
|
8
9
|
|
9
10
|
# Version
|
10
11
|
require 'pingpp/version'
|
@@ -21,7 +22,6 @@ require 'pingpp/pingpp_object'
|
|
21
22
|
require 'pingpp/api_resource'
|
22
23
|
require 'pingpp/singleton_api_resource'
|
23
24
|
require 'pingpp/list_object'
|
24
|
-
require 'pingpp/certificate_blacklist'
|
25
25
|
require 'pingpp/charge'
|
26
26
|
require 'pingpp/refund'
|
27
27
|
require 'pingpp/red_envelope'
|
@@ -43,16 +43,15 @@ module Pingpp
|
|
43
43
|
DEFAULT_CA_BUNDLE_PATH = File.dirname(__FILE__) + '/data/ca-certificates.crt'
|
44
44
|
@api_base = 'https://api.pingxx.com'
|
45
45
|
|
46
|
-
@api_version = '2015-
|
46
|
+
@api_version = '2015-10-10'
|
47
47
|
|
48
48
|
@ssl_bundle_path = DEFAULT_CA_BUNDLE_PATH
|
49
49
|
@verify_ssl_certs = true
|
50
|
-
@CERTIFICATE_VERIFIED = false
|
51
50
|
|
52
51
|
HEADERS_TO_PARSE = [:pingpp_one_version, :pingpp_sdk_version]
|
53
52
|
|
54
53
|
class << self
|
55
|
-
attr_accessor :api_key, :api_base, :verify_ssl_certs, :api_version, :parsed_headers
|
54
|
+
attr_accessor :api_key, :api_base, :verify_ssl_certs, :api_version, :parsed_headers, :private_key_path
|
56
55
|
end
|
57
56
|
|
58
57
|
def self.api_url(url='')
|
@@ -92,15 +91,19 @@ module Pingpp
|
|
92
91
|
'email support@pingxx.com if you have any questions.)')
|
93
92
|
end
|
94
93
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
94
|
+
if verify_ssl_certs
|
95
|
+
request_opts = {:verify_ssl => OpenSSL::SSL::VERIFY_PEER,
|
96
|
+
:ssl_ca_file => @ssl_bundle_path,
|
97
|
+
:ssl_version => 'TLSv1'}
|
98
|
+
else
|
99
|
+
request_opts = {:verify_ssl => false,
|
100
|
+
:ssl_version => 'TLSv1'}
|
101
|
+
unless @verify_ssl_warned
|
102
|
+
@verify_ssl_warned = true
|
103
|
+
$stderr.puts("WARNING: Running without SSL cert verification. " \
|
104
|
+
"You should never do this in production. " \
|
105
|
+
"Execute 'Pingpp.verify_ssl_certs = true' to enable verification.")
|
106
|
+
end
|
104
107
|
end
|
105
108
|
|
106
109
|
params = Util.objects_to_ids(params)
|
@@ -115,7 +118,7 @@ module Pingpp
|
|
115
118
|
payload = JSON.generate(params)
|
116
119
|
end
|
117
120
|
|
118
|
-
request_opts.update(:headers => request_headers(api_key, method.to_s.downcase.to_sym == :post).update(headers),
|
121
|
+
request_opts.update(:headers => request_headers(api_key, method.to_s.downcase.to_sym == :post, payload).update(headers),
|
119
122
|
:method => method, :open_timeout => 30,
|
120
123
|
:payload => payload, :url => url, :timeout => 80)
|
121
124
|
|
@@ -146,23 +149,6 @@ module Pingpp
|
|
146
149
|
|
147
150
|
private
|
148
151
|
|
149
|
-
def self.ssl_preflight_passed?
|
150
|
-
if !verify_ssl_certs && !@no_verify
|
151
|
-
$stderr.puts "WARNING: Running without SSL cert verification. " +
|
152
|
-
"Execute 'Pingpp.verify_ssl_certs = true' to enable verification."
|
153
|
-
|
154
|
-
@no_verify = true
|
155
|
-
|
156
|
-
elsif !Util.file_readable(@ssl_bundle_path) && !@no_bundle
|
157
|
-
$stderr.puts "WARNING: Running without SSL cert verification " +
|
158
|
-
"because #{@ssl_bundle_path} isn't readable"
|
159
|
-
|
160
|
-
@no_bundle = true
|
161
|
-
end
|
162
|
-
|
163
|
-
!(@no_verify || @no_bundle)
|
164
|
-
end
|
165
|
-
|
166
152
|
def self.user_agent
|
167
153
|
@uname ||= get_uname
|
168
154
|
lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})"
|
@@ -189,7 +175,7 @@ module Pingpp
|
|
189
175
|
map { |k,v| "#{k}=#{Util.url_encode(v)}" }.join('&')
|
190
176
|
end
|
191
177
|
|
192
|
-
def self.request_headers(api_key, is_post=false)
|
178
|
+
def self.request_headers(api_key, is_post=false, data=nil)
|
193
179
|
headers = {
|
194
180
|
:user_agent => "Pingpp/v1 RubyBindings/#{Pingpp::VERSION}",
|
195
181
|
:authorization => "Bearer #{api_key}",
|
@@ -205,6 +191,13 @@ module Pingpp
|
|
205
191
|
headers.update(:x_pingpp_client_raw_user_agent => user_agent.inspect,
|
206
192
|
:error => "#{e} (#{e.class})")
|
207
193
|
end
|
194
|
+
|
195
|
+
if is_post && private_key_path && data
|
196
|
+
signature = sign_request(data, private_key_path)
|
197
|
+
headers.update(:pingplusplus_signature => signature)
|
198
|
+
end
|
199
|
+
|
200
|
+
headers
|
208
201
|
end
|
209
202
|
|
210
203
|
def self.execute_request(opts)
|
@@ -223,6 +216,11 @@ module Pingpp
|
|
223
216
|
Util.symbolize_names(response)
|
224
217
|
end
|
225
218
|
|
219
|
+
def self.sign_request(data, key_path)
|
220
|
+
pkey = OpenSSL::PKey.read(File.read(key_path))
|
221
|
+
return Base64.strict_encode64(pkey.sign(OpenSSL::Digest::SHA256.new, data))
|
222
|
+
end
|
223
|
+
|
226
224
|
def self.general_api_error(rcode, rbody)
|
227
225
|
APIError.new("Invalid response object from API: #{rbody.inspect} " +
|
228
226
|
"(HTTP response code was #{rcode})", rcode, rbody)
|
@@ -271,7 +269,7 @@ module Pingpp
|
|
271
269
|
def self.handle_restclient_error(e)
|
272
270
|
connection_message = "Please check your internet connection and try again. " \
|
273
271
|
"If this problem persists, you should check Pingpp's service status at " \
|
274
|
-
"https://
|
272
|
+
"https://www.pingxx.com/status"
|
275
273
|
|
276
274
|
case e
|
277
275
|
when RestClient::RequestTimeout
|
@@ -284,8 +282,7 @@ module Pingpp
|
|
284
282
|
when RestClient::SSLCertificateNotVerified
|
285
283
|
message = "Could not verify Pingpp's SSL certificate. " \
|
286
284
|
"Please make sure that your network is not intercepting certificates. " \
|
287
|
-
"(Try going to (#{@api_base}) in your browser.)
|
288
|
-
"If this problem persists, let us know at support@pingxx.com."
|
285
|
+
"(Try going to (#{@api_base}) in your browser.)"
|
289
286
|
|
290
287
|
when SocketError
|
291
288
|
message = "Unexpected error communicating when trying to connect to Pingpp. " \
|
@@ -293,8 +290,7 @@ module Pingpp
|
|
293
290
|
"To check, try running 'host pingxx.com' from the command line."
|
294
291
|
|
295
292
|
else
|
296
|
-
message = "Unexpected error communicating with Pingpp.
|
297
|
-
"If this problem persists, let us know at support@pingxx.com."
|
293
|
+
message = "Unexpected error communicating with Pingpp."
|
298
294
|
|
299
295
|
end
|
300
296
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pingpp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Xufeng Weng
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-03-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rest-client
|
@@ -141,7 +141,6 @@ files:
|
|
141
141
|
- lib/pingpp/api_operations/list.rb
|
142
142
|
- lib/pingpp/api_operations/update.rb
|
143
143
|
- lib/pingpp/api_resource.rb
|
144
|
-
- lib/pingpp/certificate_blacklist.rb
|
145
144
|
- lib/pingpp/charge.rb
|
146
145
|
- lib/pingpp/errors/api_connection_error.rb
|
147
146
|
- lib/pingpp/errors/api_error.rb
|
@@ -1,51 +0,0 @@
|
|
1
|
-
require 'uri'
|
2
|
-
require 'digest/sha1'
|
3
|
-
|
4
|
-
module Pingpp
|
5
|
-
module CertificateBlacklist
|
6
|
-
|
7
|
-
BLACKLIST = {
|
8
|
-
"api.pingxx.com" => [
|
9
|
-
]
|
10
|
-
}
|
11
|
-
|
12
|
-
# Preflight the SSL certificate presented by the backend. This isn't 100%
|
13
|
-
# bulletproof, in that we're not actually validating the transport used to
|
14
|
-
# communicate with Pingpp, merely that the first attempt to does not use a
|
15
|
-
# revoked certificate.
|
16
|
-
|
17
|
-
# Unfortunately the interface to OpenSSL doesn't make it easy to check the
|
18
|
-
# certificate before sending potentially sensitive data on the wire. This
|
19
|
-
# approach raises the bar for an attacker significantly.
|
20
|
-
|
21
|
-
def self.check_ssl_cert(uri, ca_file)
|
22
|
-
uri = URI.parse(uri)
|
23
|
-
|
24
|
-
sock = TCPSocket.new(uri.host, uri.port)
|
25
|
-
ctx = OpenSSL::SSL::SSLContext.new
|
26
|
-
ctx.set_params(:verify_mode => OpenSSL::SSL::VERIFY_PEER,
|
27
|
-
:ca_file => ca_file)
|
28
|
-
|
29
|
-
socket = OpenSSL::SSL::SSLSocket.new(sock, ctx)
|
30
|
-
socket.connect
|
31
|
-
|
32
|
-
certificate = socket.peer_cert.to_der
|
33
|
-
fingerprint = Digest::SHA1.hexdigest(certificate)
|
34
|
-
|
35
|
-
if blacklisted_certs = BLACKLIST[uri.host]
|
36
|
-
if blacklisted_certs.include?(fingerprint)
|
37
|
-
raise APIConnectionError.new(
|
38
|
-
"Invalid server certificate. You tried to connect to a server that" +
|
39
|
-
"has a revoked SSL certificate, which means we cannot securely send" +
|
40
|
-
"data to that server. Please email support@pingxx.com if you need" +
|
41
|
-
"help connecting to the correct API server."
|
42
|
-
)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
socket.close
|
47
|
-
|
48
|
-
return true
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|