exact4r 0.5 → 1.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. data/CHANGELOG +69 -0
  2. data/LICENCE +3 -0
  3. data/README +186 -0
  4. data/Rakefile +74 -0
  5. data/VERSION +1 -0
  6. data/certs/e-xact.com.crt +31 -0
  7. data/certs/exact.cer +10 -10
  8. data/certs/valicert_class2_root.crt +18 -0
  9. data/lib/ews/certificate_helper.rb +68 -0
  10. data/lib/ews/transaction/fake_response.rb +137 -0
  11. data/lib/ews/transaction/mapping.rb +87 -65
  12. data/lib/ews/transaction/request.rb +51 -62
  13. data/lib/ews/transaction/response.rb +11 -2
  14. data/lib/ews/transaction/rexml-expansion-fix.rb +42 -0
  15. data/lib/ews/transaction/validator.rb +250 -0
  16. data/lib/ews/transporter.rb +139 -0
  17. data/lib/exact4r.rb +10 -2
  18. data/prod.log +49 -0
  19. data/test/credentials.rb +63 -0
  20. data/test/credentials.yml +53 -0
  21. data/test/exhaustive/batch_query_close_test.rb +205 -0
  22. data/test/exhaustive/forced_post_test.rb +81 -0
  23. data/test/exhaustive/online_debit_purchase_test.rb +70 -0
  24. data/test/exhaustive/online_debit_refund_test.rb +70 -0
  25. data/test/exhaustive/pre_auth_completion_test.rb +99 -0
  26. data/test/exhaustive/pre_auth_only_test.rb +74 -0
  27. data/test/exhaustive/pre_auth_test.rb +68 -0
  28. data/test/exhaustive/purchase_correction_test.rb +85 -0
  29. data/test/exhaustive/purchase_test.rb +68 -0
  30. data/test/exhaustive/recurring_seed_pre_auth_test.rb +68 -0
  31. data/test/exhaustive/recurring_seed_purchase_test.rb +68 -0
  32. data/test/exhaustive/referenced_void_test.rb +131 -0
  33. data/test/exhaustive/refund_correction_test.rb +85 -0
  34. data/test/exhaustive/refund_test.rb +68 -0
  35. data/test/exhaustive/secure_storage_test.rb +75 -0
  36. data/test/exhaustive/tagged_online_debit_refund_test.rb +195 -0
  37. data/test/exhaustive/tagged_pre_auth_completion_test.rb +119 -0
  38. data/test/exhaustive/tagged_pre_auth_test.rb +116 -0
  39. data/test/exhaustive/tagged_purchase_test.rb +116 -0
  40. data/test/exhaustive/tagged_refund_test.rb +142 -0
  41. data/test/exhaustive/tagged_update_test.rb +158 -0
  42. data/test/exhaustive/tagged_void_test.rb +174 -0
  43. data/test/exhaustive/transaction_details_test.rb +145 -0
  44. data/test/exhaustive/void_test.rb +118 -0
  45. data/test/general/avs_test.rb +87 -0
  46. data/test/general/client_certificate_test.rb +64 -0
  47. data/test/general/json_encoding_test.rb +62 -0
  48. data/test/general/request_test.rb +179 -0
  49. data/test/general/rest_encoding_test.rb +174 -0
  50. data/test/general/soap_encoding_test.rb +211 -0
  51. data/test/general/transporter_test.rb +74 -0
  52. data/test/general/validator_test.rb +150 -0
  53. data/test/samples/dsa_cert.pem +22 -0
  54. data/test/samples/dsa_key.pem +12 -0
  55. data/test/samples/rest.dodgy.response.xml +60 -0
  56. data/test/samples/rest.everything.response.xml +67 -0
  57. data/test/samples/rest.response.xml +60 -0
  58. data/test/samples/rsa_cert.pem +63 -0
  59. data/test/samples/rsa_key.pem +15 -0
  60. data/test/samples/soap.deserialization.fault.xml +10 -0
  61. data/test/samples/soap.dodgy.response.xml +95 -0
  62. data/test/samples/soap.everything.response.xml +75 -0
  63. data/test/samples/soap.generalfailure.fault.xml +13 -0
  64. data/test/samples/soap.nulltransaction.fault.xml +14 -0
  65. data/test/samples/soap.response.xml +95 -0
  66. data/test/test_helper.rb +107 -0
  67. metadata +127 -45
  68. data/doc/classes/EWS/Transaction/Request.html +0 -488
  69. data/doc/classes/EWS/Transaction/Response.html +0 -280
  70. data/doc/classes/EWS/Transaction/Transporter.html +0 -251
  71. data/doc/created.rid +0 -1
  72. data/doc/files/README.html +0 -230
  73. data/doc/files/lib/ews/transaction/mapping_rb.html +0 -108
  74. data/doc/files/lib/ews/transaction/request_rb.html +0 -101
  75. data/doc/files/lib/ews/transaction/response_rb.html +0 -101
  76. data/doc/files/lib/ews/transaction/transporter_rb.html +0 -108
  77. data/doc/files/lib/exact4r_rb.html +0 -113
  78. data/doc/fr_class_index.html +0 -29
  79. data/doc/fr_file_index.html +0 -32
  80. data/doc/fr_method_index.html +0 -33
  81. data/doc/index.html +0 -24
  82. data/doc/rdoc-style.css +0 -208
  83. data/lib/ews/transaction/transporter.rb +0 -128
data/CHANGELOG ADDED
@@ -0,0 +1,69 @@
1
+ == v1.7
2
+ Added support for setting a client certificate/key pair for use w/ SSL connections.
3
+
4
+ == v1.6
5
+ Added support for use w/ ActiveSupport v3.
6
+
7
+ == v1.5
8
+ Added support for specifying authorization_num & reference_no in CR requests, to obtain decrypted CC numbers.
9
+
10
+ == v1.4
11
+ Added support for specifying alternative SSL certificates
12
+
13
+ == v1.3
14
+ Added support for TaggedUpdate transactions
15
+ Added support for ReferencedVoid transactions
16
+
17
+ == v1.2
18
+ Updated with new SSL certificates for https://api.e-xact.com
19
+
20
+ == v1.1
21
+ Removed debugger statement. Sorry!
22
+
23
+ == v1.0
24
+ Added support for Batch Query and Batch Close transactions.
25
+
26
+ == v0.9.3
27
+ Reinstated Tagged Void support.
28
+
29
+ == v0.9.2
30
+ Converted tests to Test::Unit
31
+ Added complete test suite, testing all transaction types.
32
+ Enhanced validation of mandatory request information.
33
+
34
+ == v0.9.1
35
+ Added unified Address Verification API
36
+
37
+ == v0.9
38
+ Fixed incorrect decoding of boolean attributes from SOAP response
39
+
40
+ == v0.8
41
+ Added User-Agent string to HTTP requests
42
+
43
+ == v0.7
44
+ Updated with latest certificates for api.e-xact.com
45
+ Fixed SOAP namespace issue
46
+ Ensured expiry_date always submitted at MMYY
47
+
48
+ == v0.6
49
+ Added client-side request validation
50
+
51
+ == v0.5.3
52
+ Added fake response messages for testing purposes
53
+ Moved EWS::Transaction::Transporter to EWS::Transporter
54
+ Fixed typos
55
+
56
+ == v0.5.2
57
+ Updated Transporter to play nicely with mod_security
58
+ Stripped trailing '/'s from URLs
59
+ Transporter ow re-uses connection
60
+ Updated RDoc with test login details
61
+
62
+ == v0.5.1
63
+ Added CHANGELOG, LICENCE & VERSION
64
+ Ensured README etc. were included in gem and displayed as default in RDoc
65
+ Renamed send() to submit() in Transporter
66
+ Fixed path to default E-xact certificates
67
+
68
+ == v0.5
69
+ Initial Release
data/LICENCE ADDED
@@ -0,0 +1,3 @@
1
+ =Licence Terms
2
+
3
+ Distributed under the Ruby software licence[http://www.ruby-lang.org/en/LICENSE.txt]
data/README ADDED
@@ -0,0 +1,186 @@
1
+ = exact4r
2
+ A gem which provides access to E-xact's Web Services API, allowing the submission
3
+ of financial transactions via REST, JSON or SOAP.
4
+
5
+ == Getting Started
6
+
7
+ To submit requests to our transaction processing service, you must first have a Gateway ID,
8
+ and a password. Test logins are as follows:
9
+
10
+ Account 1: :gateway_id => "A00049-01", :password => "test1"
11
+ Account 2: :gateway_id => "A00427-01", :password => "testus"
12
+
13
+
14
+ =1. Submit a transaction
15
+
16
+ require 'rubygems'
17
+ require 'exact4r'
18
+
19
+ # build a purchase request
20
+ request = EWS::Transaction::Request.new({
21
+ :transaction_type => :purchase,
22
+ :amount => 10.50,
23
+ :cardholder_name => "Simon Brown",
24
+ :cc_number => "4111111111111111",
25
+ :cc_expiry => "1012", # MUST be MMYY format
26
+ :gateway_id => "XXXXXXX", # which gateway to submit the request to
27
+ :password => "YYYYYY" # your password for that gateway
28
+ })
29
+
30
+ transporter = EWS::Transporter.new
31
+ response = transporter.submit(request) # submits using REST (XML) by default
32
+
33
+ # submit using JSON, or
34
+ response = transporter.submit(request, :json)
35
+
36
+ # submit using SOAP, or
37
+ response = transporter.submit(request, :soap)
38
+
39
+ # submit explicitly via REST
40
+ response = transporter.submit(request, :rest)
41
+
42
+ # The response object is independent of type of transport chosen.
43
+ # We decode the payload into a regular object
44
+ response.transaction_tag # 1234
45
+ response.exact_resp_code # "00"
46
+ response.exact_message # "Transaction Normal"
47
+ response.bank_resp_code # "00"
48
+ response.bank_message # "APPROVED"
49
+
50
+ =2. Find information on an existing transaction
51
+
52
+ require 'rubygems'
53
+ require 'exact4r'
54
+
55
+ # build a purchase request
56
+ request = EWS::Transaction::Request.new({
57
+ :transaction_type => :purchase,
58
+ :amount => 10.50,
59
+ :cardholder_name => "Simon Brown",
60
+ :cc_number => "4111111111111111",
61
+ :cc_expiry => "1012", # MUST be MMYY format
62
+ :gateway_id => "XXXXXXX", # which gateway to submit the request to
63
+ :password => "YYYYYY" # your password for that gateway
64
+ })
65
+
66
+ transporter = EWS::Transporter.new
67
+ response = transporter.submit(request) # submits using REST (XML) by default
68
+
69
+ # you need to know the transaction tag of the transaction you are looking for
70
+ find_request = EWS::Transaction::Request.new({
71
+ :transaction_type => :transaction_details,
72
+ :transaction_tag => response.transaction_tag,
73
+ :gateway_id => "XXXXXXX",
74
+ :password => "YYYYYY"
75
+ })
76
+
77
+ find_response = transporter.submit(find_request, :json) # again, can choose your transport type as before
78
+ find_response.cc_number # 4111111111111111
79
+ find_response.amount # 10.50
80
+
81
+ =3. Re-using a Transporter
82
+
83
+ require 'rubygems'
84
+ require 'exact4r'
85
+
86
+ # The transporter object can be re-used across multiple transactions, so set it up once
87
+ # and forget about it.
88
+ # In this example, we will continue to use E-xact's default web-service URL, but we
89
+ # will specify a default transport_type of :soap
90
+ transporter = EWS::Transporter.new("https://api.e-xact.com/", {:transaction_type => :soap})
91
+
92
+ # now let's submit do a recurring seed purchase...
93
+ rsp_req = EWS::Transaction::Request.new({
94
+ :transaction_type => :recurring_seed_purchase,
95
+ :amount => 12.00,
96
+ :cardholder_name => "Simon Brown",
97
+ :cc_number => "4111111111111111",
98
+ :cc_expiry => "1012",
99
+ :gateway_id => "XXXXXX",
100
+ :password => "YYYYYY"
101
+ })
102
+
103
+ rsp_resp = transporter.submit(rsp_req)
104
+ raise "Seed Purchase failed" unless rsp_resp.approved?
105
+
106
+ # ...then do multiple refunds against it
107
+ 1.upto(10) do
108
+ rf_req = EWS::Transaction::Request.new({
109
+ :transaction_type => :tagged_refund,
110
+ :transaction_tag => rsp_resp.transaction_tag, # need to know which transaction we're refunding against...
111
+ :authorization_num => rsp_resp.authorization_num, # and its authorization_num
112
+ :amount => 1.00, # refund a dollar at a time
113
+ :gateway_id => "XXXXXX",
114
+ :password => "YYYYYY"
115
+ })
116
+ rf_resp = transporter.submit(rf_req)
117
+ raise "Refund failed: #{rf_resp.exact_resp_code}, #{rf_resp.exact_message}" unless rf_resp.approved?
118
+ end
119
+
120
+ =4. Using the AVS API
121
+
122
+ Allowable AVS params are:
123
+
124
+ - :avs_street_address => "7501ELMST."
125
+ - :avs_unit_no => "801"
126
+ - :avs_po_box => nil # P.O. Box or street address may be specified, but not both
127
+ - :avs_postal_code => "90210"
128
+ - :avs_test_flag => "X" # AVS test flag (optional)
129
+
130
+
131
+ ===Test Flags
132
+ In order to simulate an AVS response in the test environment, set the :+avs_test_flag+ to the AVS result code you would like
133
+ returned. The AVS result is a one-character response that indicates the degree of match for the provided address.
134
+ The following AVS responses are currently supported:
135
+
136
+ ===North American Response Codes
137
+ AVS Code - ABS Definition - Explanation
138
+ - X - Exact match, 9 digit zip - Street Address, and 9 digit ZIP Code match
139
+ - Y - Exact match, 5 digit zip - Street Address, and 5 digit ZIP Code match
140
+ - A - Partial match - Street Address matches, ZIP Code does not
141
+ - W - Partial match - ZIP Code matches, Street Address does not
142
+ - Z - Partial match - 5 digit ZIP Code match only
143
+ - N - No match - No Address or ZIP Code match
144
+ - U - Unavailable - Address information is unavailable for that account number, or the card issuer does not support
145
+ - G - Service Not supported, non-US Issuer does not participate
146
+ - R - Retry - Issuer system unavailable, retry later
147
+ - E - Not a mail or phone order
148
+ - S - Service not supported
149
+
150
+ ===International Response Codes
151
+ AVS Code - ABS Definition and Explanation
152
+ - G - Global non-AVS participant
153
+ - B - Address matches only
154
+ - C - Address and Postal Code do not match
155
+ - D - Address and Postal Code match
156
+ - F - Address and Postal Code match (UK only)
157
+ - I - Address information not verified for international transaction
158
+ - M - Address and Postal Code match
159
+ - P - Postal Code matches only
160
+
161
+ ===Code Sample
162
+ avs_params = {
163
+ :avs_test_flag => 'N',
164
+ :avs_street_address => '123BROWNSTREET',
165
+ :avs_unit_no => '4,
166
+ :avs_po_box => nil,
167
+ :avs_postal_code => '902101234'
168
+ }
169
+
170
+ tx_params = {
171
+ :transaction_type => :purchase,
172
+ :amount => 10.50,
173
+ :cardholder_name => "Simon Brown",
174
+ :cc_number => "4111111111111111",
175
+ :cc_expiry => "1012",
176
+ :gateway_id => "XXXXXXX",
177
+ :password => "YYYYYY"
178
+ }.merge(avs_params)
179
+
180
+ request = EWS::Transaction::Request.new(tx_params)
181
+
182
+ transporter = EWS::Transporter.new("https://api.e-xact.com/")
183
+ response = transporter.submit(request)
184
+
185
+ response.cc_verification_str1.should == "N123BROWNSTREET4902101234"
186
+ response.avs.should == N
data/Rakefile ADDED
@@ -0,0 +1,74 @@
1
+ require 'rake/rdoctask'
2
+ require 'rake/testtask'
3
+ require "rubygems"
4
+ require "rake/gempackagetask"
5
+
6
+ task :default => :'test:chase'
7
+
8
+ namespace :test do
9
+
10
+ desc 'Run all tests against all processors. Use LOCATION to specify web service URL.'
11
+ task :all do
12
+ %w(chase tsys emergis moneris).each do |processor|
13
+ 3.times { puts }
14
+ puts "#-----------------------"
15
+ puts "# Testing: " + processor
16
+ puts "#-----------------------"
17
+
18
+ ENV['PROCESSOR'] = processor
19
+ begin
20
+ Rake::Task[:'test:processor'].execute
21
+ rescue
22
+ # don't care, just run it again. can examine output later
23
+ end
24
+ end
25
+ end
26
+
27
+ desc 'Run all tests against Chase processor and Production web service. Use LOCATION to specify web service URL.'
28
+ task :chase do
29
+ ENV['PROCESSOR'] = 'chase'
30
+ ENV['LOCATION'] = 'PROD' if ENV['LOCATION'].nil?
31
+ begin
32
+ Rake::Task[:'test:processor'].execute
33
+ rescue
34
+ # don't care, just run it again. can examine output later
35
+ end
36
+ end
37
+
38
+ desc 'Run all the tests against a specific processor. Use LOCATION to specify web service URL.'
39
+ Rake::TestTask.new(:processor) do |t|
40
+ t.libs << 'lib'
41
+ t.pattern = 'test/**/*_test.rb'
42
+ t.verbose = true
43
+ end
44
+
45
+ end
46
+
47
+ desc "Build the RDoc"
48
+ Rake::RDocTask.new { |rdoc|
49
+ rdoc.rdoc_dir = 'doc'
50
+ rdoc.title = "exact4r"
51
+ rdoc.options << '--main' << 'README' << "--inline-source" << "--line-numbers"
52
+ rdoc.rdoc_files.include('CHANGELOG', 'LICENCE', 'README', 'VERSION', 'lib/**/*.rb')
53
+ }
54
+
55
+ desc "Build the exact4r gem"
56
+ spec = Gem::Specification.new do |s|
57
+ s.name="exact4r"
58
+ s.author = "E-xact Transactions Ltd."
59
+ s.homepage = "http://e-xact4r.rubyforge.org/"
60
+ s.rubyforge_project = "exact4r"
61
+ s.email = "dredmond@e-xact.com"
62
+ s.version=`cat VERSION`
63
+ s.summary = 'E-xact Web Services Client Library.'
64
+ s.files = FileList["./**/**"].to_a
65
+ s.has_rdoc = true
66
+ s.extra_rdoc_files = ['CHANGELOG', 'LICENCE', 'README', 'VERSION']
67
+ s.rdoc_options << '--main' << 'README' << '--inline-source' << '--line-numbers'
68
+ s.add_dependency('activesupport', '>= 2.3.2')
69
+ s.add_dependency('builder', '>= 2.1.2')
70
+ end
71
+
72
+ Rake::GemPackageTask.new(spec) do |pkg|
73
+ pkg.need_tar = true
74
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.7
@@ -0,0 +1,31 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIFVTCCBD2gAwIBAgIHBITApWVLMTANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE
3
+ BhMCVVMxEDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAY
4
+ BgNVBAoTEUdvRGFkZHkuY29tLCBJbmMuMTMwMQYDVQQLEypodHRwOi8vY2VydGlm
5
+ aWNhdGVzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkxMDAuBgNVBAMTJ0dvIERhZGR5
6
+ IFNlY3VyZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTERMA8GA1UEBRMIMDc5Njky
7
+ ODcwHhcNMTAwMjE2MjMzNTQzWhcNMTUwMjE2MjMzNTQzWjBRMRUwEwYDVQQKDAwq
8
+ LmUteGFjdC5jb20xITAfBgNVBAsMGERvbWFpbiBDb250cm9sIFZhbGlkYXRlZDEV
9
+ MBMGA1UEAwwMKi5lLXhhY3QuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
10
+ CgKCAQEApvqzJMP4vtbvyXwepZhj8uaKXONIo4H8aN51FOL1PDdGEfWktlWw38Xj
11
+ dU0KJrGeTcBgsfg8NehFwasilW6IbojhMmnvWyYaTzklEjMDmgda3hGRJRqbg/dW
12
+ v0nZRMO7Xy0NNMGHeN9Sxs8977LRV5Y1VjK1M3WhilP3oxe49Ov1K4FEZrxT0fbn
13
+ sqClwAm059XM+qWkUY5tBs6KSZQf1/+Xlx5txB/IqDWLrN5oRuCa+mBi5mIFE7nA
14
+ suFtI/26szCYVuK6r8spbsYeo13c/qTv4yaZbLW3uXgbgTuI4FAtOR9NpUYvLQHR
15
+ 9kYYPeusqj0DnLD6ELMXkujHW0V6MQIDAQABo4IBtjCCAbIwDwYDVR0TAQH/BAUw
16
+ AwEBADAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDgYDVR0PAQH/BAQD
17
+ AgWgMDMGA1UdHwQsMCowKKAmoCSGImh0dHA6Ly9jcmwuZ29kYWRkeS5jb20vZ2Rz
18
+ MS0xNC5jcmwwUwYDVR0gBEwwSjBIBgtghkgBhv1tAQcXATA5MDcGCCsGAQUFBwIB
19
+ FitodHRwOi8vY2VydGlmaWNhdGVzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMIGA
20
+ BggrBgEFBQcBAQR0MHIwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmdvZGFkZHku
21
+ Y29tLzBKBggrBgEFBQcwAoY+aHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNv
22
+ bS9yZXBvc2l0b3J5L2dkX2ludGVybWVkaWF0ZS5jcnQwHwYDVR0jBBgwFoAU/axh
23
+ MpNsRdbi7oVfmrrndplozOcwIwYDVR0RBBwwGoIMKi5lLXhhY3QuY29tggplLXhh
24
+ Y3QuY29tMB0GA1UdDgQWBBRmlEOazZNz8dfnnl7UimMDqciOOzANBgkqhkiG9w0B
25
+ AQUFAAOCAQEArQNhlZ3ij3Yz1U2GHiNY4fpYtNCAhzlrnNZUHaDlhiWEvOcXSB4j
26
+ ER77sgaHmZOm8PW0mXg3eK0+Km5ANWbNbPLe0yPpKRa1GbmgxQx/P4MWMiM+872l
27
+ QmpZlgLw2cGvivALAt7S74QTiqjYX10nNyHlpnlvB9Am2WgzQHQDzyKuKGglFjlw
28
+ ItZpTFFkSGEWK99cxE69GMwtZCsr4b+RB80sDA+ckd45GISg808GkWHfqJVa64k8
29
+ dlXVgRNQwLfrWN+BQeUsfsKu5jiNI7H2a8zrRt4mCa9urh15O+d4pDhXhrYSkQpI
30
+ ezmbzKzCEd1mpiT+sjCzByN3uX6n7eF/Eg==
31
+ -----END CERTIFICATE-----
data/certs/exact.cer CHANGED
@@ -1,18 +1,18 @@
1
1
  -----BEGIN CERTIFICATE-----
2
- MIIC7DCCAlWgAwIBAgIDCFDUMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT
2
+ MIIC7DCCAlWgAwIBAgIDCqSQMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT
3
3
  MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0
4
- aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDcxMTE1MDA0MDAyWhcNMDkwMjE0MDA0MDAy
4
+ aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDkwMjEzMDAxMDQ2WhcNMTAwMzE1MjMxMDQ2
5
5
  WjB3MQswCQYDVQQGEwJDQTEZMBcGA1UECBMQQnJpdGlzaCBDb2x1bWJpYTESMBAG
6
6
  A1UEBxMJVmFuY291dmVyMSIwIAYDVQQKExlFLXhhY3QgVHJhbnNhY3Rpb25zLCBM
7
7
  dGQuMRUwEwYDVQQDFAwqLmUteGFjdC5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0A
8
- MIGJAoGBAKDWJW7DQ9EAkYE0j8QKZtGyRPx84NHaMLA9uJsqG9EMPfNCRd8Puafx
9
- dJ6phUS/gP/ELRmJLlKO7qg6hTfLB3UwvAoMfF8VZM8jNbd3/b+SQD6CiPX0vETH
10
- 85qIegdNrRQNm0ZKhq10bQlzKyoOrk0MDzDd6a98NyrLhVvb1nDlAgMBAAGjga4w
11
- gaswDgYDVR0PAQH/BAQDAgTwMB0GA1UdDgQWBBTsLqYhwlm0BeEOq0Mhl6P5lxWP
12
- cTA6BgNVHR8EMzAxMC+gLaArhilodHRwOi8vY3JsLmdlb3RydXN0LmNvbS9jcmxz
8
+ MIGJAoGBAKAEtyoEOQMqbYNTo/XGXx68vElKFb3lhQ9qimRlggadSCA3F1P40dMz
9
+ 3PlXDtKN9TcvqYalvmrntjmtY7/QgBu59AJOIoFDXe/XCtW16XAlANIrTvS9zhHd
10
+ nnLhCHcLAhEJIssWb3ddXiK8NDIyHtIbWYRIdzT0aPOLtoUXfcBLAgMBAAGjga4w
11
+ gaswDgYDVR0PAQH/BAQDAgTwMB0GA1UdDgQWBBSTkCQC1iSGLxD/+kL5elS75kLe
12
+ hDA6BgNVHR8EMzAxMC+gLaArhilodHRwOi8vY3JsLmdlb3RydXN0LmNvbS9jcmxz
13
13
  L3NlY3VyZWNhLmNybDAfBgNVHSMEGDAWgBRI5mj5K9KylddH2CMgEE8zmJCf1DAd
14
14
  BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDQYJKoZIhvcNAQEFBQADgYEA
15
- X5PCTOTdEnxbG3qcrpgiwnh3ax0Orgy8VUEv3q08GlTu8b+p96/p9e4pWFnZ1FtH
16
- F8B9PGPeDGZDu0+d3HcMuPBgdl/qyVPI7BIfED7V/PXLj92QQP5VI49R9DI1dj+O
17
- bWaptb3x1gcxEP7ifBLl4fg080RlxAJmFrHTF2h9TQ4=
15
+ iHFOvsHR2SZ93jry4TaDWXXVN3gBuK9S7j4ubcGbT4HZe/1OJXE8Hpdc6lFaBgdi
16
+ ZK2qHFzZ7suhcOiF6f7HCtDDXdoGAhYJVWGvhd/2CsvaRzdVWkUQzfRgDXKoYU9W
17
+ BEQs/Y5lG23rmq4WJe6HSVN0FMaZS3aVIr54821hKPc=
18
18
  -----END CERTIFICATE-----
@@ -0,0 +1,18 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
3
+ IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
4
+ BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
5
+ aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
6
+ 9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy
7
+ NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
8
+ azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
9
+ YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
10
+ Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
11
+ cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY
12
+ dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9
13
+ WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS
14
+ v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v
15
+ UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu
16
+ IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC
17
+ W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd
18
+ -----END CERTIFICATE-----
@@ -0,0 +1,68 @@
1
+ module EWS # :nodoc:
2
+ module CertificateHelper
3
+
4
+ EXACT_ISSUER_CERT_FILE = File.dirname(__FILE__)+"/../../certs/valicert_class2_root.crt" unless defined?(EXACT_ISSUER_CERT_FILE)
5
+ EXACT_SERVER_CERT_FILE = File.dirname(__FILE__)+"/../../certs/e-xact.com.crt" unless defined?(EXACT_SERVER_CERT_FILE)
6
+
7
+ attr_accessor :issuer_cert_file, :server_cert, :client_cert, :client_key
8
+
9
+ def configure_certificates(options)
10
+ self.issuer_cert_file = (options[:issuer_cert] || EXACT_ISSUER_CERT_FILE)
11
+ server_cert_file = (options[:server_cert] || EXACT_SERVER_CERT_FILE)
12
+ self.server_cert = File.new(server_cert_file).read
13
+ if options[:client_cert]
14
+ raise ArgumentError.new "Key file not supplied" if options[:client_key].blank?
15
+ self.client_cert = OpenSSL::X509::Certificate.new(File.new(options[:client_cert]).read)
16
+ self.client_key = client_cert.public_key.class.send(:new, File.new(options[:client_key]).read)
17
+ end
18
+ end
19
+ private :configure_certificates
20
+
21
+ def validate_certificate(is_ok, ctx)
22
+ cert = ctx.current_cert
23
+ return false if cert.nil?
24
+
25
+ # preverify failed?
26
+ return false unless is_ok
27
+
28
+ self_signed = false
29
+ ca = false
30
+ pathlen = nil
31
+ server_auth = true
32
+ self_signed = (cert.subject.cmp(cert.issuer) == 0)
33
+
34
+ # Check extensions for the certificate purpose according to http://www.openssl.org/docs/apps/x509.html (Certificate Extensions) and
35
+ # http://www.ietf.org/rfc/rfc3280.txt.
36
+ cert.extensions.each do |ex|
37
+ case ex.oid
38
+ when 'basicConstraints'
39
+ /CA:(TRUE|FALSE)(?:, pathlen:)*(\d*)/ =~ ex.value
40
+ ca ||= ($1 == 'TRUE')
41
+ pathlen = $2.to_i
42
+ when 'keyUsage'
43
+ usage = ex.value.split(/\s*,\s*/)
44
+ # a CA must have
45
+ ca &&= !usage.grep(/Certificate Sign/i).empty?
46
+ # Server Cert Must have
47
+ server_auth &&= !usage.grep(/Key Encipherment/i).empty?
48
+ when 'extendedKeyUsage'
49
+ usage = ex.value.split(/\s*,\s*/)
50
+ # Server Cert Must have
51
+ server_auth &&= !usage.grep(/TLS Web Server Authentication/i).empty?
52
+ when 'nsCertType'
53
+ usage = ex.value.split(/\s*,\s*/)
54
+ ca ||= !usage.grep(/SSL CA/i).empty?
55
+ server_auth ||= !usage.grep(/SSL Server/i).empty?
56
+ end
57
+ end
58
+
59
+ # We're looking for the server cert, so accept all CAs (which have already passed pre-verification)
60
+ return true if self_signed || ca
61
+
62
+ # ensure the server cert is the one we're expecting
63
+ return server_auth && self.server_cert == cert.to_pem
64
+ end
65
+ private :validate_certificate
66
+
67
+ end
68
+ end