ruby-saml 1.11.0 → 1.12.1

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 +76 -22
  4. data/changelog.md +27 -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 +38 -16
  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: 3ac80594648fe4830b965c65366f8bb261a4edfe148c9e929f352b39a1b3428f
4
+ data.tar.gz: b6379aa66a89f2074f434e8c97163022d533e1cdc30c20555135c2e4c82353b4
5
5
  SHA512:
6
- metadata.gz: e85dc90f8f4bd5433f0078a6b5e316109dc7bbbe1ba60d5f8075d1f5047a71c208144d1da51157cf7220f2c56fb8b421e7b70ac5c828bea713146fc6bc774a29
7
- data.tar.gz: 7cce3fd10ff5d7753d518b847b7f0ff0976b4ef5a1b7cc39c6e6cd3c8b32df7649e95ab929631b9838df5a4fb3eb3f3c50479f54253094b457a484710381b8f1
6
+ metadata.gz: b1a380101d7684431209f4e8cc2704c8118621465c3b0a8efc623d573377e14706a4368eae6ea9ef1666da4c36e5e6a61ccae845d9f87e1bab98fbf2cd626ad6
7
+ data.tar.gz: e251b75351483f04d21bc4228af9752cab4d0cc4568952835960363671c28f9e38e62f9b727bde5d62b3a39e095593041e1e7dea9d93084fe6a87aef45a0f8ab
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,22 @@
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
+ `idp_sso_target_url` and `idp_slo_target_url` attributes of the Settings class deprecated in favor of `idp_sso_service_url` and `idp_slo_service_url`.
8
+ In IDPMetadataParser, `parse`, `parse_to_hash` and `parse_to_array` methods now retrieve SSO URL and SLO URL endpoints with
9
+ `idp_sso_service_url` and `idp_slo_service_url` (previously `idp_sso_target_url` and `idp_slo_target_url` respectively).
10
+
11
+ ## Updating from 1.10.x to 1.11.0
12
+ Version `1.11.0` deprecates the use of `settings.issuer` in favour of `settings.sp_entity_id`.
13
+ 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.
14
+
15
+ Version `1.10.2` includes the `valid_until` attribute in parsed IdP metadata.
16
+
17
+ Version `1.10.1` improves Ruby 1.8.7 support.
18
+
19
+ ## Updating from 1.9.0 to 1.10.0
4
20
  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
21
 
6
22
  ## Updating from 1.8.0 to 1.9.0
@@ -107,9 +123,12 @@ We created a demo project for Rails4 that uses the latest version of this librar
107
123
  * 2.4.x
108
124
  * 2.5.x
109
125
  * 2.6.x
110
- * JRuby 1.7.19
111
- * JRuby 9.0.0.0
112
- * JRuby 9.2.0.0
126
+ * 2.7.x
127
+ * 3.0.x
128
+ * JRuby 1.7.x
129
+ * JRuby 9.0.x
130
+ * JRuby 9.1.x
131
+ * JRuby 9.2.x
113
132
 
114
133
  ## Adding Features, Pull Requests
115
134
  * Fork the repository
@@ -141,7 +160,7 @@ Using `Gemfile`
141
160
 
142
161
  ```ruby
143
162
  # latest stable
144
- gem 'ruby-saml', '~> 1.9.0'
163
+ gem 'ruby-saml', '~> 1.11.0'
145
164
 
146
165
  # or track master for bleeding-edge
147
166
  gem 'ruby-saml', :github => 'onelogin/ruby-saml'
@@ -228,6 +247,7 @@ def consume
228
247
  session[:attributes] = response.attributes
229
248
  else
230
249
  authorize_failure # This method shows an error message
250
+ # List of errors is available in response.errors array
231
251
  end
232
252
  end
233
253
  ```
@@ -251,8 +271,8 @@ def saml_settings
251
271
  settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
252
272
  settings.sp_entity_id = "http://#{request.host}/saml/metadata"
253
273
  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}"
274
+ settings.idp_sso_service_url = "https://app.onelogin.com/trust/saml2/http-post/sso/#{OneLoginAppId}"
275
+ settings.idp_slo_service_url = "https://app.onelogin.com/trust/saml2/http-redirect/slo/#{OneLoginAppId}"
256
276
  settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
257
277
  settings.idp_cert_fingerprint_algorithm = "http://www.w3.org/2000/09/xmldsig#sha1"
258
278
  settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
@@ -273,7 +293,7 @@ def saml_settings
273
293
  end
274
294
  ```
275
295
 
276
- The use of settings.issuer is deprecated in favour of settings.sp_entity_id
296
+ The use of settings.issuer is deprecated in favour of settings.sp_entity_id since version 1.11.0
277
297
 
278
298
  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
299
 
@@ -282,6 +302,7 @@ response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_authnst
282
302
  response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_conditions: true}) # skips conditions
283
303
  response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_subject_confirmation: true}) # skips subject confirmation
284
304
  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
305
+ response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_audience: true}) # skips audience check
285
306
  ```
286
307
 
287
308
  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 +326,7 @@ class SamlController < ApplicationController
305
326
  session[:attributes] = response.attributes
306
327
  else
307
328
  authorize_failure # This method shows an error message
329
+ # List of errors is available in response.errors array
308
330
  end
309
331
  end
310
332
 
@@ -315,7 +337,7 @@ class SamlController < ApplicationController
315
337
 
316
338
  settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
317
339
  settings.sp_entity_id = "http://#{request.host}/saml/metadata"
318
- settings.idp_sso_target_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}"
340
+ settings.idp_sso_service_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}"
319
341
  settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
320
342
  settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
321
343
 
@@ -388,8 +410,8 @@ end
388
410
  The following attributes are set:
389
411
  * idp_entity_id
390
412
  * name_identifier_format
391
- * idp_sso_target_url
392
- * idp_slo_target_url
413
+ * idp_sso_service_url
414
+ * idp_slo_service_url
393
415
  * idp_attribute_names
394
416
  * idp_cert
395
417
  * idp_cert_fingerprint
@@ -455,6 +477,9 @@ Imagine this `saml:AttributeStatement`
455
477
  <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
456
478
  <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="1"/>
457
479
  </saml:Attribute>
480
+ <saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname">
481
+ <saml:AttributeValue>usersName</saml:AttributeValue>
482
+ </saml:Attribute>
458
483
  </saml:AttributeStatement>
459
484
  ```
460
485
 
@@ -465,7 +490,8 @@ pp(response.attributes) # is an OneLogin::RubySaml::Attributes object
465
490
  "another_value"=>["value1", "value2"],
466
491
  "role"=>["role1", "role2", "role3"],
467
492
  "attribute_with_nil_value"=>[nil],
468
- "attribute_with_nils_and_empty_strings"=>["", "valuePresent", nil, nil]}>
493
+ "attribute_with_nils_and_empty_strings"=>["", "valuePresent", nil, nil]
494
+ "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"=>["usersName"]}>
469
495
 
470
496
  # Active single_value_compatibility
471
497
  OneLogin::RubySaml::Attributes.single_value_compatibility = true
@@ -482,6 +508,9 @@ pp(response.attributes.single(:role))
482
508
  pp(response.attributes.multi(:role))
483
509
  # => ["role1", "role2", "role3"]
484
510
 
511
+ pp(response.attributes.fetch(:role))
512
+ # => "role1"
513
+
485
514
  pp(response.attributes[:attribute_with_nil_value])
486
515
  # => nil
487
516
 
@@ -497,6 +526,9 @@ pp(response.attributes.single(:not_exists))
497
526
  pp(response.attributes.multi(:not_exists))
498
527
  # => nil
499
528
 
529
+ pp(response.attributes.fetch(/givenname/))
530
+ # => "usersName"
531
+
500
532
  # Deactive single_value_compatibility
501
533
  OneLogin::RubySaml::Attributes.single_value_compatibility = false
502
534
 
@@ -512,6 +544,9 @@ pp(response.attributes.single(:role))
512
544
  pp(response.attributes.multi(:role))
513
545
  # => ["role1", "role2", "role3"]
514
546
 
547
+ pp(response.attributes.fetch(:role))
548
+ # => ["role1", "role2", "role3"]
549
+
515
550
  pp(response.attributes[:attribute_with_nil_value])
516
551
  # => [nil]
517
552
 
@@ -526,12 +561,15 @@ pp(response.attributes.single(:not_exists))
526
561
 
527
562
  pp(response.attributes.multi(:not_exists))
528
563
  # => nil
564
+
565
+ pp(response.attributes.fetch(/givenname/))
566
+ # => ["usersName"]
529
567
  ```
530
568
 
531
569
  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
570
  To add a `saml:AuthnContextDeclRef`, define `settings.authn_context_decl_ref`.
533
571
 
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
572
+ 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
573
  building the authrequest object.
536
574
 
537
575
 
@@ -570,7 +608,7 @@ Remember to provide it to the Signature builder if you are sending a `GET RelayS
570
608
  signature validation process will fail at the Identity Provider.
571
609
 
572
610
  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
611
+ The Identity Provider will validate the sign of the received request/responses with the public x509 cert of the
574
612
  Service Provider.
575
613
 
576
614
  Notice that this toolkit uses 'settings.certificate' and 'settings.private_key' for the sign and decrypt processes.
@@ -611,21 +649,27 @@ def sp_logout_request
611
649
  # LogoutRequest accepts plain browser requests w/o paramters
612
650
  settings = saml_settings
613
651
 
614
- if settings.idp_slo_target_url.nil?
652
+ if settings.idp_slo_service_url.nil?
615
653
  logger.info "SLO IdP Endpoint not found in settings, executing then a normal logout'"
616
654
  delete_session
617
655
  else
618
656
 
619
- # Since we created a new SAML request, save the transaction_id
620
- # to compare it with the response we get back
621
657
  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]}'"
658
+ logger.info "New SP SLO for userid '#{session[:userid]}' transactionid '#{logout_request.uuid}'"
624
659
 
625
660
  if settings.name_identifier_value.nil?
626
661
  settings.name_identifier_value = session[:userid]
627
662
  end
628
663
 
664
+ # Ensure user is logged out before redirect to IdP, in case anything goes wrong during single logout process (as recommended by saml2int [SDP-SP34])
665
+ logged_user = session[:userid]
666
+ logger.info "Delete session for '#{session[:userid]}'"
667
+ delete_session
668
+
669
+ # Save the transaction_id to compare it with the response we get back
670
+ session[:transaction_id] = logout_request.uuid
671
+ session[:logged_out_user] = logged_user
672
+
629
673
  relayState = url_for controller: 'saml', action: 'index'
630
674
  redirect_to(logout_request.create(settings, :RelayState => relayState))
631
675
  end
@@ -653,7 +697,7 @@ def process_logout_response
653
697
  logger.error "The SAML Logout Response is invalid"
654
698
  else
655
699
  # Actually log out this session
656
- logger.info "Delete session for '#{session[:userid]}'"
700
+ logger.info "SLO completed for '#{session[:logged_out_user]}'"
657
701
  delete_session
658
702
  end
659
703
  end
@@ -662,6 +706,8 @@ end
662
706
  def delete_session
663
707
  session[:userid] = nil
664
708
  session[:attributes] = nil
709
+ session[:transaction_id] = nil
710
+ session[:logged_out_user] = nil
665
711
  end
666
712
  ```
667
713
 
@@ -674,7 +720,7 @@ def idp_logout_request
674
720
  logout_request = OneLogin::RubySaml::SloLogoutrequest.new(params[:SAMLRequest])
675
721
  if !logout_request.is_valid?
676
722
  logger.error "IdP initiated LogoutRequest was not valid!"
677
- render :inline => logger.error
723
+ return render :inline => logger.error
678
724
  end
679
725
  logger.info "IdP initiated Logout for #{logout_request.name_id}"
680
726
 
@@ -729,6 +775,14 @@ class SamlController < ApplicationController
729
775
  end
730
776
  ```
731
777
 
778
+ You can add ValidUntil and CacheDuration to the XML Metadata using instead
779
+ ```ruby
780
+ # Valid until => 2 days from now
781
+ # Cache duration = 604800s = 1 week
782
+ valid_until = Time.now + 172800
783
+ cache_duration = 604800
784
+ meta.generate(settings, false, valid_until, cache_duration)
785
+ ```
732
786
 
733
787
  ## Clock Drift
734
788
 
data/changelog.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # RubySaml Changelog
2
2
 
3
+ ### 1.12.1 (Apr 05, 2022)
4
+ * Fix XPath typo incompatible with Rexml 3.2.5
5
+ * Refactor GCM support
6
+
7
+ ### 1.12.0 (Feb 18, 2021)
8
+ * Support AES-128-GCM, AES-192-GCM, and AES-256-GCM encryptions
9
+ * Parse & return SLO ResponseLocation in IDPMetadataParser & Settings
10
+ * Adding idp_sso_service_url and idp_slo_service_url settings
11
+ * [#536](https://github.com/onelogin/ruby-saml/pull/536) Adding feth method to be able retrieve attributes based on regex
12
+ * Reduce size of built gem by excluding the test folder
13
+ * Improve protection on Zlib deflate decompression bomb attack.
14
+ * Add ValidUntil and cacheDuration support on Metadata generator
15
+ * Add support for cacheDuration at the IdpMetadataParser
16
+ * Support customizable statusCode on generated LogoutResponse
17
+ * [#545](https://github.com/onelogin/ruby-saml/pull/545) More specific error messages for signature validation
18
+ * Support Process Transform
19
+ * Raise SettingError if invoking an action with no endpoint defined on the settings
20
+ * Made IdpMetadataParser more extensible for subclasses
21
+ *[#548](https://github.com/onelogin/ruby-saml/pull/548) Add :skip_audience option
22
+ * [#555](https://github.com/onelogin/ruby-saml/pull/555) Define 'soft' variable to prevent exception when doc cert is invalid
23
+ * Improve documentation
24
+
25
+ ### 1.11.0 (Jul 24, 2019)
26
+
27
+ * Deprecate settings.issuer in favor of settings.sp_entity_id
28
+ * Add support for certification expiration
29
+
3
30
  ### 1.10.2 (Apr 29, 2019)
4
31
 
5
32
  * 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.respond_to? :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
@@ -1,6 +1,7 @@
1
1
  require "onelogin/ruby-saml/logging"
2
2
  require "onelogin/ruby-saml/saml_message"
3
3
  require "onelogin/ruby-saml/utils"
4
+ require "onelogin/ruby-saml/setting_error"
4
5
 
5
6
  # Only supports SAML 2.0
6
7
  module OneLogin
@@ -20,6 +21,10 @@ module OneLogin
20
21
  @uuid = OneLogin::RubySaml::Utils.uuid
21
22
  end
22
23
 
24
+ def request_id
25
+ @uuid
26
+ end
27
+
23
28
  # Creates the Logout Request string.
24
29
  # @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings
25
30
  # @param params [Hash] Some extra parameters to be added in the GET for example the RelayState
@@ -33,6 +38,7 @@ module OneLogin
33
38
  params.each_pair do |key, value|
34
39
  request_params << "&#{key.to_s}=#{CGI.escape(value.to_s)}"
35
40
  end
41
+ raise SettingError.new "Invalid settings, idp_slo_target_url is not set!" if settings.idp_slo_target_url.nil? or settings.idp_slo_target_url.empty?
36
42
  @logout_url = settings.idp_slo_target_url + request_params
37
43
  end
38
44
 
@@ -103,7 +109,7 @@ module OneLogin
103
109
  root.attributes['ID'] = uuid
104
110
  root.attributes['IssueInstant'] = time
105
111
  root.attributes['Version'] = "2.0"
106
- root.attributes['Destination'] = settings.idp_slo_target_url unless settings.idp_slo_target_url.nil?
112
+ root.attributes['Destination'] = settings.idp_slo_target_url unless settings.idp_slo_target_url.nil? or settings.idp_slo_target_url.empty?
107
113
 
108
114
  if settings.sp_entity_id
109
115
  issuer = root.add_element "saml:Issuer"
@@ -47,6 +47,10 @@ module OneLogin
47
47
  @document = XMLSecurity::SignedDocument.new(@response)
48
48
  end
49
49
 
50
+ def response_id
51
+ id(document)
52
+ end
53
+
50
54
  # Checks if the Status has the "Success" code
51
55
  # @return [Boolean] True if the StatusCode is Sucess
52
56
  # @raise [ValidationError] if soft == false and validation fails