ruby-saml 1.12.4 → 1.13.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.

Potentially problematic release.


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

data/UPGRADING.md ADDED
@@ -0,0 +1,149 @@
1
+ # Ruby SAML Migration Guide
2
+
3
+ ## Updating from 1.12.x to 1.13.0 (NOT YET RELEASED)
4
+
5
+ Version `1.13.0` adds `settings.idp_sso_service_binding` and `settings.idp_slo_service_binding`, and
6
+ deprecates `settings.security[:embed_sign]`. If specified, new binding parameters will be used in place of `:embed_sign`
7
+ to determine how to handle SAML message signing (`HTTP-POST` embeds signature and `HTTP-Redirect` does not.)
8
+
9
+ In addition, the `IdpMetadataParser#parse`, `#parse_to_hash` and `#parse_to_array` methods now retrieve
10
+ `idp_sso_service_binding` and `idp_slo_service_binding`.
11
+
12
+ Lastly, for convenience you may now use the Symbol aliases `:post` and `:redirect` for any `settings.*_binding` parameter.
13
+
14
+ ## Upgrading from 1.11.x to 1.12.0
15
+
16
+ Version `1.12.0` adds support for gcm algorithm and
17
+ change/adds specific error messages for signature validations
18
+
19
+ `idp_sso_target_url` and `idp_slo_target_url` attributes of the Settings class deprecated
20
+ in favor of `idp_sso_service_url` and `idp_slo_service_url`. The `IdpMetadataParser#parse`,
21
+ `#parse_to_hash` and `#parse_to_array` methods now retrieve SSO URL and SLO URL endpoints with
22
+ `idp_sso_service_url` and `idp_slo_service_url` (previously `idp_sso_target_url` and
23
+ `idp_slo_target_url` respectively).
24
+
25
+ ## Upgrading from 1.10.x to 1.11.0
26
+
27
+ Version `1.11.0` deprecates the use of `settings.issuer` in favour of `settings.sp_entity_id`.
28
+ There are two new security settings: `settings.security[:check_idp_cert_expiration]` and
29
+ `settings.security[:check_sp_cert_expiration]` (both false by default) that check if the
30
+ IdP or SP X.509 certificate has expired, respectively.
31
+
32
+ Version `1.10.2` includes the `valid_until` attribute in parsed IdP metadata.
33
+
34
+ Version `1.10.1` improves Ruby 1.8.7 support.
35
+
36
+ ## Upgrading from 1.9.0 to 1.10.0
37
+
38
+ Version `1.10.0` improves IdpMetadataParser to allow parse multiple IDPSSODescriptor,
39
+ Add Subject support on AuthNRequest to allow SPs provide info to the IdP about the user
40
+ to be authenticated and updates the format_cert method to accept certs with /\x0d/
41
+
42
+ ## Upgrading from 1.8.0 to 1.9.0
43
+
44
+ Version `1.9.0` better supports Ruby 2.4+ and JRuby 9.2.0.0. `Settings` initialization
45
+ now has a second parameter, `keep_security_settings` (default: false), which saves security
46
+ settings attributes that are not explicitly overridden, if set to true.
47
+
48
+ ## Upgrading from 1.7.x to 1.8.0
49
+
50
+ On Version `1.8.0`, creating AuthRequests/LogoutRequests/LogoutResponses with nil RelayState
51
+ param will not generate a URL with an empty RelayState parameter anymore. It also changes
52
+ the invalid audience error message.
53
+
54
+ ## Upgrading from 1.6.0 to 1.7.0
55
+
56
+ Version `1.7.0` is a recommended update for all Ruby SAML users as it includes a fix for
57
+ the [CVE-2017-11428](https://www.cvedetails.com/cve/CVE-2017-11428/) vulnerability.
58
+
59
+ ## Upgrading from 1.5.0 to 1.6.0
60
+
61
+ Version `1.6.0` changes the preferred way to construct instances of `Logoutresponse` and
62
+ `SloLogoutrequest`. Previously the _SAMLResponse_, _RelayState_, and _SigAlg_ parameters
63
+ of these message types were provided via the constructor's `options[:get_params]` parameter.
64
+ Unfortunately this can result in incompatibility with other SAML implementations; signatures
65
+ are specified to be computed based on the _sender's_ URI-encoding of the message, which can
66
+ differ from that of Ruby SAML. In particular, Ruby SAML's URI-encoding does not match that
67
+ of Microsoft ADFS, so messages from ADFS can fail signature validation.
68
+
69
+ The new preferred way to provide _SAMLResponse_, _RelayState_, and _SigAlg_ is via the
70
+ `options[:raw_get_params]` parameter. For example:
71
+
72
+ ```ruby
73
+ # In this example `query_params` is assumed to contain decoded query parameters,
74
+ # and `raw_query_params` is assumed to contain encoded query parameters as sent by the IDP.
75
+ settings = {
76
+ settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
77
+ settings.soft = false
78
+ }
79
+ options = {
80
+ get_params: {
81
+ "Signature" => query_params["Signature"],
82
+ },
83
+ raw_get_params: {
84
+ "SAMLRequest" => raw_query_params["SAMLRequest"],
85
+ "SigAlg" => raw_query_params["SigAlg"],
86
+ "RelayState" => raw_query_params["RelayState"],
87
+ },
88
+ }
89
+ slo_logout_request = OneLogin::RubySaml::SloLogoutrequest.new(query_params["SAMLRequest"], settings, options)
90
+ raise "Invalid Logout Request" unless slo_logout_request.is_valid?
91
+ ```
92
+
93
+ The old form is still supported for backward compatibility, but all Ruby SAML users
94
+ should prefer `options[:raw_get_params]` where possible to ensure compatibility with
95
+ other SAML implementations.
96
+
97
+ ## Upgrading from 1.4.2 to 1.4.3
98
+
99
+ Version `1.4.3` introduces Recipient validation of SubjectConfirmation elements.
100
+ The 'Recipient' value is compared with the settings.assertion_consumer_service_url
101
+ value.
102
+
103
+ If you want to skip that validation, add the :skip_recipient_check option to the
104
+ initialize method of the Response object.
105
+
106
+ Parsing metadata that contains more than one certificate will propagate the
107
+ idp_cert_multi property rather than idp_cert. See [signature validation
108
+ section](#signature-validation) for details.
109
+
110
+ ## Upgrading from 1.3.x to 1.4.x
111
+
112
+ Version `1.4.0` is a recommended update for all Ruby SAML users as it includes security improvements.
113
+
114
+ ## Upgrading from 1.2.x to 1.3.x
115
+
116
+ Version `1.3.0` is a recommended update for all Ruby SAML users as it includes security fixes.
117
+ It adds security improvements in order to prevent Signature wrapping attacks.
118
+ [CVE-2016-5697](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-5697)
119
+
120
+ ## Upgrading from 1.1.x to 1.2.x
121
+
122
+ Version `1.2` adds IDP metadata parsing improvements, uuid deprecation in favour of SecureRandom,
123
+ refactor error handling and some minor improvements.
124
+
125
+ There is no compatibility issue detected.
126
+
127
+ For more details, please review [CHANGELOG.md](CHANGELOG.md).
128
+
129
+ ## Upgrading from 1.0.x to 1.1.x
130
+
131
+ Version `1.1` adds some improvements on signature validation and solves some namespace conflicts.
132
+
133
+ ## Upgrading from 0.9.x to 1.0.x
134
+
135
+ Version `1.0` is a recommended update for all Ruby SAML users as it includes security fixes.
136
+
137
+ Version `1.0` adds security improvements like entity expansion limitation, more SAML message validations, and other important improvements like decrypt support.
138
+
139
+ ### Important Changes
140
+
141
+ Please note the `get_idp_metadata` method raises an exception when it is not able to fetch the idp metadata, so review your integration if you are using this functionality.
142
+
143
+ ## Upgrading from 0.8.x to 0.9.x
144
+
145
+ Version `0.9` adds many new features and improvements.
146
+
147
+ ## Upgrading from 0.7.x to 0.8.x
148
+
149
+ Version `0.8.x` changes the namespace of the gem from `OneLogin::Saml` to `OneLogin::RubySaml`. Please update your implementations of the gem accordingly.
@@ -73,7 +73,7 @@ module OneLogin
73
73
  base64_request = encode(request)
74
74
  request_params = {"SAMLRequest" => base64_request}
75
75
 
76
- if settings.security[:authn_requests_signed] && !settings.security[:embed_sign] && settings.private_key
76
+ if settings.idp_sso_service_binding == Utils::BINDINGS[:redirect] && settings.security[:authn_requests_signed] && settings.private_key
77
77
  params['SigAlg'] = settings.security[:signature_method]
78
78
  url_string = OneLogin::RubySaml::Utils.build_query(
79
79
  :type => 'SAMLRequest',
@@ -179,8 +179,7 @@ module OneLogin
179
179
  end
180
180
 
181
181
  def sign_document(document, settings)
182
- # embed signature
183
- if settings.security[:authn_requests_signed] && settings.private_key && settings.certificate && settings.security[:embed_sign]
182
+ if settings.idp_sso_service_binding == Utils::BINDINGS[:post] && settings.security[:authn_requests_signed] && settings.private_key && settings.certificate
184
183
  private_key = settings.get_sp_key
185
184
  cert = settings.get_sp_cert
186
185
  document.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
@@ -11,6 +11,10 @@ module OneLogin
11
11
 
12
12
  # Auxiliary class to retrieve and parse the Identity Provider Metadata
13
13
  #
14
+ # This class does not validate in any way the URL that is introduced,
15
+ # make sure to validate it properly before use it in a parse_remote method.
16
+ # Read the `Security warning` section of the README.md file to get more info
17
+ #
14
18
  class IdpMetadataParser
15
19
 
16
20
  module SamlMetadata
@@ -43,7 +47,7 @@ module OneLogin
43
47
  SamlMetadata::NAMESPACE
44
48
  )
45
49
  end
46
-
50
+
47
51
  # Parse the Identity Provider metadata and update the settings with the
48
52
  # IdP values
49
53
  #
@@ -52,9 +56,10 @@ module OneLogin
52
56
  #
53
57
  # @param options [Hash] options used for parsing the metadata and the returned Settings instance
54
58
  # @option options [OneLogin::RubySaml::Settings, Hash] :settings the OneLogin::RubySaml::Settings object which gets the parsed metadata merged into or an hash for Settings overrides.
55
- # @option options [Array<String>, nil] :sso_binding an ordered list of bindings to detect the single signon URL. The first binding in the list that is included in the metadata will be used.
56
- # @option options [Array<String>, nil] :slo_binding an ordered list of bindings to detect the single logout URL. The first binding in the list that is included in the metadata will be used.
57
- # @option options [String, nil] :entity_id when this is given, the entity descriptor for this ID is used. When ommitted, the first entity descriptor is used.
59
+ # @option options [String, nil] :entity_id when this is given, the entity descriptor for this ID is used. When omitted, the first entity descriptor is used.
60
+ # @option options [String, Array<String>, nil] :sso_binding an ordered list of bindings to detect the single signon URL. The first binding in the list that is included in the metadata will be used.
61
+ # @option options [String, Array<String>, nil] :slo_binding an ordered list of bindings to detect the single logout URL. The first binding in the list that is included in the metadata will be used.
62
+ # @option options [String, Array<String>, nil] :name_id_format an ordered list of NameIDFormats to detect a desired value. The first NameIDFormat in the list that is included in the metadata will be used.
58
63
  #
59
64
  # @return [OneLogin::RubySaml::Settings]
60
65
  #
@@ -70,9 +75,10 @@ module OneLogin
70
75
  # @param validate_cert [Boolean] If true and the URL is HTTPs, the cert of the domain is checked.
71
76
  #
72
77
  # @param options [Hash] options used for parsing the metadata
73
- # @option options [Array<String>, nil] :sso_binding an ordered list of bindings to detect the single signon URL. The first binding in the list that is included in the metadata will be used.
74
- # @option options [Array<String>, nil] :slo_binding an ordered list of bindings to detect the single logout URL. The first binding in the list that is included in the metadata will be used.
75
- # @option options [String, nil] :entity_id when this is given, the entity descriptor for this ID is used. When ommitted, the first entity descriptor is used.
78
+ # @option options [String, nil] :entity_id when this is given, the entity descriptor for this ID is used. When omitted, the first entity descriptor is used.
79
+ # @option options [String, Array<String>, nil] :sso_binding an ordered list of bindings to detect the single signon URL. The first binding in the list that is included in the metadata will be used.
80
+ # @option options [String, Array<String>, nil] :slo_binding an ordered list of bindings to detect the single logout URL. The first binding in the list that is included in the metadata will be used.
81
+ # @option options [String, Array<String>, nil] :name_id_format an ordered list of NameIDFormats to detect a desired value. The first NameIDFormat in the list that is included in the metadata will be used.
76
82
  #
77
83
  # @return [Hash]
78
84
  #
@@ -87,9 +93,10 @@ module OneLogin
87
93
  # @param validate_cert [Boolean] If true and the URL is HTTPs, the cert of the domain is checked.
88
94
  #
89
95
  # @param options [Hash] options used for parsing the metadata
90
- # @option options [Array<String>, nil] :sso_binding an ordered list of bindings to detect the single signon URL. The first binding in the list that is included in the metadata will be used.
91
- # @option options [Array<String>, nil] :slo_binding an ordered list of bindings to detect the single logout URL. The first binding in the list that is included in the metadata will be used.
92
- # @option options [String, nil] :entity_id when this is given, the entity descriptor for this ID is used. When ommitted, all found IdPs are returned.
96
+ # @option options [String, nil] :entity_id when this is given, the entity descriptor for this ID is used. When omitted, all found IdPs are returned.
97
+ # @option options [String, Array<String>, nil] :sso_binding an ordered list of bindings to detect the single signon URL. The first binding in the list that is included in the metadata will be used.
98
+ # @option options [String, Array<String>, nil] :slo_binding an ordered list of bindings to detect the single logout URL. The first binding in the list that is included in the metadata will be used.
99
+ # @option options [String, Array<String>, nil] :name_id_format an ordered list of NameIDFormats to detect a desired value. The first NameIDFormat in the list that is included in the metadata will be used.
93
100
  #
94
101
  # @return [Array<Hash>]
95
102
  #
@@ -105,9 +112,10 @@ module OneLogin
105
112
  #
106
113
  # @param options [Hash] :settings to provide the OneLogin::RubySaml::Settings object or an hash for Settings overrides
107
114
  # @option options [OneLogin::RubySaml::Settings, Hash] :settings the OneLogin::RubySaml::Settings object which gets the parsed metadata merged into or an hash for Settings overrides.
108
- # @option options [Array<String>, nil] :sso_binding an ordered list of bindings to detect the single signon URL. The first binding in the list that is included in the metadata will be used.
109
- # @option options [Array<String>, nil] :slo_binding an ordered list of bindings to detect the single logout URL. The first binding in the list that is included in the metadata will be used.
110
- # @option options [String, nil] :entity_id when this is given, the entity descriptor for this ID is used. When ommitted, the first entity descriptor is used.
115
+ # @option options [String, nil] :entity_id when this is given, the entity descriptor for this ID is used. When omitted, the first entity descriptor is used.
116
+ # @option options [String, Array<String>, nil] :sso_binding an ordered list of bindings to detect the single signon URL. The first binding in the list that is included in the metadata will be used.
117
+ # @option options [String, Array<String>, nil] :slo_binding an ordered list of bindings to detect the single logout URL. The first binding in the list that is included in the metadata will be used.
118
+ # @option options [String, Array<String>, nil] :name_id_format an ordered list of NameIDFormats to detect a desired value. The first NameIDFormat in the list that is included in the metadata will be used.
111
119
  #
112
120
  # @return [OneLogin::RubySaml::Settings]
113
121
  def parse(idp_metadata, options = {})
@@ -115,8 +123,10 @@ module OneLogin
115
123
 
116
124
  unless parsed_metadata[:cache_duration].nil?
117
125
  cache_valid_until_timestamp = OneLogin::RubySaml::Utils.parse_duration(parsed_metadata[:cache_duration])
118
- if parsed_metadata[:valid_until].nil? || cache_valid_until_timestamp < Time.parse(parsed_metadata[:valid_until], Time.now.utc).to_i
119
- parsed_metadata[:valid_until] = Time.at(cache_valid_until_timestamp).utc.strftime("%Y-%m-%dT%H:%M:%SZ")
126
+ unless cache_valid_until_timestamp.nil?
127
+ if parsed_metadata[:valid_until].nil? || cache_valid_until_timestamp < Time.parse(parsed_metadata[:valid_until], Time.now.utc).to_i
128
+ parsed_metadata[:valid_until] = Time.at(cache_valid_until_timestamp).utc.strftime("%Y-%m-%dT%H:%M:%SZ")
129
+ end
120
130
  end
121
131
  end
122
132
  # Remove the cache_duration because on the settings
@@ -139,9 +149,10 @@ module OneLogin
139
149
  # @param idp_metadata [String]
140
150
  #
141
151
  # @param options [Hash] options used for parsing the metadata and the returned Settings instance
142
- # @option options [Array<String>, nil] :sso_binding an ordered list of bindings to detect the single signon URL. The first binding in the list that is included in the metadata will be used.
143
- # @option options [Array<String>, nil] :slo_binding an ordered list of bindings to detect the single logout URL. The first binding in the list that is included in the metadata will be used.
144
- # @option options [String, nil] :entity_id when this is given, the entity descriptor for this ID is used. When ommitted, the first entity descriptor is used.
152
+ # @option options [String, nil] :entity_id when this is given, the entity descriptor for this ID is used. When omitted, the first entity descriptor is used.
153
+ # @option options [String, Array<String>, nil] :sso_binding an ordered list of bindings to detect the single signon URL. The first binding in the list that is included in the metadata will be used.
154
+ # @option options [String, Array<String>, nil] :slo_binding an ordered list of bindings to detect the single logout URL. The first binding in the list that is included in the metadata will be used.
155
+ # @option options [String, Array<String>, nil] :name_id_format an ordered list of NameIDFormats to detect a desired value. The first NameIDFormat in the list that is included in the metadata will be used.
145
156
  #
146
157
  # @return [Hash]
147
158
  def parse_to_hash(idp_metadata, options = {})
@@ -153,13 +164,14 @@ module OneLogin
153
164
  # @param idp_metadata [String]
154
165
  #
155
166
  # @param options [Hash] options used for parsing the metadata and the returned Settings instance
156
- # @option options [Array<String>, nil] :sso_binding an ordered list of bindings to detect the single signon URL. The first binding in the list that is included in the metadata will be used.
157
- # @option options [Array<String>, nil] :slo_binding an ordered list of bindings to detect the single logout URL. The first binding in the list that is included in the metadata will be used.
158
- # @option options [String, nil] :entity_id when this is given, the entity descriptor for this ID is used. When ommitted, all found IdPs are returned.
167
+ # @option options [String, nil] :entity_id when this is given, the entity descriptor for this ID is used. When omitted, all found IdPs are returned.
168
+ # @option options [String, Array<String>, nil] :sso_binding an ordered list of bindings to detect the single signon URL. The first binding in the list that is included in the metadata will be used.
169
+ # @option options [String, Array<String>, nil] :slo_binding an ordered list of bindings to detect the single logout URL. The first binding in the list that is included in the metadata will be used.
170
+ # @option options [String, Array<String>, nil] :name_id_format an ordered list of NameIDFormats to detect a desired value. The first NameIDFormat in the list that is included in the metadata will be used.
159
171
  #
160
172
  # @return [Array<Hash>]
161
173
  def parse_to_array(idp_metadata, options = {})
162
- parse_to_idp_metadata_array(idp_metadata, options).map{|idp_md| idp_md.to_hash(options)}
174
+ parse_to_idp_metadata_array(idp_metadata, options).map { |idp_md| idp_md.to_hash(options) }
163
175
  end
164
176
 
165
177
  def parse_to_idp_metadata_array(idp_metadata, options = {})
@@ -171,9 +183,9 @@ module OneLogin
171
183
  raise ArgumentError.new("idp_metadata must contain an IDPSSODescriptor element")
172
184
  end
173
185
 
174
- return idpsso_descriptors.map{|id| IdpMetadata.new(id, id.parent.attributes["entityID"])}
186
+ idpsso_descriptors.map {|id| IdpMetadata.new(id, id.parent.attributes["entityID"])}
175
187
  end
176
-
188
+
177
189
  private
178
190
 
179
191
  # Retrieve the remote IdP metadata from the URL or a cached copy.
@@ -210,19 +222,23 @@ module OneLogin
210
222
 
211
223
  class IdpMetadata
212
224
  attr_reader :idpsso_descriptor, :entity_id
213
-
225
+
214
226
  def initialize(idpsso_descriptor, entity_id)
215
227
  @idpsso_descriptor = idpsso_descriptor
216
228
  @entity_id = entity_id
217
229
  end
218
230
 
219
231
  def to_hash(options = {})
232
+ sso_binding = options[:sso_binding]
233
+ slo_binding = options[:slo_binding]
220
234
  {
221
235
  :idp_entity_id => @entity_id,
222
- :name_identifier_format => idp_name_id_format,
223
- :idp_sso_service_url => single_signon_service_url(options),
224
- :idp_slo_service_url => single_logout_service_url(options),
225
- :idp_slo_response_service_url => single_logout_response_service_url(options),
236
+ :name_identifier_format => idp_name_id_format(options[:name_id_format]),
237
+ :idp_sso_service_url => single_signon_service_url(sso_binding),
238
+ :idp_sso_service_binding => single_signon_service_binding(sso_binding),
239
+ :idp_slo_service_url => single_logout_service_url(slo_binding),
240
+ :idp_slo_service_binding => single_logout_service_binding(slo_binding),
241
+ :idp_slo_response_service_url => single_logout_response_service_url(slo_binding),
226
242
  :idp_attribute_names => attribute_names,
227
243
  :idp_cert => nil,
228
244
  :idp_cert_fingerprint => nil,
@@ -234,17 +250,6 @@ module OneLogin
234
250
  end
235
251
  end
236
252
 
237
- # @return [String|nil] IdP Name ID Format value if exists
238
- #
239
- def idp_name_id_format
240
- node = REXML::XPath.first(
241
- @idpsso_descriptor,
242
- "md:NameIDFormat",
243
- SamlMetadata::NAMESPACE
244
- )
245
- Utils.element_text(node)
246
- end
247
-
248
253
  # @return [String|nil] 'validUntil' attribute of metadata
249
254
  #
250
255
  def valid_until
@@ -259,39 +264,31 @@ module OneLogin
259
264
  root.attributes['cacheDuration'] if root && root.attributes
260
265
  end
261
266
 
262
- # @param binding_priority [Array]
263
- # @return [String|nil] SingleSignOnService binding if exists
267
+ # @param name_id_priority [String|Array<String>] The prioritized list of NameIDFormat values to select. Will select first value if nil.
268
+ # @return [String|nil] IdP NameIDFormat value if exists
264
269
  #
265
- def single_signon_service_binding(binding_priority = nil)
270
+ def idp_name_id_format(name_id_priority = nil)
266
271
  nodes = REXML::XPath.match(
267
272
  @idpsso_descriptor,
268
- "md:SingleSignOnService/@Binding",
273
+ "md:NameIDFormat",
269
274
  SamlMetadata::NAMESPACE
270
275
  )
271
- if binding_priority
272
- values = nodes.map(&:value)
273
- binding_priority.detect{ |binding| values.include? binding }
274
- else
275
- nodes.first.value if nodes.any?
276
- end
276
+ first_ranked_text(nodes, name_id_priority)
277
277
  end
278
278
 
279
- # @param options [Hash]
280
- # @return [String|nil] SingleSignOnService endpoint if exists
279
+ # @param binding_priority [String|Array<String>] The prioritized list of Binding values to select. Will select first value if nil.
280
+ # @return [String|nil] SingleSignOnService binding if exists
281
281
  #
282
- def single_signon_service_url(options = {})
283
- binding = single_signon_service_binding(options[:sso_binding])
284
- return if binding.nil?
285
-
286
- node = REXML::XPath.first(
282
+ def single_signon_service_binding(binding_priority = nil)
283
+ nodes = REXML::XPath.match(
287
284
  @idpsso_descriptor,
288
- "md:SingleSignOnService[@Binding=\"#{binding}\"]/@Location",
285
+ "md:SingleSignOnService/@Binding",
289
286
  SamlMetadata::NAMESPACE
290
287
  )
291
- return node.value if node
288
+ first_ranked_value(nodes, binding_priority)
292
289
  end
293
290
 
294
- # @param binding_priority [Array]
291
+ # @param binding_priority [String|Array<String>] The prioritized list of Binding values to select. Will select first value if nil.
295
292
  # @return [String|nil] SingleLogoutService binding if exists
296
293
  #
297
294
  def single_logout_service_binding(binding_priority = nil)
@@ -300,19 +297,29 @@ module OneLogin
300
297
  "md:SingleLogoutService/@Binding",
301
298
  SamlMetadata::NAMESPACE
302
299
  )
303
- if binding_priority
304
- values = nodes.map(&:value)
305
- binding_priority.detect{ |binding| values.include? binding }
306
- else
307
- nodes.first.value if nodes.any?
308
- end
300
+ first_ranked_value(nodes, binding_priority)
301
+ end
302
+
303
+ # @param binding_priority [String|Array<String>] The prioritized list of Binding values to select. Will select first value if nil.
304
+ # @return [String|nil] SingleSignOnService endpoint if exists
305
+ #
306
+ def single_signon_service_url(binding_priority = nil)
307
+ binding = single_signon_service_binding(binding_priority)
308
+ return if binding.nil?
309
+
310
+ node = REXML::XPath.first(
311
+ @idpsso_descriptor,
312
+ "md:SingleSignOnService[@Binding=\"#{binding}\"]/@Location",
313
+ SamlMetadata::NAMESPACE
314
+ )
315
+ node.value if node
309
316
  end
310
317
 
311
- # @param options [Hash]
318
+ # @param binding_priority [String|Array<String>] The prioritized list of Binding values to select. Will select first value if nil.
312
319
  # @return [String|nil] SingleLogoutService endpoint if exists
313
320
  #
314
- def single_logout_service_url(options = {})
315
- binding = single_logout_service_binding(options[:slo_binding])
321
+ def single_logout_service_url(binding_priority = nil)
322
+ binding = single_logout_service_binding(binding_priority)
316
323
  return if binding.nil?
317
324
 
318
325
  node = REXML::XPath.first(
@@ -320,14 +327,14 @@ module OneLogin
320
327
  "md:SingleLogoutService[@Binding=\"#{binding}\"]/@Location",
321
328
  SamlMetadata::NAMESPACE
322
329
  )
323
- return node.value if node
330
+ node.value if node
324
331
  end
325
332
 
326
- # @param options [Hash]
333
+ # @param binding_priority [String|Array<String>] The prioritized list of Binding values to select. Will select first value if nil.
327
334
  # @return [String|nil] SingleLogoutService response url if exists
328
335
  #
329
- def single_logout_response_service_url(options = {})
330
- binding = single_logout_service_binding(options[:slo_binding])
336
+ def single_logout_response_service_url(binding_priority = nil)
337
+ binding = single_logout_service_binding(binding_priority)
331
338
  return if binding.nil?
332
339
 
333
340
  node = REXML::XPath.first(
@@ -335,7 +342,7 @@ module OneLogin
335
342
  "md:SingleLogoutService[@Binding=\"#{binding}\"]/@ResponseLocation",
336
343
  SamlMetadata::NAMESPACE
337
344
  )
338
- return node.value if node
345
+ node.value if node
339
346
  end
340
347
 
341
348
  # @return [String|nil] Unformatted Certificate if exists
@@ -417,15 +424,41 @@ module OneLogin
417
424
  parsed_metadata[:idp_cert_fingerprint_algorithm]
418
425
  )
419
426
  end
420
- else
421
- # symbolize keys of certificates and pass it on
422
- parsed_metadata[:idp_cert_multi] = Hash[certificates.map { |k, v| [k.to_sym, v] }]
423
427
  end
428
+
429
+ # symbolize keys of certificates and pass it on
430
+ parsed_metadata[:idp_cert_multi] = Hash[certificates.map { |k, v| [k.to_sym, v] }]
424
431
  end
425
432
 
426
433
  def certificates_has_one(key)
427
434
  certificates.key?(key) && certificates[key].size == 1
428
435
  end
436
+
437
+ private
438
+
439
+ def first_ranked_text(nodes, priority = nil)
440
+ return unless nodes.any?
441
+
442
+ priority = Array(priority)
443
+ if priority.any?
444
+ values = nodes.map(&:text)
445
+ Array(priority).detect { |candidate| values.include?(candidate) }
446
+ else
447
+ nodes.first.text
448
+ end
449
+ end
450
+
451
+ def first_ranked_value(nodes, priority = nil)
452
+ return unless nodes.any?
453
+
454
+ priority = Array(priority)
455
+ if priority.any?
456
+ values = nodes.map(&:value)
457
+ priority.detect { |candidate| values.include?(candidate) }
458
+ else
459
+ nodes.first.value
460
+ end
461
+ end
429
462
  end
430
463
 
431
464
  def merge_parsed_metadata_into(settings, parsed_metadata)
@@ -70,8 +70,8 @@ module OneLogin
70
70
  base64_request = encode(request)
71
71
  request_params = {"SAMLRequest" => base64_request}
72
72
 
73
- if settings.security[:logout_requests_signed] && !settings.security[:embed_sign] && settings.private_key
74
- params['SigAlg'] = settings.security[:signature_method]
73
+ if settings.idp_slo_service_binding == Utils::BINDINGS[:redirect] && settings.security[:logout_requests_signed] && settings.private_key
74
+ params['SigAlg'] = settings.security[:signature_method]
75
75
  url_string = OneLogin::RubySaml::Utils.build_query(
76
76
  :type => 'SAMLRequest',
77
77
  :data => base64_request,
@@ -138,7 +138,7 @@ module OneLogin
138
138
 
139
139
  def sign_document(document, settings)
140
140
  # embed signature
141
- if settings.security[:logout_requests_signed] && settings.private_key && settings.certificate && settings.security[:embed_sign]
141
+ if settings.idp_slo_service_binding == Utils::BINDINGS[:post] && settings.security[:logout_requests_signed] && settings.private_key && settings.certificate
142
142
  private_key = settings.get_sp_key
143
143
  cert = settings.get_sp_cert
144
144
  document.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
@@ -43,7 +43,7 @@ module OneLogin
43
43
  end
44
44
 
45
45
  @options = options
46
- @response = decode_raw_saml(response)
46
+ @response = decode_raw_saml(response, settings)
47
47
  @document = XMLSecurity::SignedDocument.new(@response)
48
48
  end
49
49
 
@@ -150,8 +150,7 @@ module OneLogin
150
150
  # @raise [ValidationError] if soft == false and validation fails
151
151
  #
152
152
  def validate_structure
153
- check_malformed_doc = check_malformed_doc?(settings)
154
- unless valid_saml?(document, soft, check_malformed_doc)
153
+ unless valid_saml?(document, soft)
155
154
  return append_error("Invalid SAML Logout Response. Not match the saml-schema-protocol-2.0.xsd")
156
155
  end
157
156