ruby-saml 1.11.0 → 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
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