exact4r 0.5 → 1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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