ruby-saml 1.4.3 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of ruby-saml might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 455ea502cffeb8d8b1c0bc673de3495e0e1b070a
4
- data.tar.gz: f5327bfb4d61922b77444f397a8347910a6bab33
3
+ metadata.gz: d6958524060034d6eaaf5c0f968711ecdc45e2bd
4
+ data.tar.gz: f1714aa26982ce596b8e07f2e20a8637611d7523
5
5
  SHA512:
6
- metadata.gz: a3842b07e3f0da562c0302a7a399f63901bbe0a7ae88a68f60dce296fc0d7a9eebed08544db2cfcea2857a76dfed58cc158d6dacba10671135a804d899d8edad
7
- data.tar.gz: 0ce3cc58921b2d384983cca9a29a75890e39a25e0b62da97e27f3a4c32baabf15b338ff9cd83b5aeb8d980ff69f10f094b72afab8a047c917ac775b7f4a1b896
6
+ metadata.gz: 1cb2ee3fefaec17210a0087dbadc68698b4fa9969d20c81512c9d1904bf941a59179a73b2d46ed52c38867f0c201b82e9f8cdd0f50b114103b7fe72cfcbb7bdc
7
+ data.tar.gz: ed45f61f08919d846edb627699ed1679f5d7e8c1c5136a4703d3c9129bc7259e9f761a1a8fc12bb439bd5e96128069c5c0b3b9115e1f8d38e55a874523793cec
data/README.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Ruby SAML [![Build Status](https://secure.travis-ci.org/onelogin/ruby-saml.svg)](http://travis-ci.org/onelogin/ruby-saml) [![Coverage Status](https://coveralls.io/repos/onelogin/ruby-saml/badge.svg?branch=master%0A)](https://coveralls.io/r/onelogin/ruby-saml?branch=master%0A) [![Gem Version](https://badge.fury.io/rb/ruby-saml.svg)](http://badge.fury.io/rb/ruby-saml)
2
2
 
3
+ ## Updating from 1.4.2 to 1.4.3
4
+
5
+ Version `1.4.3` introduces Recipient validation of SubjectConfirmation elements.
6
+ The 'Recipient' value is compared with the settings.assertion_consumer_service_url
7
+ value.
8
+ If you want to skip that validation, add the :skip_recipient_check option to the
9
+ initialize method of the Response object.
10
+
3
11
  ## Updating from 1.3.x to 1.4.X
4
12
 
5
13
  Version `1.4.0` is a recommended update for all Ruby SAML users as it includes security improvements.
@@ -169,7 +177,6 @@ def saml_settings
169
177
 
170
178
  settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
171
179
  settings.issuer = "http://#{request.host}/saml/metadata"
172
- settings.idp_sso_target_url = "https://app.onelogin.com/saml/metadata/#{OneLoginAppId}"
173
180
  settings.idp_entity_id = "https://app.onelogin.com/saml/metadata/#{OneLoginAppId}"
174
181
  settings.idp_sso_target_url = "https://app.onelogin.com/trust/saml2/http-post/sso/#{OneLoginAppId}"
175
182
  settings.idp_slo_target_url = "https://app.onelogin.com/trust/saml2/http-redirect/slo/#{OneLoginAppId}"
@@ -1,4 +1,13 @@
1
1
  # RubySaml Changelog
2
+ ### 1.5.0 (August 31, 2017)
3
+ * [#400](https://github.com/onelogin/ruby-saml/pull/400) When validating Signature use stored IdP certficate if Signature contains no info about Certificate
4
+ * [#402](https://github.com/onelogin/ruby-saml/pull/402) Fix validate_response_state method that rejected SAMLResponses when using idp_cert_multi and idp_cert and idp_cert_fingerprint were not provided.
5
+ * [#411](https://github.com/onelogin/ruby-saml/pull/411) Allow space in Base64 string
6
+ * [#407](https://github.com/onelogin/ruby-saml/issues/407) Improve IdpMetadataParser raising an ArgumentError when parser method receive a metadata string with no IDPSSODescriptor element.
7
+ * [#374](https://github.com/onelogin/ruby-saml/issues/374) Support more than one level of StatusCode
8
+ * [#405](https://github.com/onelogin/ruby-saml/pull/405) Support ADFS encrypted key (Accept KeyInfo nodes with no ds namespace)
9
+
10
+
2
11
  ### 1.4.3 (May 18, 2017)
3
12
  * Added SubjectConfirmation Recipient validation
4
13
  * [#393](https://github.com/onelogin/ruby-saml/pull/393) Implement IdpMetadataParser#parse_to_hash
@@ -102,6 +102,10 @@ module OneLogin
102
102
  @options = options
103
103
  @entity_descriptor = nil
104
104
 
105
+ if idpsso_descriptor.nil?
106
+ raise ArgumentError.new("idp_metadata may contain an IDPSSODescriptor element")
107
+ end
108
+
105
109
  {
106
110
  :idp_entity_id => idp_entity_id,
107
111
  :name_identifier_format => idp_name_id_format,
@@ -164,6 +168,16 @@ module OneLogin
164
168
  path << "[@entityID=\"#{entity_id}\"]"
165
169
  end
166
170
 
171
+ def idpsso_descriptor
172
+ unless entity_descriptor.nil?
173
+ return REXML::XPath.first(
174
+ entity_descriptor,
175
+ "md:IDPSSODescriptor",
176
+ namespace
177
+ )
178
+ end
179
+ end
180
+
167
181
  # @return [String|nil] IdP Entity ID value if exists
168
182
  #
169
183
  def idp_entity_id
@@ -335,7 +349,8 @@ module OneLogin
335
349
  )
336
350
  end
337
351
  else
338
- parsed_metadata[:idp_cert_multi] = certificates
352
+ # symbolize keys of certificates and pass it on
353
+ parsed_metadata[:idp_cert_multi] = Hash[certificates.map { |k, v| [k.to_sym, v] }]
339
354
  end
340
355
  end
341
356
 
@@ -168,7 +168,7 @@ module OneLogin
168
168
 
169
169
  return append_error("No issuer in settings of the logout response") if settings.issuer.nil?
170
170
 
171
- if settings.idp_cert_fingerprint.nil? && settings.idp_cert.nil?
171
+ if settings.idp_cert_fingerprint.nil? && settings.idp_cert.nil? && settings.idp_cert_multi.nil?
172
172
  return append_error("No fingerprint or certificate on settings of the logout response")
173
173
  end
174
174
 
@@ -37,6 +37,7 @@ module OneLogin
37
37
  # with the :skip_conditions, or allow a clock_drift when checking dates with :allowed_clock_drift
38
38
  # or :matches_request_id that will validate that the response matches the ID of the request,
39
39
  # or skip the subject confirmation validation with the :skip_subject_confirmation option
40
+ # or skip the recipient validation of the subject confirmation element with :skip_recipient_check option
40
41
  def initialize(response, options = {})
41
42
  raise ArgumentError.new("Response cannot be nil") if response.nil?
42
43
 
@@ -206,7 +207,23 @@ module OneLogin
206
207
  )
207
208
  if nodes.size == 1
208
209
  node = nodes[0]
209
- node.attributes["Value"] if node && node.attributes
210
+ code = node.attributes["Value"] if node && node.attributes
211
+
212
+ unless code == "urn:oasis:names:tc:SAML:2.0:status:Success"
213
+ nodes = REXML::XPath.match(
214
+ document,
215
+ "/p:Response/p:Status/p:StatusCode/p:StatusCode",
216
+ { "p" => PROTOCOL }
217
+ )
218
+ statuses = nodes.collect do |node|
219
+ node.attributes["Value"]
220
+ end
221
+ extra_code = statuses.join(" | ")
222
+ if extra_code
223
+ code = "#{code} | #{extra_code}"
224
+ end
225
+ end
226
+ code
210
227
  end
211
228
  end
212
229
  end
@@ -411,7 +428,7 @@ module OneLogin
411
428
 
412
429
  return append_error("No settings on response") if settings.nil?
413
430
 
414
- if settings.idp_cert_fingerprint.nil? && settings.idp_cert.nil?
431
+ if settings.idp_cert_fingerprint.nil? && settings.idp_cert.nil? && settings.idp_cert_multi.nil?
415
432
  return append_error("No fingerprint or certificate on settings")
416
433
  end
417
434
 
@@ -650,13 +667,13 @@ module OneLogin
650
667
 
651
668
  now = Time.now.utc
652
669
 
653
- if not_before && (now + allowed_clock_drift) < not_before
654
- error_msg = "Current time is earlier than NotBefore condition #{(now + allowed_clock_drift)} < #{not_before})"
670
+ if not_before && (now_with_drift = now + allowed_clock_drift) < not_before
671
+ error_msg = "Current time is earlier than NotBefore condition (#{now_with_drift} < #{not_before})"
655
672
  return append_error(error_msg)
656
673
  end
657
674
 
658
- if not_on_or_after && now >= (not_on_or_after + allowed_clock_drift)
659
- error_msg = "Current time is on or after NotOnOrAfter condition (#{now} >= #{not_on_or_after + allowed_clock_drift})"
675
+ if not_on_or_after && now >= (not_on_or_after_with_drift = not_on_or_after + allowed_clock_drift)
676
+ error_msg = "Current time is on or after NotOnOrAfter condition (#{now} >= #{not_on_or_after_with_drift})"
660
677
  return append_error(error_msg)
661
678
  end
662
679
 
@@ -813,7 +830,7 @@ module OneLogin
813
830
  opts[:cert] = settings.get_idp_cert
814
831
  fingerprint = settings.get_fingerprint
815
832
 
816
- unless fingerprint && doc.validate_document(fingerprint, @soft, opts)
833
+ unless fingerprint && doc.validate_document(fingerprint, @soft, opts)
817
834
  return append_error(error_msg)
818
835
  end
819
836
  else
@@ -129,7 +129,7 @@ module OneLogin
129
129
  # @return [true, false] whether or not the string is base64 encoded
130
130
  #
131
131
  def base64_encoded?(string)
132
- !!string.gsub(/[\r\n]|\\r|\\n/, "").match(BASE64_FORMAT)
132
+ !!string.gsub(/[\r\n]|\\r|\\n|\s/, "").match(BASE64_FORMAT)
133
133
  end
134
134
 
135
135
  # Inflate method
@@ -87,7 +87,15 @@ module OneLogin
87
87
  # @return [String] The status error message
88
88
  def self.status_error_msg(error_msg, status_code = nil, status_message = nil)
89
89
  unless status_code.nil?
90
- printable_code = status_code.split(':').last
90
+ if status_code.include? "|"
91
+ status_codes = status_code.split(' | ')
92
+ values = status_codes.collect do |status_code|
93
+ status_code.split(':').last
94
+ end
95
+ printable_code = values.join(" => ")
96
+ else
97
+ printable_code = status_code.split(':').last
98
+ end
91
99
  error_msg << ', was ' + printable_code
92
100
  end
93
101
 
@@ -131,23 +139,25 @@ module OneLogin
131
139
  def self.retrieve_symmetric_key(encrypt_data, private_key)
132
140
  encrypted_key = REXML::XPath.first(
133
141
  encrypt_data,
134
- "./ds:KeyInfo/xenc:EncryptedKey or \
135
- //xenc:EncryptedKey[@Id=$id]",
142
+ "./ds:KeyInfo/xenc:EncryptedKey | ./KeyInfo/xenc:EncryptedKey | //xenc:EncryptedKey[@Id=$id]",
136
143
  { "ds" => DSIG, "xenc" => XENC },
137
- { "id" => self.retrieve_symetric_key_reference(encrypt_data) }
144
+ { "id" => self.retrieve_symetric_key_reference(encrypt_data) }
138
145
  )
139
146
 
140
147
  encrypted_symmetric_key_element = REXML::XPath.first(
141
148
  encrypted_key,
142
149
  "./xenc:CipherData/xenc:CipherValue",
143
- { "ds" => DSIG, "xenc" => XENC }
150
+ "xenc" => XENC
144
151
  )
152
+
145
153
  cipher_text = Base64.decode64(encrypted_symmetric_key_element.text)
154
+
146
155
  encrypt_method = REXML::XPath.first(
147
156
  encrypted_key,
148
157
  "./xenc:EncryptionMethod",
149
- {"ds" => DSIG, "xenc" => XENC }
158
+ "xenc" => XENC
150
159
  )
160
+
151
161
  algorithm = encrypt_method.attributes['Algorithm']
152
162
  retrieve_plaintext(cipher_text, private_key, algorithm)
153
163
  end
@@ -1,5 +1,5 @@
1
1
  module OneLogin
2
2
  module RubySaml
3
- VERSION = '1.4.3'
3
+ VERSION = '1.5.0'
4
4
  end
5
5
  end
@@ -260,9 +260,11 @@ module XMLSecurity
260
260
  # check saml response cert matches provided idp cert
261
261
  if idp_cert.to_pem != cert.to_pem
262
262
  return false
263
+ end
264
+ else
265
+ base64_cert = Base64.encode64(idp_cert.to_pem)
263
266
  end
264
- validate_signature(base64_cert, true)
265
- end
267
+ validate_signature(base64_cert, true)
266
268
  end
267
269
 
268
270
  def validate_signature(base64_cert, soft = true)
@@ -202,10 +202,10 @@ class IdpMetadataParserTest < Minitest::Test
202
202
  assert_nil settings.idp_cert_fingerprint
203
203
  assert_nil settings.idp_cert
204
204
  assert_equal 2, settings.idp_cert_multi.size
205
- assert settings.idp_cert_multi.key?("signing")
206
- assert_equal 2, settings.idp_cert_multi["signing"].size
207
- assert settings.idp_cert_multi.key?("encryption")
208
- assert_equal 1, settings.idp_cert_multi["encryption"].size
205
+ assert settings.idp_cert_multi.key?(:signing)
206
+ assert_equal 2, settings.idp_cert_multi[:signing].size
207
+ assert settings.idp_cert_multi.key?(:encryption)
208
+ assert_equal 1, settings.idp_cert_multi[:encryption].size
209
209
  end
210
210
  end
211
211
 
@@ -332,4 +332,15 @@ class IdpMetadataParserTest < Minitest::Test
332
332
  assert_equal ["AuthToken", "SSOStartPage"], @settings.idp_attribute_names
333
333
  end
334
334
  end
335
+
336
+ describe "parsing metadata with no IDPSSODescriptor element" do
337
+ before do
338
+ @idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
339
+ @idp_metadata = no_idp_metadata_descriptor
340
+ end
341
+
342
+ it "raise due no IDPSSODescriptor element" do
343
+ assert_raises(ArgumentError) { @idp_metadata_parser.parse(@idp_metadata) }
344
+ end
345
+ end
335
346
  end
@@ -90,6 +90,7 @@ class RubySamlTest < Minitest::Test
90
90
  it "invalidate logout response when initiated with no idp cert or fingerprint" do
91
91
  settings.idp_cert_fingerprint = nil
92
92
  settings.idp_cert = nil
93
+ settings.idp_cert_multi = nil
93
94
  logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings)
94
95
  assert !logoutresponse.validate
95
96
  assert_includes logoutresponse.errors, "No fingerprint or certificate on settings of the logout response"
@@ -0,0 +1,21 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <md:EntityDescriptor entityID="https://sp.example.com/sp.xml" validUntil="2014-04-17T18:02:33.910Z" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
3
+ <md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:1.1:protocol urn:oasis:names:tc:SAML:2.0:protocol" AuthnRequestsSigned="true">
4
+ <md:KeyDescriptor use="signing">
5
+ <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
6
+ <ds:X509Data>
7
+ <ds:X509Certificate>LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURxekNDQXhTZ0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBRENCaGpFTE1Ba0dBMVVFQmhNQ1FWVXgKRERBS0JnTlZCQWdUQTA1VFZ6RVBNQTBHQTFVRUJ4TUdVM2xrYm1WNU1Rd3dDZ1lEVlFRS0RBTlFTVlF4Q1RBSApCZ05WQkFzTUFERVlNQllHQTFVRUF3d1BiR0YzY21WdVkyVndhWFF1WTI5dE1TVXdJd1lKS29aSWh2Y05BUWtCCkRCWnNZWGR5Wlc1alpTNXdhWFJBWjIxaGFXd3VZMjl0TUI0WERURXlNRFF4T1RJeU5UUXhPRm9YRFRNeU1EUXgKTkRJeU5UUXhPRm93Z1lZeEN6QUpCZ05WQkFZVEFrRlZNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVApCbE41Wkc1bGVURU1NQW9HQTFVRUNnd0RVRWxVTVFrd0J3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psCmJtTmxjR2wwTG1OdmJURWxNQ01HQ1NxR1NJYjNEUUVKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnYKYlRDQm56QU5CZ2txaGtpRzl3MEJBUUVGQUFPQmpRQXdnWWtDZ1lFQXFqaWUzUjJvaStwRGFldndJeXMvbWJVVApubkdsa3h0ZGlrcnExMXZleHd4SmlQTmhtaHFSVzNtVXVKRXpsbElkVkw2RW14R1lUcXBxZjkzSGxoa3NhZUowCjhVZ2pQOVVtTVlyaFZKdTFqY0ZXVjdmei9yKzIxL2F3VG5EVjlzTVlRcXVJUllZeTdiRzByMU9iaXdkb3ZudGsKN2dGSTA2WjB2WmFjREU1Ym9xVUNBd0VBQWFPQ0FTVXdnZ0VoTUFrR0ExVWRFd1FDTUFBd0N3WURWUjBQQkFRRApBZ1VnTUIwR0ExVWREZ1FXQkJTUk9OOEdKOG8rOGpnRnRqa3R3WmRxeDZCUnlUQVRCZ05WSFNVRUREQUtCZ2dyCkJnRUZCUWNEQVRBZEJnbGdoa2dCaHZoQ0FRMEVFQllPVkdWemRDQllOVEE1SUdObGNuUXdnYk1HQTFVZEl3U0IKcXpDQnFJQVVrVGpmQmlmS1B2STRCYlk1TGNHWGFzZWdVY21oZ1l5a2dZa3dnWVl4Q3pBSkJnTlZCQVlUQWtGVgpNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVEJsTjVaRzVsZVRFTU1Bb0dBMVVFQ2d3RFVFbFVNUWt3CkJ3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psYm1ObGNHbDBMbU52YlRFbE1DTUdDU3FHU0liM0RRRUoKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnZiWUlCQVRBTkJna3Foa2lHOXcwQkFRc0ZBQU9CZ1FDRQpUQWVKVERTQVc2ejFVRlRWN1FyZWg0VUxGT1JhajkrZUN1RjNLV0RIYyswSVFDajlyZG5ERzRRL3dmNy9yYVEwCkpuUFFDU0NkclBMSmV5b1BIN1FhVHdvYUY3ZHpWdzRMQ3N5TkpURld4NGNNNTBWdzZSNWZET2dpQzhic2ZmUzgKQkptb3VscnJaRE5OVmpHOG1XNmNMeHJZdlZRT3JSVmVjQ0ZJZ3NzQ2JBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=</ds:X509Certificate>
8
+ </ds:X509Data>
9
+ </ds:KeyInfo>
10
+ </md:KeyDescriptor>
11
+ <md:KeyDescriptor use="encryption">
12
+ <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
13
+ <ds:X509Data>
14
+ <ds:X509Certificate>LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURxekNDQXhTZ0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBRENCaGpFTE1Ba0dBMVVFQmhNQ1FWVXgKRERBS0JnTlZCQWdUQTA1VFZ6RVBNQTBHQTFVRUJ4TUdVM2xrYm1WNU1Rd3dDZ1lEVlFRS0RBTlFTVlF4Q1RBSApCZ05WQkFzTUFERVlNQllHQTFVRUF3d1BiR0YzY21WdVkyVndhWFF1WTI5dE1TVXdJd1lKS29aSWh2Y05BUWtCCkRCWnNZWGR5Wlc1alpTNXdhWFJBWjIxaGFXd3VZMjl0TUI0WERURXlNRFF4T1RJeU5UUXhPRm9YRFRNeU1EUXgKTkRJeU5UUXhPRm93Z1lZeEN6QUpCZ05WQkFZVEFrRlZNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVApCbE41Wkc1bGVURU1NQW9HQTFVRUNnd0RVRWxVTVFrd0J3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psCmJtTmxjR2wwTG1OdmJURWxNQ01HQ1NxR1NJYjNEUUVKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnYKYlRDQm56QU5CZ2txaGtpRzl3MEJBUUVGQUFPQmpRQXdnWWtDZ1lFQXFqaWUzUjJvaStwRGFldndJeXMvbWJVVApubkdsa3h0ZGlrcnExMXZleHd4SmlQTmhtaHFSVzNtVXVKRXpsbElkVkw2RW14R1lUcXBxZjkzSGxoa3NhZUowCjhVZ2pQOVVtTVlyaFZKdTFqY0ZXVjdmei9yKzIxL2F3VG5EVjlzTVlRcXVJUllZeTdiRzByMU9iaXdkb3ZudGsKN2dGSTA2WjB2WmFjREU1Ym9xVUNBd0VBQWFPQ0FTVXdnZ0VoTUFrR0ExVWRFd1FDTUFBd0N3WURWUjBQQkFRRApBZ1VnTUIwR0ExVWREZ1FXQkJTUk9OOEdKOG8rOGpnRnRqa3R3WmRxeDZCUnlUQVRCZ05WSFNVRUREQUtCZ2dyCkJnRUZCUWNEQVRBZEJnbGdoa2dCaHZoQ0FRMEVFQllPVkdWemRDQllOVEE1SUdObGNuUXdnYk1HQTFVZEl3U0IKcXpDQnFJQVVrVGpmQmlmS1B2STRCYlk1TGNHWGFzZWdVY21oZ1l5a2dZa3dnWVl4Q3pBSkJnTlZCQVlUQWtGVgpNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVEJsTjVaRzVsZVRFTU1Bb0dBMVVFQ2d3RFVFbFVNUWt3CkJ3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psYm1ObGNHbDBMbU52YlRFbE1DTUdDU3FHU0liM0RRRUoKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnZiWUlCQVRBTkJna3Foa2lHOXcwQkFRc0ZBQU9CZ1FDRQpUQWVKVERTQVc2ejFVRlRWN1FyZWg0VUxGT1JhajkrZUN1RjNLV0RIYyswSVFDajlyZG5ERzRRL3dmNy9yYVEwCkpuUFFDU0NkclBMSmV5b1BIN1FhVHdvYUY3ZHpWdzRMQ3N5TkpURld4NGNNNTBWdzZSNWZET2dpQzhic2ZmUzgKQkptb3VscnJaRE5OVmpHOG1XNmNMeHJZdlZRT3JSVmVjQ0ZJZ3NzQ2JBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=</ds:X509Certificate>
15
+ </ds:X509Data>
16
+ </ds:KeyInfo>
17
+ </md:KeyDescriptor>
18
+ <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://sp.example.com/saml2-logout.php/default-sp"/>
19
+ <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://sp.example.com/saml2-acs.php/default-sp" index="0"/>
20
+ </md:SPSSODescriptor>
21
+ </md:EntityDescriptor>
@@ -30,6 +30,7 @@ class RubySamlTest < Minitest::Test
30
30
  let(:response_no_statuscode) { OneLogin::RubySaml::Response.new(read_invalid_response("no_status_code.xml.base64")) }
31
31
  let(:response_statuscode_responder) { OneLogin::RubySaml::Response.new(read_invalid_response("status_code_responder.xml.base64")) }
32
32
  let(:response_statuscode_responder_and_msg) { OneLogin::RubySaml::Response.new(read_invalid_response("status_code_responer_and_msg.xml.base64")) }
33
+ let(:response_double_statuscode) { OneLogin::RubySaml::Response.new(response_document_double_status_code) }
33
34
  let(:response_encrypted_attrs) { OneLogin::RubySaml::Response.new(response_document_encrypted_attrs) }
34
35
  let(:response_no_signed_elements) { OneLogin::RubySaml::Response.new(read_invalid_response("no_signature.xml.base64")) }
35
36
  let(:response_multiple_signed) { OneLogin::RubySaml::Response.new(read_invalid_response("multiple_signed.xml.base64")) }
@@ -138,6 +139,7 @@ class RubySamlTest < Minitest::Test
138
139
  it "raise when No fingerprint or certificate on settings" do
139
140
  settings.idp_cert_fingerprint = nil
140
141
  settings.idp_cert = nil
142
+ settings.idp_cert_multi = nil
141
143
  response.settings = settings
142
144
  error_msg = "No fingerprint or certificate on settings"
143
145
  assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
@@ -516,12 +518,12 @@ class RubySamlTest < Minitest::Test
516
518
  assert_empty response.errors
517
519
  end
518
520
 
519
- it "return false when the status if no Status provided" do
521
+ it "return false when no Status provided" do
520
522
  assert !response_no_status.send(:validate_success_status)
521
523
  assert_includes response_no_status.errors, "The status code of the Response was not Success"
522
524
  end
523
525
 
524
- it "return false when the status if no StatusCode provided" do
526
+ it "return false when no StatusCode provided" do
525
527
  assert !response_no_statuscode.send(:validate_success_status)
526
528
  assert_includes response_no_statuscode.errors, "The status code of the Response was not Success"
527
529
  end
@@ -535,6 +537,11 @@ class RubySamlTest < Minitest::Test
535
537
  assert !response_statuscode_responder_and_msg.send(:validate_success_status)
536
538
  assert_includes response_statuscode_responder_and_msg.errors, "The status code of the Response was not Success, was Responder -> something_is_wrong"
537
539
  end
540
+
541
+ it "return false when the status is not 'Success'" do
542
+ assert !response_double_statuscode.send(:validate_success_status)
543
+ assert_includes response_double_statuscode.errors, "The status code of the Response was not Success, was Requester => UnsupportedBinding"
544
+ end
538
545
  end
539
546
 
540
547
  describe "#validate_structure" do
@@ -1271,6 +1278,11 @@ class RubySamlTest < Minitest::Test
1271
1278
 
1272
1279
  response4 = OneLogin::RubySaml::Response.new(unsigned_message_encrypted_unsigned_assertion, :settings => settings)
1273
1280
  assert response4.decrypted_document
1281
+
1282
+ assert OneLogin::RubySaml::Response.new(
1283
+ Base64.encode64(File.read('test/responses/unsigned_encrypted_adfs.xml')),
1284
+ :settings => settings
1285
+ ).decrypted_document
1274
1286
  end
1275
1287
  end
1276
1288
 
@@ -0,0 +1 @@
1
+ PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgSUQ9InBmeGJjODI2YWZkLWU5ZmUtZDNmYi1kODc0LWM0NzAwYzNlZjBjOCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDYtMDRUMDI6MjI6MDJaIiBEZXN0aW5hdGlvbj0iaHR0cDovL2FwcC5tdWRhLm5vL3Nzby9jb25zdW1lIiBJblJlc3BvbnNlVG89Il9mYzRhMzRiMC03ZWZiLTAxMmUtY2FhZS03ODJiY2IxM2JiMzgiPjxzYW1sOklzc3Vlcj5odHRwczovL2FwcC5vbmVsb2dpbi5jb20vc2FtbDI8L3NhbWw6SXNzdWVyPjxzYW1scDpTdGF0dXM+PHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6UmVxdWVzdGVyIj4gIDxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlVuc3VwcG9ydGVkQmluZGluZyIgLz48L3NhbWxwOlN0YXR1c0NvZGU+PC9zYW1scDpTdGF0dXM+PHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgVmVyc2lvbj0iMi4wIiBJRD0icGZ4OTUxNmIwZjMtNDUzNi0xMGY2LWM2ZmEtOWRkNTIzZTE0OThjIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDYtMDRUMDI6MjI6MDJaIj48c2FtbDpJc3N1ZXI+aHR0cHM6Ly9hcHAub25lbG9naW4uY29tL3NhbWwyPC9zYW1sOklzc3Vlcj48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+dGVzdEBvbmVsb2dpbi5jb208L3NhbWw6TmFtZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwOTAtMDYtMDRUMDI6Mjc6MDJaIiBSZWNpcGllbnQ9InJlY2lwaWVudCIvPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDpTdWJqZWN0PjxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDExLTA2LTA0VDAyOjE3OjAyWiIgTm90T25PckFmdGVyPSIyMDkwLTA2LTA0VDAyOjI3OjAyWiI+PHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDpBdWRpZW5jZT5odHRwczovL3NvbWVvbmUuZXhhbXBsZS5jb20vYXVkaWVuY2U8L3NhbWw6QXVkaWVuY2U+PC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE0LTA2LTA0VDAyOjIyOjAyWiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjA5MC0wNi0wNVQwMjoyMjowMloiIFNlc3Npb25JbmRleD0iXzE2ZjU3MGZiYzAzMTUwMDdhMDM1NWRmZWE2YjNjNDZjIj48c2FtbDpBdXRobkNvbnRleHQ+PHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmRQcm90ZWN0ZWRUcmFuc3BvcnQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+PC9zYW1sOkF1dGhuQ29udGV4dD48L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+PC9zYW1sOkFzc2VydGlvbj48L3NhbWxwOlJlc3BvbnNlPg==
@@ -0,0 +1,23 @@
1
+ <?xml version="1.0"?>
2
+ <samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_9365faa3a6f6a23b5cd75a74cb0786dc19e845200e" Version="2.0" IssueInstant="2015-03-19T14:00:31Z" Destination="http://rubysaml.com:3000/saml/acs" InResponseTo="_3ab7adb0-b06e-0132-5c3b-0090f5dedd77">
3
+ <saml:Issuer>https://idp.example.com/simplesaml/saml2/idp/metadata.php</saml:Issuer>
4
+ <samlp:Status>
5
+ <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
6
+ </samlp:Status>
7
+ <EncryptedAssertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
8
+ <xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Type="http://www.w3.org/2001/04/xmlenc#Element">
9
+ <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
10
+ <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
11
+ <e:EncryptedKey xmlns:e="http://www.w3.org/2001/04/xmlenc#">
12
+ <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"/>
13
+ <e:CipherData>
14
+ <e:CipherValue>xkJn7LQHrTZzLdrNec33hMSC5dc8B0uM4fw8HKhwpkWINwC9deaj0QVz4fZ82Zv2QdP3v6r0wJ/8VrUIF8puJTHSU0u2EUU+XqBJxvivGp2UhUPCyWmirSi6efBnWqMKhOz5YJoaLEu17yw+kOnavLZPv2bB8b5bo2mZPiaPG6s=</e:CipherValue>
15
+ </e:CipherData>
16
+ </e:EncryptedKey>
17
+ </KeyInfo>
18
+ <xenc:CipherData>
19
+ <xenc:CipherValue>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</xenc:CipherValue>
20
+ </xenc:CipherData>
21
+ </xenc:EncryptedData>
22
+ </EncryptedAssertion>
23
+ </samlp:Response>
@@ -136,6 +136,10 @@ class Minitest::Test
136
136
  @response_document_encrypted_attrs ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response_encrypted_attrs.xml.base64'))
137
137
  end
138
138
 
139
+ def response_document_double_status_code
140
+ @response_document_double_status_code ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response_double_status_code.xml.base64'))
141
+ end
142
+
139
143
  def signature_fingerprint_1
140
144
  @signature_fingerprint1 ||= "C5:19:85:D9:47:F1:BE:57:08:20:25:05:08:46:EB:27:F6:CA:B7:83"
141
145
  end
@@ -162,6 +166,10 @@ class Minitest::Test
162
166
  @idp_metadata_descriptor3 ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'idp_descriptor_3.xml'))
163
167
  end
164
168
 
169
+ def no_idp_metadata_descriptor
170
+ @no_idp_metadata_descriptor ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'no_idp_descriptor.xml'))
171
+ end
172
+
165
173
  def idp_metadata_multiple_descriptors
166
174
  @idp_metadata_multiple_descriptors ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'idp_multiple_descriptors.xml'))
167
175
  end
@@ -106,7 +106,7 @@ class XmlSecurityTest < Minitest::Test
106
106
  end
107
107
  end
108
108
 
109
- describe "#algorithm" do
109
+ describe "#algorithm" do
110
110
  it "SHA1" do
111
111
  alg = OpenSSL::Digest::SHA1
112
112
  assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2000/09/xmldsig#rsa-sha1")
@@ -130,7 +130,7 @@ class XmlSecurityTest < Minitest::Test
130
130
  alg = OpenSSL::Digest::SHA512
131
131
  assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512")
132
132
  assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2001/04/xmldsig-more#sha512")
133
- end
133
+ end
134
134
  end
135
135
 
136
136
  describe "Fingerprint Algorithms" do
@@ -278,7 +278,7 @@ class XmlSecurityTest < Minitest::Test
278
278
  logout_request2.sign_document(ruby_saml_key, ruby_saml_cert_text)
279
279
  # verify our signature
280
280
  signed_doc2 = XMLSecurity::SignedDocument.new(logout_request2.to_s)
281
- signed_doc2.validate_document(ruby_saml_cert_fingerprint, false)
281
+ signed_doc2.validate_document(ruby_saml_cert_fingerprint, false)
282
282
  assert signed_doc2.validate_document(ruby_saml_cert_fingerprint, false)
283
283
  end
284
284
 
@@ -293,7 +293,7 @@ class XmlSecurityTest < Minitest::Test
293
293
  logout_response2.sign_document(ruby_saml_key, ruby_saml_cert_text)
294
294
  # verify our signature
295
295
  signed_doc2 = XMLSecurity::SignedDocument.new(logout_response2.to_s)
296
- signed_doc2.validate_document(ruby_saml_cert_fingerprint, false)
296
+ signed_doc2.validate_document(ruby_saml_cert_fingerprint, false)
297
297
  assert signed_doc2.validate_document(ruby_saml_cert_fingerprint, false)
298
298
  end
299
299
  end
@@ -316,9 +316,14 @@ class XmlSecurityTest < Minitest::Test
316
316
  Timecop.freeze Time.parse('2012-11-20 17:55:00 UTC') do
317
317
  assert !response.is_valid?
318
318
 
319
- contains_expected_error = response.errors.include? "Current time is earlier than NotBefore condition 2012-11-20 17:55:00 UTC < 2012-11-28 17:53:45 UTC)"
320
- contains_expected_error ||= response.errors.include? "Current time is earlier than NotBefore condition Tue Nov 20 17:55:00 UTC 2012 < Wed Nov 28 17:53:45 UTC 2012)"
321
- assert contains_expected_error
319
+ time_1 = '2012-11-20 17:55:00 UTC < 2012-11-28 17:53:45 UTC'
320
+ time_2 = 'Tue Nov 20 17:55:00 UTC 2012 < Wed Nov 28 17:53:45 UTC 2012'
321
+
322
+ errors = [time_1, time_2].map do |time|
323
+ "Current time is earlier than NotBefore condition (#{time})"
324
+ end
325
+
326
+ assert_predicate response.errors & errors, :any?
322
327
  end
323
328
  end
324
329
 
@@ -344,7 +349,7 @@ class XmlSecurityTest < Minitest::Test
344
349
  assert document.validate_document(fingerprint, true), 'Document should be valid'
345
350
  end
346
351
  end
347
-
352
+
348
353
  describe 'when response has signed assertion' do
349
354
  let(:document_data) { read_response('response_with_signed_assertion_3.xml') }
350
355
  let(:document) { OneLogin::RubySaml::Response.new(document_data).document }
@@ -388,5 +393,29 @@ class XmlSecurityTest < Minitest::Test
388
393
  end
389
394
  end
390
395
  end
396
+
397
+ describe '#validate_document_with_cert' do
398
+ describe 'with valid document ' do
399
+ describe 'when response has cert' do
400
+ let(:document_data) { read_response('response_with_signed_message_and_assertion.xml') }
401
+ let(:document) { OneLogin::RubySaml::Response.new(document_data).document }
402
+ let(:idp_cert) { OpenSSL::X509::Certificate.new(ruby_saml_cert_text) }
403
+ let(:fingerprint) { '4b68c453c7d994aad9025c99d5efcf566287fe8d' }
404
+
405
+ it 'is valid' do
406
+ assert document.validate_document_with_cert(idp_cert), 'Document should be valid'
407
+ end
408
+ end
409
+
410
+ describe 'when response has no cert but you have local cert' do
411
+ let(:document) { OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate).document }
412
+ let(:idp_cert) { OpenSSL::X509::Certificate.new(ruby_saml_cert_text) }
413
+
414
+ it 'is valid' do
415
+ assert document.validate_document_with_cert(idp_cert), 'Document should be valid'
416
+ end
417
+ end
418
+ end
419
+ end
391
420
  end
392
421
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-saml
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.3
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OneLogin LLC
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-05-18 00:00:00.000000000 Z
11
+ date: 2017-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -218,6 +218,7 @@ files:
218
218
  - test/metadata/idp_descriptor_2.xml
219
219
  - test/metadata/idp_descriptor_3.xml
220
220
  - test/metadata/idp_multiple_descriptors.xml
221
+ - test/metadata/no_idp_descriptor.xml
221
222
  - test/metadata_test.rb
222
223
  - test/request_test.rb
223
224
  - test/response_test.rb
@@ -263,6 +264,7 @@ files:
263
264
  - test/responses/no_signature_ns.xml
264
265
  - test/responses/open_saml_response.xml
265
266
  - test/responses/response_assertion_wrapped.xml.base64
267
+ - test/responses/response_double_status_code.xml.base64
266
268
  - test/responses/response_encrypted_attrs.xml.base64
267
269
  - test/responses/response_encrypted_nameid.xml.base64
268
270
  - test/responses/response_eval.xml
@@ -290,6 +292,7 @@ files:
290
292
  - test/responses/simple_saml_php.xml
291
293
  - test/responses/starfield_response.xml.base64
292
294
  - test/responses/test_sign.xml
295
+ - test/responses/unsigned_encrypted_adfs.xml
293
296
  - test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64
294
297
  - test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64
295
298
  - test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64
@@ -327,7 +330,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
327
330
  version: '0'
328
331
  requirements: []
329
332
  rubyforge_project: http://www.rubygems.org/gems/ruby-saml
330
- rubygems_version: 2.4.8
333
+ rubygems_version: 2.5.1
331
334
  signing_key:
332
335
  specification_version: 4
333
336
  summary: SAML Ruby Tookit
@@ -363,6 +366,7 @@ test_files:
363
366
  - test/metadata/idp_descriptor_2.xml
364
367
  - test/metadata/idp_descriptor_3.xml
365
368
  - test/metadata/idp_multiple_descriptors.xml
369
+ - test/metadata/no_idp_descriptor.xml
366
370
  - test/metadata_test.rb
367
371
  - test/request_test.rb
368
372
  - test/response_test.rb
@@ -408,6 +412,7 @@ test_files:
408
412
  - test/responses/no_signature_ns.xml
409
413
  - test/responses/open_saml_response.xml
410
414
  - test/responses/response_assertion_wrapped.xml.base64
415
+ - test/responses/response_double_status_code.xml.base64
411
416
  - test/responses/response_encrypted_attrs.xml.base64
412
417
  - test/responses/response_encrypted_nameid.xml.base64
413
418
  - test/responses/response_eval.xml
@@ -435,6 +440,7 @@ test_files:
435
440
  - test/responses/simple_saml_php.xml
436
441
  - test/responses/starfield_response.xml.base64
437
442
  - test/responses/test_sign.xml
443
+ - test/responses/unsigned_encrypted_adfs.xml
438
444
  - test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64
439
445
  - test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64
440
446
  - test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64