ruby-saml 1.11.0 → 1.12.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.

Files changed (158) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +14 -12
  3. data/README.md +67 -19
  4. data/changelog.md +23 -0
  5. data/lib/onelogin/ruby-saml/attributes.rb +24 -1
  6. data/lib/onelogin/ruby-saml/authrequest.rb +9 -4
  7. data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +62 -24
  8. data/lib/onelogin/ruby-saml/logoutrequest.rb +7 -1
  9. data/lib/onelogin/ruby-saml/logoutresponse.rb +4 -0
  10. data/lib/onelogin/ruby-saml/metadata.rb +9 -1
  11. data/lib/onelogin/ruby-saml/response.rb +37 -15
  12. data/lib/onelogin/ruby-saml/saml_message.rb +6 -0
  13. data/lib/onelogin/ruby-saml/setting_error.rb +6 -0
  14. data/lib/onelogin/ruby-saml/settings.rb +34 -2
  15. data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +4 -0
  16. data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +27 -14
  17. data/lib/onelogin/ruby-saml/utils.rb +56 -0
  18. data/lib/onelogin/ruby-saml/version.rb +1 -1
  19. data/lib/xml_security.rb +34 -6
  20. data/ruby-saml.gemspec +8 -4
  21. metadata +22 -282
  22. data/test/certificates/certificate.der +0 -0
  23. data/test/certificates/certificate1 +0 -12
  24. data/test/certificates/certificate_without_head_foot +0 -1
  25. data/test/certificates/formatted_certificate +0 -14
  26. data/test/certificates/formatted_chained_certificate +0 -42
  27. data/test/certificates/formatted_private_key +0 -12
  28. data/test/certificates/formatted_rsa_private_key +0 -12
  29. data/test/certificates/invalid_certificate1 +0 -1
  30. data/test/certificates/invalid_certificate2 +0 -1
  31. data/test/certificates/invalid_certificate3 +0 -12
  32. data/test/certificates/invalid_chained_certificate1 +0 -1
  33. data/test/certificates/invalid_private_key1 +0 -1
  34. data/test/certificates/invalid_private_key2 +0 -1
  35. data/test/certificates/invalid_private_key3 +0 -10
  36. data/test/certificates/invalid_rsa_private_key1 +0 -1
  37. data/test/certificates/invalid_rsa_private_key2 +0 -1
  38. data/test/certificates/invalid_rsa_private_key3 +0 -10
  39. data/test/certificates/ruby-saml-2.crt +0 -15
  40. data/test/certificates/ruby-saml.crt +0 -14
  41. data/test/certificates/ruby-saml.key +0 -15
  42. data/test/idp_metadata_parser_test.rb +0 -594
  43. data/test/logging_test.rb +0 -62
  44. data/test/logout_requests/invalid_slo_request.xml +0 -6
  45. data/test/logout_requests/slo_request.xml +0 -4
  46. data/test/logout_requests/slo_request.xml.base64 +0 -1
  47. data/test/logout_requests/slo_request_deflated.xml.base64 +0 -1
  48. data/test/logout_requests/slo_request_with_name_id_format.xml +0 -4
  49. data/test/logout_requests/slo_request_with_session_index.xml +0 -5
  50. data/test/logout_responses/logoutresponse_fixtures.rb +0 -86
  51. data/test/logoutrequest_test.rb +0 -260
  52. data/test/logoutresponse_test.rb +0 -427
  53. data/test/metadata/idp_descriptor.xml +0 -26
  54. data/test/metadata/idp_descriptor_2.xml +0 -56
  55. data/test/metadata/idp_descriptor_3.xml +0 -14
  56. data/test/metadata/idp_descriptor_4.xml +0 -72
  57. data/test/metadata/idp_metadata_different_sign_and_encrypt_cert.xml +0 -72
  58. data/test/metadata/idp_metadata_multi_certs.xml +0 -75
  59. data/test/metadata/idp_metadata_multi_signing_certs.xml +0 -52
  60. data/test/metadata/idp_metadata_same_sign_and_encrypt_cert.xml +0 -71
  61. data/test/metadata/idp_multiple_descriptors.xml +0 -59
  62. data/test/metadata/idp_multiple_descriptors_2.xml +0 -59
  63. data/test/metadata/no_idp_descriptor.xml +0 -21
  64. data/test/metadata_test.rb +0 -331
  65. data/test/request_test.rb +0 -340
  66. data/test/response_test.rb +0 -1629
  67. data/test/responses/adfs_response_sha1.xml +0 -46
  68. data/test/responses/adfs_response_sha256.xml +0 -46
  69. data/test/responses/adfs_response_sha384.xml +0 -46
  70. data/test/responses/adfs_response_sha512.xml +0 -46
  71. data/test/responses/adfs_response_xmlns.xml +0 -45
  72. data/test/responses/attackxee.xml +0 -13
  73. data/test/responses/invalids/duplicated_attributes.xml.base64 +0 -1
  74. data/test/responses/invalids/empty_destination.xml.base64 +0 -1
  75. data/test/responses/invalids/empty_nameid.xml.base64 +0 -1
  76. data/test/responses/invalids/encrypted_new_attack.xml.base64 +0 -1
  77. data/test/responses/invalids/invalid_audience.xml.base64 +0 -1
  78. data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +0 -1
  79. data/test/responses/invalids/invalid_issuer_message.xml.base64 +0 -1
  80. data/test/responses/invalids/invalid_signature_position.xml.base64 +0 -1
  81. data/test/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 +0 -1
  82. data/test/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 +0 -1
  83. data/test/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 +0 -1
  84. data/test/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 +0 -1
  85. data/test/responses/invalids/multiple_assertions.xml.base64 +0 -2
  86. data/test/responses/invalids/multiple_signed.xml.base64 +0 -1
  87. data/test/responses/invalids/no_authnstatement.xml.base64 +0 -1
  88. data/test/responses/invalids/no_conditions.xml.base64 +0 -1
  89. data/test/responses/invalids/no_id.xml.base64 +0 -1
  90. data/test/responses/invalids/no_issuer_assertion.xml.base64 +0 -1
  91. data/test/responses/invalids/no_issuer_response.xml.base64 +0 -1
  92. data/test/responses/invalids/no_nameid.xml.base64 +0 -1
  93. data/test/responses/invalids/no_saml2.xml.base64 +0 -1
  94. data/test/responses/invalids/no_signature.xml.base64 +0 -1
  95. data/test/responses/invalids/no_status.xml.base64 +0 -1
  96. data/test/responses/invalids/no_status_code.xml.base64 +0 -1
  97. data/test/responses/invalids/no_subjectconfirmation_data.xml.base64 +0 -1
  98. data/test/responses/invalids/no_subjectconfirmation_method.xml.base64 +0 -1
  99. data/test/responses/invalids/response_invalid_signed_element.xml.base64 +0 -1
  100. data/test/responses/invalids/response_with_concealed_signed_assertion.xml +0 -51
  101. data/test/responses/invalids/response_with_doubled_signed_assertion.xml +0 -49
  102. data/test/responses/invalids/signature_wrapping_attack.xml.base64 +0 -1
  103. data/test/responses/invalids/status_code_responder.xml.base64 +0 -1
  104. data/test/responses/invalids/status_code_responer_and_msg.xml.base64 +0 -1
  105. data/test/responses/invalids/wrong_spnamequalifier.xml.base64 +0 -1
  106. data/test/responses/no_signature_ns.xml +0 -48
  107. data/test/responses/open_saml_response.xml +0 -56
  108. data/test/responses/response_assertion_wrapped.xml.base64 +0 -93
  109. data/test/responses/response_audience_self_closed_tag.xml.base64 +0 -1
  110. data/test/responses/response_double_status_code.xml.base64 +0 -1
  111. data/test/responses/response_encrypted_attrs.xml.base64 +0 -1
  112. data/test/responses/response_encrypted_nameid.xml.base64 +0 -1
  113. data/test/responses/response_eval.xml +0 -7
  114. data/test/responses/response_no_cert_and_encrypted_attrs.xml +0 -29
  115. data/test/responses/response_node_text_attack.xml.base64 +0 -1
  116. data/test/responses/response_node_text_attack2.xml.base64 +0 -1
  117. data/test/responses/response_node_text_attack3.xml.base64 +0 -1
  118. data/test/responses/response_unsigned_xml_base64 +0 -1
  119. data/test/responses/response_with_ampersands.xml +0 -139
  120. data/test/responses/response_with_ampersands.xml.base64 +0 -93
  121. data/test/responses/response_with_ds_namespace_at_the_root.xml.base64 +0 -1
  122. data/test/responses/response_with_multiple_attribute_statements.xml +0 -72
  123. data/test/responses/response_with_multiple_attribute_values.xml +0 -67
  124. data/test/responses/response_with_retrieval_method.xml +0 -26
  125. data/test/responses/response_with_saml2_namespace.xml.base64 +0 -102
  126. data/test/responses/response_with_signed_assertion.xml.base64 +0 -66
  127. data/test/responses/response_with_signed_assertion_2.xml.base64 +0 -1
  128. data/test/responses/response_with_signed_assertion_3.xml +0 -30
  129. data/test/responses/response_with_signed_message_and_assertion.xml +0 -34
  130. data/test/responses/response_with_undefined_recipient.xml.base64 +0 -1
  131. data/test/responses/response_without_attributes.xml.base64 +0 -79
  132. data/test/responses/response_without_reference_uri.xml.base64 +0 -1
  133. data/test/responses/response_wrapped.xml.base64 +0 -150
  134. data/test/responses/signed_message_encrypted_signed_assertion.xml.base64 +0 -1
  135. data/test/responses/signed_message_encrypted_unsigned_assertion.xml.base64 +0 -1
  136. data/test/responses/signed_nameid_in_atts.xml +0 -47
  137. data/test/responses/signed_unqual_nameid_in_atts.xml +0 -47
  138. data/test/responses/simple_saml_php.xml +0 -71
  139. data/test/responses/starfield_response.xml.base64 +0 -1
  140. data/test/responses/test_sign.xml +0 -43
  141. data/test/responses/unsigned_encrypted_adfs.xml +0 -23
  142. data/test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64 +0 -1
  143. data/test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64 +0 -1
  144. data/test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64 +0 -1
  145. data/test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64 +0 -1
  146. data/test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64 +0 -1
  147. data/test/responses/unsigned_message_encrypted_signed_assertion.xml.base64 +0 -1
  148. data/test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64 +0 -1
  149. data/test/responses/valid_response.xml.base64 +0 -1
  150. data/test/responses/valid_response_with_formatted_x509certificate.xml.base64 +0 -1
  151. data/test/responses/valid_response_without_x509certificate.xml.base64 +0 -1
  152. data/test/saml_message_test.rb +0 -56
  153. data/test/settings_test.rb +0 -338
  154. data/test/slo_logoutrequest_test.rb +0 -467
  155. data/test/slo_logoutresponse_test.rb +0 -233
  156. data/test/test_helper.rb +0 -333
  157. data/test/utils_test.rb +0 -259
  158. data/test/xml_security_test.rb +0 -421
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: afcd8a95f66ec94e3bb68b5a5264a651ae623923
4
- data.tar.gz: eb05bc60959fde11ac20b0af0d14c07b7c5c1b38
2
+ SHA256:
3
+ metadata.gz: 43bc92cf8a14835577d9bb32d1bdcef71fd5ffccb351dd41ac9b56863fb173c7
4
+ data.tar.gz: e7975fcf413d9c64801f7b5190246685548205034dc74315bc169738697e1006
5
5
  SHA512:
6
- metadata.gz: e85dc90f8f4bd5433f0078a6b5e316109dc7bbbe1ba60d5f8075d1f5047a71c208144d1da51157cf7220f2c56fb8b421e7b70ac5c828bea713146fc6bc774a29
7
- data.tar.gz: 7cce3fd10ff5d7753d518b847b7f0ff0976b4ef5a1b7cc39c6e6cd3c8b32df7649e95ab929631b9838df5a4fb3eb3f3c50479f54253094b457a484710381b8f1
6
+ metadata.gz: 0a09fcb8777969eb6d54b29d20520c7e17e3f7dc128cfc81475eba8c9b31f5926f2b64d308b18268762b43fb60cf7956f6e266c1f5343d2b9d58e545be0c3392
7
+ data.tar.gz: 8e9008647a610935764b2f578b76c5eb72d09be482d76b5f4d8ab51fd29d4569a3ba2e2db61f63fbdde27bd1be14bf4afdeffe1c7f699afc0b7d5e1c85d0fe09
data/.travis.yml CHANGED
@@ -1,18 +1,18 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.8.7
4
3
  - 1.9.3
5
4
  - 2.0.0
6
5
  - 2.1.10
7
6
  - 2.2.10
8
7
  - 2.3.8
9
8
  - 2.4.6
10
- - 2.5.5
11
- - 2.6.3
12
- - ree
9
+ - 2.5.8
10
+ - 2.6.6
11
+ - 2.7.2
12
+ - 3.0.0
13
13
  - jruby-1.7.27
14
14
  - jruby-9.1.17.0
15
- - jruby-9.2.7.0
15
+ - jruby-9.2.13.0
16
16
  gemfile:
17
17
  - Gemfile
18
18
  - gemfiles/nokogiri-1.5.gemfile
@@ -20,27 +20,29 @@ before_install:
20
20
  - gem update bundler
21
21
  matrix:
22
22
  exclude:
23
- - rvm: 1.8.7
24
- gemfile: Gemfile
25
- - rvm: ree
26
- gemfile: Gemfile
27
23
  - rvm: jruby-1.7.27
28
24
  gemfile: gemfiles/nokogiri-1.5.gemfile
29
25
  - rvm: jruby-9.1.17.0
30
26
  gemfile: gemfiles/nokogiri-1.5.gemfile
31
- - rvm: jruby-9.2.7.0
27
+ - rvm: jruby-9.2.13.0
32
28
  gemfile: gemfiles/nokogiri-1.5.gemfile
33
29
  - rvm: 2.1.5
34
30
  gemfile: gemfiles/nokogiri-1.5.gemfile
31
+ - rvm: 2.1.10
32
+ gemfile: gemfiles/nokogiri-1.5.gemfile
35
33
  - rvm: 2.2.10
36
34
  gemfile: gemfiles/nokogiri-1.5.gemfile
37
35
  - rvm: 2.3.8
38
36
  gemfile: gemfiles/nokogiri-1.5.gemfile
39
37
  - rvm: 2.4.6
40
38
  gemfile: gemfiles/nokogiri-1.5.gemfile
41
- - rvm: 2.5.5
39
+ - rvm: 2.5.8
40
+ gemfile: gemfiles/nokogiri-1.5.gemfile
41
+ - rvm: 2.6.6
42
+ gemfile: gemfiles/nokogiri-1.5.gemfile
43
+ - rvm: 2.7.2
42
44
  gemfile: gemfiles/nokogiri-1.5.gemfile
43
- - rvm: 2.6.3
45
+ - rvm: 3.0.0
44
46
  gemfile: gemfiles/nokogiri-1.5.gemfile
45
47
  env:
46
48
  - JRUBY_OPTS="--debug"
data/README.md CHANGED
@@ -1,6 +1,18 @@
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)
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)](https://coveralls.io/r/onelogin/ruby-saml?branch=master)
2
2
 
3
- # Updating from 1.9.0 to 1.10.0
3
+ ## Updating from 1.11.x to 1.12.0
4
+ Version `1.12.0` adds support for gcm algorithm and
5
+ change/adds specific error messages for signature validations
6
+
7
+ ## Updating from 1.10.x to 1.11.0
8
+ Version `1.11.0` deprecates the use of `settings.issuer` in favour of `settings.sp_entity_id`.
9
+ There are two new security settings: `settings.security[:check_idp_cert_expiration]` and `settings.security[:check_sp_cert_expiration]` (both false by default) that check if the IdP or SP X.509 certificate has expired, respectively.
10
+
11
+ Version `1.10.2` includes the `valid_until` attribute in parsed IdP metadata.
12
+
13
+ Version `1.10.1` improves Ruby 1.8.7 support.
14
+
15
+ ## Updating from 1.9.0 to 1.10.0
4
16
  Version `1.10.0` improves IdpMetadataParser to allow parse multiple IDPSSODescriptor, Add Subject support on AuthNRequest to allow SPs provide info to the IdP about the user to be authenticated and updates the format_cert method to accept certs with /\x0d/
5
17
 
6
18
  ## Updating from 1.8.0 to 1.9.0
@@ -107,6 +119,7 @@ We created a demo project for Rails4 that uses the latest version of this librar
107
119
  * 2.4.x
108
120
  * 2.5.x
109
121
  * 2.6.x
122
+ * 2.7.x
110
123
  * JRuby 1.7.19
111
124
  * JRuby 9.0.0.0
112
125
  * JRuby 9.2.0.0
@@ -141,7 +154,7 @@ Using `Gemfile`
141
154
 
142
155
  ```ruby
143
156
  # latest stable
144
- gem 'ruby-saml', '~> 1.9.0'
157
+ gem 'ruby-saml', '~> 1.11.0'
145
158
 
146
159
  # or track master for bleeding-edge
147
160
  gem 'ruby-saml', :github => 'onelogin/ruby-saml'
@@ -228,6 +241,7 @@ def consume
228
241
  session[:attributes] = response.attributes
229
242
  else
230
243
  authorize_failure # This method shows an error message
244
+ # List of errors is available in response.errors array
231
245
  end
232
246
  end
233
247
  ```
@@ -251,8 +265,8 @@ def saml_settings
251
265
  settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
252
266
  settings.sp_entity_id = "http://#{request.host}/saml/metadata"
253
267
  settings.idp_entity_id = "https://app.onelogin.com/saml/metadata/#{OneLoginAppId}"
254
- settings.idp_sso_target_url = "https://app.onelogin.com/trust/saml2/http-post/sso/#{OneLoginAppId}"
255
- settings.idp_slo_target_url = "https://app.onelogin.com/trust/saml2/http-redirect/slo/#{OneLoginAppId}"
268
+ settings.idp_sso_service_url = "https://app.onelogin.com/trust/saml2/http-post/sso/#{OneLoginAppId}"
269
+ settings.idp_slo_service_url = "https://app.onelogin.com/trust/saml2/http-redirect/slo/#{OneLoginAppId}"
256
270
  settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
257
271
  settings.idp_cert_fingerprint_algorithm = "http://www.w3.org/2000/09/xmldsig#sha1"
258
272
  settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
@@ -273,7 +287,7 @@ def saml_settings
273
287
  end
274
288
  ```
275
289
 
276
- The use of settings.issuer is deprecated in favour of settings.sp_entity_id
290
+ The use of settings.issuer is deprecated in favour of settings.sp_entity_id since version 1.11.0
277
291
 
278
292
  Some assertion validations can be skipped by passing parameters to `OneLogin::RubySaml::Response.new()`. For example, you can skip the `AuthnStatement`, `Conditions`, `Recipient`, or the `SubjectConfirmation` validations by initializing the response with different options:
279
293
 
@@ -282,6 +296,7 @@ response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_authnst
282
296
  response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_conditions: true}) # skips conditions
283
297
  response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_subject_confirmation: true}) # skips subject confirmation
284
298
  response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_recipient_check: true}) # doens't skip subject confirmation, but skips the recipient check which is a sub check of the subject_confirmation check
299
+ response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_audience: true}) # skips audience check
285
300
  ```
286
301
 
287
302
  All that's left is to wrap everything in a controller and reference it in the initialization and consumption URLs in OneLogin. A full controller example could look like this:
@@ -305,6 +320,7 @@ class SamlController < ApplicationController
305
320
  session[:attributes] = response.attributes
306
321
  else
307
322
  authorize_failure # This method shows an error message
323
+ # List of errors is available in response.errors array
308
324
  end
309
325
  end
310
326
 
@@ -315,7 +331,7 @@ class SamlController < ApplicationController
315
331
 
316
332
  settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
317
333
  settings.sp_entity_id = "http://#{request.host}/saml/metadata"
318
- settings.idp_sso_target_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}"
334
+ settings.idp_sso_service_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}"
319
335
  settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
320
336
  settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
321
337
 
@@ -388,8 +404,8 @@ end
388
404
  The following attributes are set:
389
405
  * idp_entity_id
390
406
  * name_identifier_format
391
- * idp_sso_target_url
392
- * idp_slo_target_url
407
+ * idp_sso_service_url
408
+ * idp_slo_service_url
393
409
  * idp_attribute_names
394
410
  * idp_cert
395
411
  * idp_cert_fingerprint
@@ -455,6 +471,9 @@ Imagine this `saml:AttributeStatement`
455
471
  <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
456
472
  <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="1"/>
457
473
  </saml:Attribute>
474
+ <saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname">
475
+ <saml:AttributeValue>usersName</saml:AttributeValue>
476
+ </saml:Attribute>
458
477
  </saml:AttributeStatement>
459
478
  ```
460
479
 
@@ -465,7 +484,8 @@ pp(response.attributes) # is an OneLogin::RubySaml::Attributes object
465
484
  "another_value"=>["value1", "value2"],
466
485
  "role"=>["role1", "role2", "role3"],
467
486
  "attribute_with_nil_value"=>[nil],
468
- "attribute_with_nils_and_empty_strings"=>["", "valuePresent", nil, nil]}>
487
+ "attribute_with_nils_and_empty_strings"=>["", "valuePresent", nil, nil]
488
+ "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"=>["usersName"]}>
469
489
 
470
490
  # Active single_value_compatibility
471
491
  OneLogin::RubySaml::Attributes.single_value_compatibility = true
@@ -482,6 +502,9 @@ pp(response.attributes.single(:role))
482
502
  pp(response.attributes.multi(:role))
483
503
  # => ["role1", "role2", "role3"]
484
504
 
505
+ pp(response.attributes.fetch(:role))
506
+ # => "role1"
507
+
485
508
  pp(response.attributes[:attribute_with_nil_value])
486
509
  # => nil
487
510
 
@@ -497,6 +520,9 @@ pp(response.attributes.single(:not_exists))
497
520
  pp(response.attributes.multi(:not_exists))
498
521
  # => nil
499
522
 
523
+ pp(response.attributes.fetch(/givenname/))
524
+ # => "usersName"
525
+
500
526
  # Deactive single_value_compatibility
501
527
  OneLogin::RubySaml::Attributes.single_value_compatibility = false
502
528
 
@@ -512,6 +538,9 @@ pp(response.attributes.single(:role))
512
538
  pp(response.attributes.multi(:role))
513
539
  # => ["role1", "role2", "role3"]
514
540
 
541
+ pp(response.attributes.fetch(:role))
542
+ # => ["role1", "role2", "role3"]
543
+
515
544
  pp(response.attributes[:attribute_with_nil_value])
516
545
  # => [nil]
517
546
 
@@ -526,12 +555,15 @@ pp(response.attributes.single(:not_exists))
526
555
 
527
556
  pp(response.attributes.multi(:not_exists))
528
557
  # => nil
558
+
559
+ pp(response.attributes.fetch(/givenname/))
560
+ # => ["usersName"]
529
561
  ```
530
562
 
531
563
  The `saml:AuthnContextClassRef` of the AuthNRequest can be provided by `settings.authn_context`; possible values are described at [SAMLAuthnCxt]. The comparison method can be set using `settings.authn_context_comparison` parameter. Possible values include: 'exact', 'better', 'maximum' and 'minimum' (default value is 'exact').
532
564
  To add a `saml:AuthnContextDeclRef`, define `settings.authn_context_decl_ref`.
533
565
 
534
- In a SP-initiaited flow, the SP can indicate to the IdP the subject that should be authenticated. This is done by defining the `settings.name_identifier_value_requested` before
566
+ In a SP-initiated flow, the SP can indicate to the IdP the subject that should be authenticated. This is done by defining the `settings.name_identifier_value_requested` before
535
567
  building the authrequest object.
536
568
 
537
569
 
@@ -570,7 +602,7 @@ Remember to provide it to the Signature builder if you are sending a `GET RelayS
570
602
  signature validation process will fail at the Identity Provider.
571
603
 
572
604
  The Service Provider will sign the request/responses with its private key.
573
- The Identity Provider will validate the sign of the received request/responses with the public x500 cert of the
605
+ The Identity Provider will validate the sign of the received request/responses with the public x509 cert of the
574
606
  Service Provider.
575
607
 
576
608
  Notice that this toolkit uses 'settings.certificate' and 'settings.private_key' for the sign and decrypt processes.
@@ -611,21 +643,27 @@ def sp_logout_request
611
643
  # LogoutRequest accepts plain browser requests w/o paramters
612
644
  settings = saml_settings
613
645
 
614
- if settings.idp_slo_target_url.nil?
646
+ if settings.idp_slo_service_url.nil?
615
647
  logger.info "SLO IdP Endpoint not found in settings, executing then a normal logout'"
616
648
  delete_session
617
649
  else
618
650
 
619
- # Since we created a new SAML request, save the transaction_id
620
- # to compare it with the response we get back
621
651
  logout_request = OneLogin::RubySaml::Logoutrequest.new()
622
- session[:transaction_id] = logout_request.uuid
623
- logger.info "New SP SLO for userid '#{session[:userid]}' transactionid '#{session[:transaction_id]}'"
652
+ logger.info "New SP SLO for userid '#{session[:userid]}' transactionid '#{logout_request.uuid}'"
624
653
 
625
654
  if settings.name_identifier_value.nil?
626
655
  settings.name_identifier_value = session[:userid]
627
656
  end
628
657
 
658
+ # Ensure user is logged out before redirect to IdP, in case anything goes wrong during single logout process (as recommended by saml2int [SDP-SP34])
659
+ logged_user = session[:userid]
660
+ logger.info "Delete session for '#{session[:userid]}'"
661
+ delete_session
662
+
663
+ # Save the transaction_id to compare it with the response we get back
664
+ session[:transaction_id] = logout_request.uuid
665
+ session[:logged_out_user] = logged_user
666
+
629
667
  relayState = url_for controller: 'saml', action: 'index'
630
668
  redirect_to(logout_request.create(settings, :RelayState => relayState))
631
669
  end
@@ -653,7 +691,7 @@ def process_logout_response
653
691
  logger.error "The SAML Logout Response is invalid"
654
692
  else
655
693
  # Actually log out this session
656
- logger.info "Delete session for '#{session[:userid]}'"
694
+ logger.info "SLO completed for '#{session[:logged_out_user]}'"
657
695
  delete_session
658
696
  end
659
697
  end
@@ -662,6 +700,8 @@ end
662
700
  def delete_session
663
701
  session[:userid] = nil
664
702
  session[:attributes] = nil
703
+ session[:transaction_id] = nil
704
+ session[:logged_out_user] = nil
665
705
  end
666
706
  ```
667
707
 
@@ -674,7 +714,7 @@ def idp_logout_request
674
714
  logout_request = OneLogin::RubySaml::SloLogoutrequest.new(params[:SAMLRequest])
675
715
  if !logout_request.is_valid?
676
716
  logger.error "IdP initiated LogoutRequest was not valid!"
677
- render :inline => logger.error
717
+ return render :inline => logger.error
678
718
  end
679
719
  logger.info "IdP initiated Logout for #{logout_request.name_id}"
680
720
 
@@ -729,6 +769,14 @@ class SamlController < ApplicationController
729
769
  end
730
770
  ```
731
771
 
772
+ You can add ValidUntil and CacheDuration to the XML Metadata using instead
773
+ ```ruby
774
+ # Valid until => 2 days from now
775
+ # Cache duration = 604800s = 1 week
776
+ valid_until = Time.now + 172800
777
+ cache_duration = 604800
778
+ meta.generate(settings, false, valid_until, cache_duration)
779
+ ```
732
780
 
733
781
  ## Clock Drift
734
782
 
data/changelog.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # RubySaml Changelog
2
2
 
3
+ ### 1.12.0 (Feb 18, 2021)
4
+ * Support AES-128-GCM, AES-192-GCM, and AES-256-GCM encryptions
5
+ * Parse & return SLO ResponseLocation in IDPMetadataParser & Settings
6
+ * Adding idp_sso_service_url and idp_slo_service_url settings
7
+ * [#536](https://github.com/onelogin/ruby-saml/pull/536) Adding feth method to be able retrieve attributes based on regex
8
+ * Reduce size of built gem by excluding the test folder
9
+ * Improve protection on Zlib deflate decompression bomb attack.
10
+ * Add ValidUntil and cacheDuration support on Metadata generator
11
+ * Add support for cacheDuration at the IdpMetadataParser
12
+ * Support customizable statusCode on generated LogoutResponse
13
+ * [#545](https://github.com/onelogin/ruby-saml/pull/545) More specific error messages for signature validation
14
+ * Support Process Transform
15
+ * Raise SettingError if invoking an action with no endpoint defined on the settings
16
+ * Made IdpMetadataParser more extensible for subclasses
17
+ *[#548](https://github.com/onelogin/ruby-saml/pull/548) Add :skip_audience option
18
+ * [#555](https://github.com/onelogin/ruby-saml/pull/555) Define 'soft' variable to prevent exception when doc cert is invalid
19
+ * Improve documentation
20
+
21
+ ### 1.11.0 (Jul 24, 2019)
22
+
23
+ * Deprecate settings.issuer in favor of settings.sp_entity_id
24
+ * Add support for certification expiration
25
+
3
26
  ### 1.10.2 (Apr 29, 2019)
4
27
 
5
28
  * Add valid until, accessor
@@ -79,7 +79,7 @@ module OneLogin
79
79
  self.class.single_value_compatibility ? single(canonize_name(name)) : multi(canonize_name(name))
80
80
  end
81
81
 
82
- # @return [Array] Return all attributes as an array
82
+ # @return [Hash] Return all attributes as a hash
83
83
  #
84
84
  def all
85
85
  attributes
@@ -113,6 +113,29 @@ module OneLogin
113
113
  end
114
114
  end
115
115
 
116
+ # Fetch attribute value using name or regex
117
+ # @param name [String|Regexp] The attribute name
118
+ # @return [String|Array] Depending on the single value compatibility status this returns:
119
+ # - First value if single_value_compatibility = true
120
+ # response.attributes['mail'] # => 'user@example.com'
121
+ # - All values if single_value_compatibility = false
122
+ # response.attributes['mail'] # => ['user@example.com','user@example.net']
123
+ #
124
+ def fetch(name)
125
+ attributes.each_key do |attribute_key|
126
+ if name.is_a?(Regexp)
127
+ if name.method_exists? :match?
128
+ return self[attribute_key] if name.match?(attribute_key)
129
+ else
130
+ return self[attribute_key] if name.match(attribute_key)
131
+ end
132
+ elsif canonize_name(name) == canonize_name(attribute_key)
133
+ return self[attribute_key]
134
+ end
135
+ end
136
+ nil
137
+ end
138
+
116
139
  protected
117
140
 
118
141
  # stringifies all names so both 'email' and :email return the same result
@@ -3,6 +3,7 @@ require "rexml/document"
3
3
  require "onelogin/ruby-saml/logging"
4
4
  require "onelogin/ruby-saml/saml_message"
5
5
  require "onelogin/ruby-saml/utils"
6
+ require "onelogin/ruby-saml/setting_error"
6
7
 
7
8
  # Only supports SAML 2.0
8
9
  module OneLogin
@@ -23,6 +24,10 @@ module OneLogin
23
24
  @uuid = OneLogin::RubySaml::Utils.uuid
24
25
  end
25
26
 
27
+ def request_id
28
+ @uuid
29
+ end
30
+
26
31
  # Creates the AuthNRequest string.
27
32
  # @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings
28
33
  # @param params [Hash] Some extra parameters to be added in the GET for example the RelayState
@@ -30,14 +35,14 @@ module OneLogin
30
35
  #
31
36
  def create(settings, params = {})
32
37
  params = create_params(settings, params)
33
- params_prefix = (settings.idp_sso_target_url =~ /\?/) ? '&' : '?'
38
+ params_prefix = (settings.idp_sso_service_url =~ /\?/) ? '&' : '?'
34
39
  saml_request = CGI.escape(params.delete("SAMLRequest"))
35
40
  request_params = "#{params_prefix}SAMLRequest=#{saml_request}"
36
41
  params.each_pair do |key, value|
37
42
  request_params << "&#{key.to_s}=#{CGI.escape(value.to_s)}"
38
43
  end
39
- raise "Invalid settings, idp_sso_target_url is not set!" if settings.idp_sso_target_url.nil?
40
- @login_url = settings.idp_sso_target_url + request_params
44
+ raise SettingError.new "Invalid settings, idp_sso_service_url is not set!" if settings.idp_sso_service_url.nil? or settings.idp_sso_service_url.empty?
45
+ @login_url = settings.idp_sso_service_url + request_params
41
46
  end
42
47
 
43
48
  # Creates the Get parameters for the request.
@@ -107,7 +112,7 @@ module OneLogin
107
112
  root.attributes['ID'] = uuid
108
113
  root.attributes['IssueInstant'] = time
109
114
  root.attributes['Version'] = "2.0"
110
- root.attributes['Destination'] = settings.idp_sso_target_url unless settings.idp_sso_target_url.nil?
115
+ root.attributes['Destination'] = settings.idp_sso_service_url unless settings.idp_sso_service_url.nil? or settings.idp_sso_service_url.empty?
111
116
  root.attributes['IsPassive'] = settings.passive unless settings.passive.nil?
112
117
  root.attributes['ProtocolBinding'] = settings.protocol_binding unless settings.protocol_binding.nil?
113
118
  root.attributes["AttributeConsumingServiceIndex"] = settings.attributes_index unless settings.attributes_index.nil?
@@ -15,10 +15,10 @@ module OneLogin
15
15
 
16
16
  module SamlMetadata
17
17
  module Vocabulary
18
- METADATA = "urn:oasis:names:tc:SAML:2.0:metadata"
19
- DSIG = "http://www.w3.org/2000/09/xmldsig#"
20
- NAME_FORMAT = "urn:oasis:names:tc:SAML:2.0:attrname-format:*"
21
- SAML_ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
18
+ METADATA = "urn:oasis:names:tc:SAML:2.0:metadata".freeze
19
+ DSIG = "http://www.w3.org/2000/09/xmldsig#".freeze
20
+ NAME_FORMAT = "urn:oasis:names:tc:SAML:2.0:attrname-format:*".freeze
21
+ SAML_ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion".freeze
22
22
  end
23
23
 
24
24
  NAMESPACE = {
@@ -26,7 +26,7 @@ module OneLogin
26
26
  "NameFormat" => Vocabulary::NAME_FORMAT,
27
27
  "saml" => Vocabulary::SAML_ASSERTION,
28
28
  "ds" => Vocabulary::DSIG
29
- }
29
+ }.freeze
30
30
  end
31
31
 
32
32
  include SamlMetadata::Vocabulary
@@ -34,6 +34,16 @@ module OneLogin
34
34
  attr_reader :response
35
35
  attr_reader :options
36
36
 
37
+ # fetch IdP descriptors from a metadata document
38
+ def self.get_idps(metadata_document, only_entity_id=nil)
39
+ path = "//md:EntityDescriptor#{only_entity_id && '[@entityID="' + only_entity_id + '"]'}/md:IDPSSODescriptor"
40
+ REXML::XPath.match(
41
+ metadata_document,
42
+ path,
43
+ SamlMetadata::NAMESPACE
44
+ )
45
+ end
46
+
37
47
  # Parse the Identity Provider metadata and update the settings with the
38
48
  # IdP values
39
49
  #
@@ -103,6 +113,16 @@ module OneLogin
103
113
  def parse(idp_metadata, options = {})
104
114
  parsed_metadata = parse_to_hash(idp_metadata, options)
105
115
 
116
+ unless parsed_metadata[:cache_duration].nil?
117
+ 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")
120
+ end
121
+ end
122
+ # Remove the cache_duration because on the settings
123
+ # we only gonna suppot valid_until
124
+ parsed_metadata.delete(:cache_duration)
125
+
106
126
  settings = options[:settings]
107
127
 
108
128
  if settings.nil?
@@ -139,17 +159,21 @@ module OneLogin
139
159
  #
140
160
  # @return [Array<Hash>]
141
161
  def parse_to_array(idp_metadata, options = {})
162
+ parse_to_idp_metadata_array(idp_metadata, options).map{|idp_md| idp_md.to_hash(options)}
163
+ end
164
+
165
+ def parse_to_idp_metadata_array(idp_metadata, options = {})
142
166
  @document = REXML::Document.new(idp_metadata)
143
167
  @options = options
144
168
 
145
- idpsso_descriptors = IdpMetadata::get_idps(@document, options[:entity_id])
169
+ idpsso_descriptors = self.class.get_idps(@document, options[:entity_id])
146
170
  if !idpsso_descriptors.any?
147
171
  raise ArgumentError.new("idp_metadata must contain an IDPSSODescriptor element")
148
172
  end
149
173
 
150
- return idpsso_descriptors.map{|id| IdpMetadata.new(id, id.parent.attributes["entityID"]).to_hash(options)}
174
+ return idpsso_descriptors.map{|id| IdpMetadata.new(id, id.parent.attributes["entityID"])}
151
175
  end
152
-
176
+
153
177
  private
154
178
 
155
179
  # Retrieve the remote IdP metadata from the URL or a cached copy.
@@ -175,6 +199,7 @@ module OneLogin
175
199
  end
176
200
 
177
201
  get = Net::HTTP::Get.new(uri.request_uri)
202
+ get.basic_auth uri.user, uri.password if uri.user
178
203
  @response = http.request(get)
179
204
  return response.body if response.is_a? Net::HTTPSuccess
180
205
 
@@ -184,15 +209,8 @@ module OneLogin
184
209
  end
185
210
 
186
211
  class IdpMetadata
187
- def self.get_idps(metadata_document, only_entity_id=nil)
188
- path = "//md:EntityDescriptor#{only_entity_id && '[@entityID="' + only_entity_id + '"]'}/md:IDPSSODescriptor"
189
- REXML::XPath.match(
190
- metadata_document,
191
- path,
192
- SamlMetadata::NAMESPACE
193
- )
194
- end
195
-
212
+ attr_reader :idpsso_descriptor, :entity_id
213
+
196
214
  def initialize(idpsso_descriptor, entity_id)
197
215
  @idpsso_descriptor = idpsso_descriptor
198
216
  @entity_id = entity_id
@@ -202,13 +220,15 @@ module OneLogin
202
220
  {
203
221
  :idp_entity_id => @entity_id,
204
222
  :name_identifier_format => idp_name_id_format,
205
- :idp_sso_target_url => single_signon_service_url(options),
206
- :idp_slo_target_url => single_logout_service_url(options),
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),
207
226
  :idp_attribute_names => attribute_names,
208
227
  :idp_cert => nil,
209
228
  :idp_cert_fingerprint => nil,
210
229
  :idp_cert_multi => nil,
211
- :valid_until => valid_until
230
+ :valid_until => valid_until,
231
+ :cache_duration => cache_duration,
212
232
  }.tap do |response_hash|
213
233
  merge_certificates_into(response_hash) unless certificates.nil?
214
234
  end
@@ -232,6 +252,13 @@ module OneLogin
232
252
  root.attributes['validUntil'] if root && root.attributes
233
253
  end
234
254
 
255
+ # @return [String|nil] 'cacheDuration' attribute of metadata
256
+ #
257
+ def cache_duration
258
+ root = @idpsso_descriptor.root
259
+ root.attributes['cacheDuration'] if root && root.attributes
260
+ end
261
+
235
262
  # @param binding_priority [Array]
236
263
  # @return [String|nil] SingleSignOnService binding if exists
237
264
  #
@@ -296,6 +323,21 @@ module OneLogin
296
323
  return node.value if node
297
324
  end
298
325
 
326
+ # @param options [Hash]
327
+ # @return [String|nil] SingleLogoutService response url if exists
328
+ #
329
+ def single_logout_response_service_url(options = {})
330
+ binding = single_logout_service_binding(options[:slo_binding])
331
+ return if binding.nil?
332
+
333
+ node = REXML::XPath.first(
334
+ @idpsso_descriptor,
335
+ "md:SingleLogoutService[@Binding=\"#{binding}\"]/@ResponseLocation",
336
+ SamlMetadata::NAMESPACE
337
+ )
338
+ return node.value if node
339
+ end
340
+
299
341
  # @return [String|nil] Unformatted Certificate if exists
300
342
  #
301
343
  def certificates
@@ -393,10 +435,6 @@ module OneLogin
393
435
 
394
436
  settings
395
437
  end
396
-
397
- if self.respond_to?(:private_constant)
398
- private_constant :SamlMetadata, :IdpMetadata
399
- end
400
438
  end
401
439
  end
402
440
  end