ruby-saml 1.9.0 → 1.14.0

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 (159) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/test.yml +25 -0
  3. data/{changelog.md → CHANGELOG.md} +64 -1
  4. data/README.md +394 -211
  5. data/UPGRADING.md +149 -0
  6. data/lib/onelogin/ruby-saml/attributes.rb +24 -1
  7. data/lib/onelogin/ruby-saml/authrequest.rb +26 -10
  8. data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +285 -184
  9. data/lib/onelogin/ruby-saml/logging.rb +3 -3
  10. data/lib/onelogin/ruby-saml/logoutrequest.rb +26 -11
  11. data/lib/onelogin/ruby-saml/logoutresponse.rb +27 -11
  12. data/lib/onelogin/ruby-saml/metadata.rb +62 -17
  13. data/lib/onelogin/ruby-saml/response.rb +86 -37
  14. data/lib/onelogin/ruby-saml/saml_message.rb +14 -5
  15. data/lib/onelogin/ruby-saml/setting_error.rb +6 -0
  16. data/lib/onelogin/ruby-saml/settings.rb +117 -41
  17. data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +33 -31
  18. data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +43 -20
  19. data/lib/onelogin/ruby-saml/utils.rb +101 -9
  20. data/lib/onelogin/ruby-saml/version.rb +1 -1
  21. data/lib/xml_security.rb +39 -13
  22. data/ruby-saml.gemspec +21 -8
  23. metadata +43 -284
  24. data/.travis.yml +0 -32
  25. data/test/certificates/certificate1 +0 -12
  26. data/test/certificates/certificate_without_head_foot +0 -1
  27. data/test/certificates/formatted_certificate +0 -14
  28. data/test/certificates/formatted_chained_certificate +0 -42
  29. data/test/certificates/formatted_private_key +0 -12
  30. data/test/certificates/formatted_rsa_private_key +0 -12
  31. data/test/certificates/invalid_certificate1 +0 -1
  32. data/test/certificates/invalid_certificate2 +0 -1
  33. data/test/certificates/invalid_certificate3 +0 -12
  34. data/test/certificates/invalid_chained_certificate1 +0 -1
  35. data/test/certificates/invalid_private_key1 +0 -1
  36. data/test/certificates/invalid_private_key2 +0 -1
  37. data/test/certificates/invalid_private_key3 +0 -10
  38. data/test/certificates/invalid_rsa_private_key1 +0 -1
  39. data/test/certificates/invalid_rsa_private_key2 +0 -1
  40. data/test/certificates/invalid_rsa_private_key3 +0 -10
  41. data/test/certificates/ruby-saml-2.crt +0 -15
  42. data/test/certificates/ruby-saml.crt +0 -14
  43. data/test/certificates/ruby-saml.key +0 -15
  44. data/test/idp_metadata_parser_test.rb +0 -579
  45. data/test/logging_test.rb +0 -62
  46. data/test/logout_requests/invalid_slo_request.xml +0 -6
  47. data/test/logout_requests/slo_request.xml +0 -4
  48. data/test/logout_requests/slo_request.xml.base64 +0 -1
  49. data/test/logout_requests/slo_request_deflated.xml.base64 +0 -1
  50. data/test/logout_requests/slo_request_with_name_id_format.xml +0 -4
  51. data/test/logout_requests/slo_request_with_session_index.xml +0 -5
  52. data/test/logout_responses/logoutresponse_fixtures.rb +0 -67
  53. data/test/logoutrequest_test.rb +0 -226
  54. data/test/logoutresponse_test.rb +0 -402
  55. data/test/metadata/idp_descriptor.xml +0 -26
  56. data/test/metadata/idp_descriptor_2.xml +0 -56
  57. data/test/metadata/idp_descriptor_3.xml +0 -14
  58. data/test/metadata/idp_descriptor_4.xml +0 -72
  59. data/test/metadata/idp_metadata_different_sign_and_encrypt_cert.xml +0 -72
  60. data/test/metadata/idp_metadata_multi_certs.xml +0 -75
  61. data/test/metadata/idp_metadata_multi_signing_certs.xml +0 -52
  62. data/test/metadata/idp_metadata_same_sign_and_encrypt_cert.xml +0 -71
  63. data/test/metadata/idp_multiple_descriptors.xml +0 -53
  64. data/test/metadata/no_idp_descriptor.xml +0 -21
  65. data/test/metadata_test.rb +0 -331
  66. data/test/request_test.rb +0 -323
  67. data/test/response_test.rb +0 -1619
  68. data/test/responses/adfs_response_sha1.xml +0 -46
  69. data/test/responses/adfs_response_sha256.xml +0 -46
  70. data/test/responses/adfs_response_sha384.xml +0 -46
  71. data/test/responses/adfs_response_sha512.xml +0 -46
  72. data/test/responses/adfs_response_xmlns.xml +0 -45
  73. data/test/responses/attackxee.xml +0 -13
  74. data/test/responses/invalids/duplicated_attributes.xml.base64 +0 -1
  75. data/test/responses/invalids/empty_destination.xml.base64 +0 -1
  76. data/test/responses/invalids/empty_nameid.xml.base64 +0 -1
  77. data/test/responses/invalids/encrypted_new_attack.xml.base64 +0 -1
  78. data/test/responses/invalids/invalid_audience.xml.base64 +0 -1
  79. data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +0 -1
  80. data/test/responses/invalids/invalid_issuer_message.xml.base64 +0 -1
  81. data/test/responses/invalids/invalid_signature_position.xml.base64 +0 -1
  82. data/test/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 +0 -1
  83. data/test/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 +0 -1
  84. data/test/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 +0 -1
  85. data/test/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 +0 -1
  86. data/test/responses/invalids/multiple_assertions.xml.base64 +0 -2
  87. data/test/responses/invalids/multiple_signed.xml.base64 +0 -1
  88. data/test/responses/invalids/no_authnstatement.xml.base64 +0 -1
  89. data/test/responses/invalids/no_conditions.xml.base64 +0 -1
  90. data/test/responses/invalids/no_id.xml.base64 +0 -1
  91. data/test/responses/invalids/no_issuer_assertion.xml.base64 +0 -1
  92. data/test/responses/invalids/no_issuer_response.xml.base64 +0 -1
  93. data/test/responses/invalids/no_nameid.xml.base64 +0 -1
  94. data/test/responses/invalids/no_saml2.xml.base64 +0 -1
  95. data/test/responses/invalids/no_signature.xml.base64 +0 -1
  96. data/test/responses/invalids/no_status.xml.base64 +0 -1
  97. data/test/responses/invalids/no_status_code.xml.base64 +0 -1
  98. data/test/responses/invalids/no_subjectconfirmation_data.xml.base64 +0 -1
  99. data/test/responses/invalids/no_subjectconfirmation_method.xml.base64 +0 -1
  100. data/test/responses/invalids/response_invalid_signed_element.xml.base64 +0 -1
  101. data/test/responses/invalids/response_with_concealed_signed_assertion.xml +0 -51
  102. data/test/responses/invalids/response_with_doubled_signed_assertion.xml +0 -49
  103. data/test/responses/invalids/signature_wrapping_attack.xml.base64 +0 -1
  104. data/test/responses/invalids/status_code_responder.xml.base64 +0 -1
  105. data/test/responses/invalids/status_code_responer_and_msg.xml.base64 +0 -1
  106. data/test/responses/invalids/wrong_spnamequalifier.xml.base64 +0 -1
  107. data/test/responses/no_signature_ns.xml +0 -48
  108. data/test/responses/open_saml_response.xml +0 -56
  109. data/test/responses/response_assertion_wrapped.xml.base64 +0 -93
  110. data/test/responses/response_audience_self_closed_tag.xml.base64 +0 -1
  111. data/test/responses/response_double_status_code.xml.base64 +0 -1
  112. data/test/responses/response_encrypted_attrs.xml.base64 +0 -1
  113. data/test/responses/response_encrypted_nameid.xml.base64 +0 -1
  114. data/test/responses/response_eval.xml +0 -7
  115. data/test/responses/response_no_cert_and_encrypted_attrs.xml +0 -29
  116. data/test/responses/response_node_text_attack.xml.base64 +0 -1
  117. data/test/responses/response_node_text_attack2.xml.base64 +0 -1
  118. data/test/responses/response_node_text_attack3.xml.base64 +0 -1
  119. data/test/responses/response_unsigned_xml_base64 +0 -1
  120. data/test/responses/response_with_ampersands.xml +0 -139
  121. data/test/responses/response_with_ampersands.xml.base64 +0 -93
  122. data/test/responses/response_with_ds_namespace_at_the_root.xml.base64 +0 -1
  123. data/test/responses/response_with_multiple_attribute_statements.xml +0 -72
  124. data/test/responses/response_with_multiple_attribute_values.xml +0 -67
  125. data/test/responses/response_with_retrieval_method.xml +0 -26
  126. data/test/responses/response_with_saml2_namespace.xml.base64 +0 -102
  127. data/test/responses/response_with_signed_assertion.xml.base64 +0 -66
  128. data/test/responses/response_with_signed_assertion_2.xml.base64 +0 -1
  129. data/test/responses/response_with_signed_assertion_3.xml +0 -30
  130. data/test/responses/response_with_signed_message_and_assertion.xml +0 -34
  131. data/test/responses/response_with_undefined_recipient.xml.base64 +0 -1
  132. data/test/responses/response_without_attributes.xml.base64 +0 -79
  133. data/test/responses/response_without_reference_uri.xml.base64 +0 -1
  134. data/test/responses/response_wrapped.xml.base64 +0 -150
  135. data/test/responses/signed_message_encrypted_signed_assertion.xml.base64 +0 -1
  136. data/test/responses/signed_message_encrypted_unsigned_assertion.xml.base64 +0 -1
  137. data/test/responses/signed_nameid_in_atts.xml +0 -47
  138. data/test/responses/signed_unqual_nameid_in_atts.xml +0 -47
  139. data/test/responses/simple_saml_php.xml +0 -71
  140. data/test/responses/starfield_response.xml.base64 +0 -1
  141. data/test/responses/test_sign.xml +0 -43
  142. data/test/responses/unsigned_encrypted_adfs.xml +0 -23
  143. data/test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64 +0 -1
  144. data/test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64 +0 -1
  145. data/test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64 +0 -1
  146. data/test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64 +0 -1
  147. data/test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64 +0 -1
  148. data/test/responses/unsigned_message_encrypted_signed_assertion.xml.base64 +0 -1
  149. data/test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64 +0 -1
  150. data/test/responses/valid_response.xml.base64 +0 -1
  151. data/test/responses/valid_response_with_formatted_x509certificate.xml.base64 +0 -1
  152. data/test/responses/valid_response_without_x509certificate.xml.base64 +0 -1
  153. data/test/saml_message_test.rb +0 -56
  154. data/test/settings_test.rb +0 -329
  155. data/test/slo_logoutrequest_test.rb +0 -448
  156. data/test/slo_logoutresponse_test.rb +0 -199
  157. data/test/test_helper.rb +0 -327
  158. data/test/utils_test.rb +0 -254
  159. data/test/xml_security_test.rb +0 -421
@@ -3,6 +3,7 @@ if RUBY_VERSION < '1.9'
3
3
  else
4
4
  require 'securerandom'
5
5
  end
6
+ require "openssl"
6
7
 
7
8
  module OneLogin
8
9
  module RubySaml
@@ -12,8 +13,69 @@ module OneLogin
12
13
  class Utils
13
14
  @@uuid_generator = UUID.new if RUBY_VERSION < '1.9'
14
15
 
15
- DSIG = "http://www.w3.org/2000/09/xmldsig#"
16
- XENC = "http://www.w3.org/2001/04/xmlenc#"
16
+ BINDINGS = { :post => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST".freeze,
17
+ :redirect => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect".freeze }.freeze
18
+ DSIG = "http://www.w3.org/2000/09/xmldsig#".freeze
19
+ XENC = "http://www.w3.org/2001/04/xmlenc#".freeze
20
+ DURATION_FORMAT = %r(^
21
+ (-?)P # 1: Duration sign
22
+ (?:
23
+ (?:(\d+)Y)? # 2: Years
24
+ (?:(\d+)M)? # 3: Months
25
+ (?:(\d+)D)? # 4: Days
26
+ (?:T
27
+ (?:(\d+)H)? # 5: Hours
28
+ (?:(\d+)M)? # 6: Minutes
29
+ (?:(\d+(?:[.,]\d+)?)S)? # 7: Seconds
30
+ )?
31
+ |
32
+ (\d+)W # 8: Weeks
33
+ )
34
+ $)x.freeze
35
+ UUID_PREFIX = '_'
36
+
37
+ # Checks if the x509 cert provided is expired
38
+ #
39
+ # @param cert [Certificate] The x509 certificate
40
+ #
41
+ def self.is_cert_expired(cert)
42
+ if cert.is_a?(String)
43
+ cert = OpenSSL::X509::Certificate.new(cert)
44
+ end
45
+
46
+ return cert.not_after < Time.now
47
+ end
48
+
49
+ # Interprets a ISO8601 duration value relative to a given timestamp.
50
+ #
51
+ # @param duration [String] The duration, as a string.
52
+ # @param timestamp [Integer] The unix timestamp we should apply the
53
+ # duration to. Optional, default to the
54
+ # current time.
55
+ #
56
+ # @return [Integer] The new timestamp, after the duration is applied.
57
+ #
58
+ def self.parse_duration(duration, timestamp=Time.now.utc)
59
+ return nil if RUBY_VERSION < '1.9' # 1.8.7 not supported
60
+
61
+ matches = duration.match(DURATION_FORMAT)
62
+
63
+ if matches.nil?
64
+ raise Exception.new("Invalid ISO 8601 duration")
65
+ end
66
+
67
+ sign = matches[1] == '-' ? -1 : 1
68
+
69
+ durYears, durMonths, durDays, durHours, durMinutes, durSeconds, durWeeks =
70
+ matches[2..8].map { |match| match ? sign * match.tr(',', '.').to_f : 0.0 }
71
+
72
+ initial_datetime = Time.at(timestamp).utc.to_datetime
73
+ final_datetime = initial_datetime.next_year(durYears)
74
+ final_datetime = final_datetime.next_month(durMonths)
75
+ final_datetime = final_datetime.next_day((7*durWeeks) + durDays)
76
+ final_timestamp = final_datetime.to_time.utc.to_i + (durHours * 3600) + (durMinutes * 60) + durSeconds
77
+ return final_timestamp
78
+ end
17
79
 
18
80
  # Return a properly formatted x509 certificate
19
81
  #
@@ -22,7 +84,11 @@ module OneLogin
22
84
  #
23
85
  def self.format_cert(cert)
24
86
  # don't try to format an encoded certificate or if is empty or nil
25
- return cert if cert.nil? || cert.empty? || cert.match(/\x0d/)
87
+ if cert.respond_to?(:ascii_only?)
88
+ return cert if cert.nil? || cert.empty? || !cert.ascii_only?
89
+ else
90
+ return cert if cert.nil? || cert.empty? || cert.match(/\x0d/)
91
+ end
26
92
 
27
93
  if cert.scan(/BEGIN CERTIFICATE/).length > 1
28
94
  formatted_cert = []
@@ -101,27 +167,36 @@ module OneLogin
101
167
  #
102
168
  # @param rawparams [Hash] Raw GET Parameters
103
169
  # @param params [Hash] GET Parameters
170
+ # @param lowercase_url_encoding [bool] Lowercase URL Encoding (For ADFS urlencode compatiblity)
104
171
  # @return [Hash] New raw parameters
105
172
  #
106
- def self.prepare_raw_get_params(rawparams, params)
173
+ def self.prepare_raw_get_params(rawparams, params, lowercase_url_encoding=false)
107
174
  rawparams ||= {}
108
175
 
109
176
  if rawparams['SAMLRequest'].nil? && !params['SAMLRequest'].nil?
110
- rawparams['SAMLRequest'] = CGI.escape(params['SAMLRequest'])
177
+ rawparams['SAMLRequest'] = escape_request_param(params['SAMLRequest'], lowercase_url_encoding)
111
178
  end
112
179
  if rawparams['SAMLResponse'].nil? && !params['SAMLResponse'].nil?
113
- rawparams['SAMLResponse'] = CGI.escape(params['SAMLResponse'])
180
+ rawparams['SAMLResponse'] = escape_request_param(params['SAMLResponse'], lowercase_url_encoding)
114
181
  end
115
182
  if rawparams['RelayState'].nil? && !params['RelayState'].nil?
116
- rawparams['RelayState'] = CGI.escape(params['RelayState'])
183
+ rawparams['RelayState'] = escape_request_param(params['RelayState'], lowercase_url_encoding)
117
184
  end
118
185
  if rawparams['SigAlg'].nil? && !params['SigAlg'].nil?
119
- rawparams['SigAlg'] = CGI.escape(params['SigAlg'])
186
+ rawparams['SigAlg'] = escape_request_param(params['SigAlg'], lowercase_url_encoding)
120
187
  end
121
188
 
122
189
  rawparams
123
190
  end
124
191
 
192
+ def self.escape_request_param(param, lowercase_url_encoding)
193
+ CGI.escape(param).tap do |escaped|
194
+ next unless lowercase_url_encoding
195
+
196
+ escaped.gsub!(/%[A-Fa-f0-9]{2}/) { |match| match.downcase }
197
+ end
198
+ end
199
+
125
200
  # Validate the Signature parameter sent on the HTTP-Redirect binding
126
201
  # @param params [Hash] Parameters to be used in the validation process
127
202
  # @option params [OpenSSL::X509::Certificate] cert The Identity provider public certtificate
@@ -236,6 +311,9 @@ module OneLogin
236
311
  when 'http://www.w3.org/2001/04/xmlenc#aes128-cbc' then cipher = OpenSSL::Cipher.new('AES-128-CBC').decrypt
237
312
  when 'http://www.w3.org/2001/04/xmlenc#aes192-cbc' then cipher = OpenSSL::Cipher.new('AES-192-CBC').decrypt
238
313
  when 'http://www.w3.org/2001/04/xmlenc#aes256-cbc' then cipher = OpenSSL::Cipher.new('AES-256-CBC').decrypt
314
+ when 'http://www.w3.org/2009/xmlenc11#aes128-gcm' then auth_cipher = OpenSSL::Cipher::AES.new(128, :GCM).decrypt
315
+ when 'http://www.w3.org/2009/xmlenc11#aes192-gcm' then auth_cipher = OpenSSL::Cipher::AES.new(192, :GCM).decrypt
316
+ when 'http://www.w3.org/2009/xmlenc11#aes256-gcm' then auth_cipher = OpenSSL::Cipher::AES.new(256, :GCM).decrypt
239
317
  when 'http://www.w3.org/2001/04/xmlenc#rsa-1_5' then rsa = symmetric_key
240
318
  when 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p' then oaep = symmetric_key
241
319
  end
@@ -246,6 +324,16 @@ module OneLogin
246
324
  cipher.padding, cipher.key, cipher.iv = 0, symmetric_key, cipher_text[0..iv_len-1]
247
325
  assertion_plaintext = cipher.update(data)
248
326
  assertion_plaintext << cipher.final
327
+ elsif auth_cipher
328
+ iv_len, text_len, tag_len = auth_cipher.iv_len, cipher_text.length, 16
329
+ data = cipher_text[iv_len..text_len-1-tag_len]
330
+ auth_cipher.padding = 0
331
+ auth_cipher.key = symmetric_key
332
+ auth_cipher.iv = cipher_text[0..iv_len-1]
333
+ auth_cipher.auth_data = ''
334
+ auth_cipher.auth_tag = cipher_text[text_len-tag_len..-1]
335
+ assertion_plaintext = auth_cipher.update(data)
336
+ assertion_plaintext << auth_cipher.final
249
337
  elsif rsa
250
338
  rsa.private_decrypt(cipher_text)
251
339
  elsif oaep
@@ -255,8 +343,12 @@ module OneLogin
255
343
  end
256
344
  end
257
345
 
346
+ def self.set_prefix(value)
347
+ UUID_PREFIX.replace value
348
+ end
349
+
258
350
  def self.uuid
259
- RUBY_VERSION < '1.9' ? "_#{@@uuid_generator.generate}" : "_#{SecureRandom.uuid}"
351
+ "#{UUID_PREFIX}" + (RUBY_VERSION < '1.9' ? "#{@@uuid_generator.generate}" : "#{SecureRandom.uuid}")
260
352
  end
261
353
 
262
354
  # Given two strings, attempt to match them as URIs using Rails' parse method. If they can be parsed,
@@ -1,5 +1,5 @@
1
1
  module OneLogin
2
2
  module RubySaml
3
- VERSION = '1.9.0'
3
+ VERSION = '1.14.0'
4
4
  end
5
5
  end
data/lib/xml_security.rb CHANGED
@@ -159,15 +159,13 @@ module XMLSecurity
159
159
  x509_cert_element.text = Base64.encode64(certificate.to_der).gsub(/\n/, "")
160
160
 
161
161
  # add the signature
162
- issuer_element = self.elements["//saml:Issuer"]
162
+ issuer_element = elements["//saml:Issuer"]
163
163
  if issuer_element
164
- self.root.insert_after issuer_element, signature_element
164
+ root.insert_after(issuer_element, signature_element)
165
+ elsif first_child = root.children[0]
166
+ root.insert_before(first_child, signature_element)
165
167
  else
166
- if sp_sso_descriptor = self.elements["/md:EntityDescriptor"]
167
- self.root.insert_before sp_sso_descriptor, signature_element
168
- else
169
- self.root.add_element(signature_element)
170
- end
168
+ root.add_element(signature_element)
171
169
  end
172
170
  end
173
171
 
@@ -212,7 +210,7 @@ module XMLSecurity
212
210
  begin
213
211
  cert = OpenSSL::X509::Certificate.new(cert_text)
214
212
  rescue OpenSSL::X509::CertificateError => _e
215
- return append_error("Certificate Error", soft)
213
+ return append_error("Document Certificate Error", soft)
216
214
  end
217
215
 
218
216
  if options[:fingerprint_alg]
@@ -224,7 +222,6 @@ module XMLSecurity
224
222
 
225
223
  # check cert matches registered idp cert
226
224
  if fingerprint != idp_cert_fingerprint.gsub(/[^a-zA-Z0-9]/,"").downcase
227
- @errors << "Fingerprint mismatch"
228
225
  return append_error("Fingerprint mismatch", soft)
229
226
  end
230
227
  else
@@ -241,7 +238,7 @@ module XMLSecurity
241
238
  validate_signature(base64_cert, soft)
242
239
  end
243
240
 
244
- def validate_document_with_cert(idp_cert)
241
+ def validate_document_with_cert(idp_cert, soft = true)
245
242
  # get cert from response
246
243
  cert_element = REXML::XPath.first(
247
244
  self,
@@ -255,12 +252,12 @@ module XMLSecurity
255
252
  begin
256
253
  cert = OpenSSL::X509::Certificate.new(cert_text)
257
254
  rescue OpenSSL::X509::CertificateError => _e
258
- return append_error("Certificate Error", soft)
255
+ return append_error("Document Certificate Error", soft)
259
256
  end
260
257
 
261
258
  # check saml response cert matches provided idp cert
262
259
  if idp_cert.to_pem != cert.to_pem
263
- return false
260
+ return append_error("Certificate of the Signature element does not match provided certificate", soft)
264
261
  end
265
262
  else
266
263
  base64_cert = Base64.encode64(idp_cert.to_pem)
@@ -326,6 +323,9 @@ module XMLSecurity
326
323
  '//ds:CanonicalizationMethod',
327
324
  { "ds" => DSIG }
328
325
  )
326
+
327
+ canon_algorithm = process_transforms(ref, canon_algorithm)
328
+
329
329
  canon_hashed_element = hashed_element.canonicalize(canon_algorithm, inclusive_namespaces)
330
330
 
331
331
  digest_algorithm = algorithm(REXML::XPath.first(
@@ -342,7 +342,6 @@ module XMLSecurity
342
342
  digest_value = Base64.decode64(OneLogin::RubySaml::Utils.element_text(encoded_digest_value))
343
343
 
344
344
  unless digests_match?(hash, digest_value)
345
- @errors << "Digest mismatch"
346
345
  return append_error("Digest mismatch", soft)
347
346
  end
348
347
 
@@ -360,6 +359,33 @@ module XMLSecurity
360
359
 
361
360
  private
362
361
 
362
+ def process_transforms(ref, canon_algorithm)
363
+ transforms = REXML::XPath.match(
364
+ ref,
365
+ "//ds:Transforms/ds:Transform",
366
+ { "ds" => DSIG }
367
+ )
368
+
369
+ transforms.each do |transform_element|
370
+ if transform_element.attributes && transform_element.attributes["Algorithm"]
371
+ algorithm = transform_element.attributes["Algorithm"]
372
+ case algorithm
373
+ when "http://www.w3.org/TR/2001/REC-xml-c14n-20010315",
374
+ "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
375
+ canon_algorithm = Nokogiri::XML::XML_C14N_1_0
376
+ when "http://www.w3.org/2006/12/xml-c14n11",
377
+ "http://www.w3.org/2006/12/xml-c14n11#WithComments"
378
+ canon_algorithm = Nokogiri::XML::XML_C14N_1_1
379
+ when "http://www.w3.org/2001/10/xml-exc-c14n#",
380
+ "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"
381
+ canon_algorithm = Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
382
+ end
383
+ end
384
+ end
385
+
386
+ canon_algorithm
387
+ end
388
+
363
389
  def digests_match?(hash, digest_value)
364
390
  hash == digest_value
365
391
  end
data/ruby-saml.gemspec CHANGED
@@ -15,38 +15,51 @@ Gem::Specification.new do |s|
15
15
  "LICENSE",
16
16
  "README.md"
17
17
  ]
18
- s.files = `git ls-files`.split("\n")
19
- s.homepage = %q{http://github.com/onelogin/ruby-saml}
20
- s.rubyforge_project = %q{http://www.rubygems.org/gems/ruby-saml}
18
+ s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
+ s.homepage = %q{https://github.com/onelogin/ruby-saml}
21
20
  s.rdoc_options = ["--charset=UTF-8"]
22
21
  s.require_paths = ["lib"]
23
22
  s.rubygems_version = %q{1.3.7}
24
23
  s.required_ruby_version = '>= 1.8.7'
25
24
  s.summary = %q{SAML Ruby Tookit}
26
- s.test_files = `git ls-files test/*`.split("\n")
27
25
 
28
26
  # Because runtime dependencies are determined at build time, we cannot make
29
27
  # Nokogiri's version dependent on the Ruby version, even though we would
30
28
  # have liked to constrain Ruby 1.8.7 to install only the 1.5.x versions.
31
29
  if defined?(JRUBY_VERSION)
32
- s.add_runtime_dependency('nokogiri', '>= 1.6.0')
33
- s.add_runtime_dependency('jruby-openssl', '>= 0.9.8') if JRUBY_VERSION < '9.2.0.0'
30
+ if JRUBY_VERSION < '9.2.0.0'
31
+ s.add_runtime_dependency('nokogiri', '>= 1.8.2', '<= 1.8.5')
32
+ s.add_runtime_dependency('jruby-openssl', '>= 0.9.8')
33
+ s.add_runtime_dependency('json', '< 2.3.0')
34
+ else
35
+ s.add_runtime_dependency('nokogiri', '>= 1.8.2')
36
+ end
34
37
  elsif RUBY_VERSION < '1.9'
35
38
  s.add_runtime_dependency('uuid')
36
39
  s.add_runtime_dependency('nokogiri', '<= 1.5.11')
37
40
  elsif RUBY_VERSION < '2.1'
38
41
  s.add_runtime_dependency('nokogiri', '>= 1.5.10', '<= 1.6.8.1')
42
+ s.add_runtime_dependency('json', '< 2.3.0')
43
+ elsif RUBY_VERSION < '2.3'
44
+ s.add_runtime_dependency('nokogiri', '>= 1.9.1', '< 1.10.0')
39
45
  else
40
- s.add_runtime_dependency('nokogiri', '>= 1.5.10')
46
+ s.add_runtime_dependency('nokogiri', '>= 1.10.5')
47
+ s.add_runtime_dependency('rexml')
41
48
  end
42
49
 
50
+ s.add_development_dependency('coveralls')
43
51
  s.add_development_dependency('minitest', '~> 5.5')
44
52
  s.add_development_dependency('mocha', '~> 0.14')
45
53
  s.add_development_dependency('rake', '~> 10')
46
54
  s.add_development_dependency('shoulda', '~> 2.11')
47
55
  s.add_development_dependency('simplecov')
48
56
  s.add_development_dependency('systemu', '~> 2')
49
- s.add_development_dependency('timecop', '<= 0.6.0')
57
+
58
+ if RUBY_VERSION < '2.1'
59
+ s.add_development_dependency('timecop', '<= 0.6.0')
60
+ else
61
+ s.add_development_dependency('timecop', '~> 0.9')
62
+ end
50
63
 
51
64
  if defined?(JRUBY_VERSION)
52
65
  # All recent versions of JRuby play well with pry