ruby-saml 1.11.0 → 1.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +25 -0
  3. data/{changelog.md → CHANGELOG.md} +44 -1
  4. data/README.md +333 -217
  5. data/UPGRADING.md +149 -0
  6. data/lib/onelogin/ruby-saml/attributes.rb +24 -1
  7. data/lib/onelogin/ruby-saml/authrequest.rb +11 -7
  8. data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +154 -83
  9. data/lib/onelogin/ruby-saml/logoutrequest.rb +12 -6
  10. data/lib/onelogin/ruby-saml/logoutresponse.rb +5 -1
  11. data/lib/onelogin/ruby-saml/metadata.rb +62 -17
  12. data/lib/onelogin/ruby-saml/response.rb +51 -31
  13. data/lib/onelogin/ruby-saml/saml_message.rb +8 -3
  14. data/lib/onelogin/ruby-saml/setting_error.rb +6 -0
  15. data/lib/onelogin/ruby-saml/settings.rb +89 -49
  16. data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +16 -4
  17. data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +31 -17
  18. data/lib/onelogin/ruby-saml/utils.rb +63 -2
  19. data/lib/onelogin/ruby-saml/version.rb +1 -1
  20. data/lib/xml_security.rb +39 -13
  21. data/ruby-saml.gemspec +8 -4
  22. metadata +24 -282
  23. data/.travis.yml +0 -46
  24. data/test/certificates/certificate.der +0 -0
  25. data/test/certificates/certificate1 +0 -12
  26. data/test/certificates/certificate_without_head_foot +0 -1
  27. data/test/certificates/formatted_certificate +0 -14
  28. data/test/certificates/formatted_chained_certificate +0 -42
  29. data/test/certificates/formatted_private_key +0 -12
  30. data/test/certificates/formatted_rsa_private_key +0 -12
  31. data/test/certificates/invalid_certificate1 +0 -1
  32. data/test/certificates/invalid_certificate2 +0 -1
  33. data/test/certificates/invalid_certificate3 +0 -12
  34. data/test/certificates/invalid_chained_certificate1 +0 -1
  35. data/test/certificates/invalid_private_key1 +0 -1
  36. data/test/certificates/invalid_private_key2 +0 -1
  37. data/test/certificates/invalid_private_key3 +0 -10
  38. data/test/certificates/invalid_rsa_private_key1 +0 -1
  39. data/test/certificates/invalid_rsa_private_key2 +0 -1
  40. data/test/certificates/invalid_rsa_private_key3 +0 -10
  41. data/test/certificates/ruby-saml-2.crt +0 -15
  42. data/test/certificates/ruby-saml.crt +0 -14
  43. data/test/certificates/ruby-saml.key +0 -15
  44. data/test/idp_metadata_parser_test.rb +0 -594
  45. data/test/logging_test.rb +0 -62
  46. data/test/logout_requests/invalid_slo_request.xml +0 -6
  47. data/test/logout_requests/slo_request.xml +0 -4
  48. data/test/logout_requests/slo_request.xml.base64 +0 -1
  49. data/test/logout_requests/slo_request_deflated.xml.base64 +0 -1
  50. data/test/logout_requests/slo_request_with_name_id_format.xml +0 -4
  51. data/test/logout_requests/slo_request_with_session_index.xml +0 -5
  52. data/test/logout_responses/logoutresponse_fixtures.rb +0 -86
  53. data/test/logoutrequest_test.rb +0 -260
  54. data/test/logoutresponse_test.rb +0 -427
  55. data/test/metadata/idp_descriptor.xml +0 -26
  56. data/test/metadata/idp_descriptor_2.xml +0 -56
  57. data/test/metadata/idp_descriptor_3.xml +0 -14
  58. data/test/metadata/idp_descriptor_4.xml +0 -72
  59. data/test/metadata/idp_metadata_different_sign_and_encrypt_cert.xml +0 -72
  60. data/test/metadata/idp_metadata_multi_certs.xml +0 -75
  61. data/test/metadata/idp_metadata_multi_signing_certs.xml +0 -52
  62. data/test/metadata/idp_metadata_same_sign_and_encrypt_cert.xml +0 -71
  63. data/test/metadata/idp_multiple_descriptors.xml +0 -59
  64. data/test/metadata/idp_multiple_descriptors_2.xml +0 -59
  65. data/test/metadata/no_idp_descriptor.xml +0 -21
  66. data/test/metadata_test.rb +0 -331
  67. data/test/request_test.rb +0 -340
  68. data/test/response_test.rb +0 -1629
  69. data/test/responses/adfs_response_sha1.xml +0 -46
  70. data/test/responses/adfs_response_sha256.xml +0 -46
  71. data/test/responses/adfs_response_sha384.xml +0 -46
  72. data/test/responses/adfs_response_sha512.xml +0 -46
  73. data/test/responses/adfs_response_xmlns.xml +0 -45
  74. data/test/responses/attackxee.xml +0 -13
  75. data/test/responses/invalids/duplicated_attributes.xml.base64 +0 -1
  76. data/test/responses/invalids/empty_destination.xml.base64 +0 -1
  77. data/test/responses/invalids/empty_nameid.xml.base64 +0 -1
  78. data/test/responses/invalids/encrypted_new_attack.xml.base64 +0 -1
  79. data/test/responses/invalids/invalid_audience.xml.base64 +0 -1
  80. data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +0 -1
  81. data/test/responses/invalids/invalid_issuer_message.xml.base64 +0 -1
  82. data/test/responses/invalids/invalid_signature_position.xml.base64 +0 -1
  83. data/test/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 +0 -1
  84. data/test/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 +0 -1
  85. data/test/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 +0 -1
  86. data/test/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 +0 -1
  87. data/test/responses/invalids/multiple_assertions.xml.base64 +0 -2
  88. data/test/responses/invalids/multiple_signed.xml.base64 +0 -1
  89. data/test/responses/invalids/no_authnstatement.xml.base64 +0 -1
  90. data/test/responses/invalids/no_conditions.xml.base64 +0 -1
  91. data/test/responses/invalids/no_id.xml.base64 +0 -1
  92. data/test/responses/invalids/no_issuer_assertion.xml.base64 +0 -1
  93. data/test/responses/invalids/no_issuer_response.xml.base64 +0 -1
  94. data/test/responses/invalids/no_nameid.xml.base64 +0 -1
  95. data/test/responses/invalids/no_saml2.xml.base64 +0 -1
  96. data/test/responses/invalids/no_signature.xml.base64 +0 -1
  97. data/test/responses/invalids/no_status.xml.base64 +0 -1
  98. data/test/responses/invalids/no_status_code.xml.base64 +0 -1
  99. data/test/responses/invalids/no_subjectconfirmation_data.xml.base64 +0 -1
  100. data/test/responses/invalids/no_subjectconfirmation_method.xml.base64 +0 -1
  101. data/test/responses/invalids/response_invalid_signed_element.xml.base64 +0 -1
  102. data/test/responses/invalids/response_with_concealed_signed_assertion.xml +0 -51
  103. data/test/responses/invalids/response_with_doubled_signed_assertion.xml +0 -49
  104. data/test/responses/invalids/signature_wrapping_attack.xml.base64 +0 -1
  105. data/test/responses/invalids/status_code_responder.xml.base64 +0 -1
  106. data/test/responses/invalids/status_code_responer_and_msg.xml.base64 +0 -1
  107. data/test/responses/invalids/wrong_spnamequalifier.xml.base64 +0 -1
  108. data/test/responses/no_signature_ns.xml +0 -48
  109. data/test/responses/open_saml_response.xml +0 -56
  110. data/test/responses/response_assertion_wrapped.xml.base64 +0 -93
  111. data/test/responses/response_audience_self_closed_tag.xml.base64 +0 -1
  112. data/test/responses/response_double_status_code.xml.base64 +0 -1
  113. data/test/responses/response_encrypted_attrs.xml.base64 +0 -1
  114. data/test/responses/response_encrypted_nameid.xml.base64 +0 -1
  115. data/test/responses/response_eval.xml +0 -7
  116. data/test/responses/response_no_cert_and_encrypted_attrs.xml +0 -29
  117. data/test/responses/response_node_text_attack.xml.base64 +0 -1
  118. data/test/responses/response_node_text_attack2.xml.base64 +0 -1
  119. data/test/responses/response_node_text_attack3.xml.base64 +0 -1
  120. data/test/responses/response_unsigned_xml_base64 +0 -1
  121. data/test/responses/response_with_ampersands.xml +0 -139
  122. data/test/responses/response_with_ampersands.xml.base64 +0 -93
  123. data/test/responses/response_with_ds_namespace_at_the_root.xml.base64 +0 -1
  124. data/test/responses/response_with_multiple_attribute_statements.xml +0 -72
  125. data/test/responses/response_with_multiple_attribute_values.xml +0 -67
  126. data/test/responses/response_with_retrieval_method.xml +0 -26
  127. data/test/responses/response_with_saml2_namespace.xml.base64 +0 -102
  128. data/test/responses/response_with_signed_assertion.xml.base64 +0 -66
  129. data/test/responses/response_with_signed_assertion_2.xml.base64 +0 -1
  130. data/test/responses/response_with_signed_assertion_3.xml +0 -30
  131. data/test/responses/response_with_signed_message_and_assertion.xml +0 -34
  132. data/test/responses/response_with_undefined_recipient.xml.base64 +0 -1
  133. data/test/responses/response_without_attributes.xml.base64 +0 -79
  134. data/test/responses/response_without_reference_uri.xml.base64 +0 -1
  135. data/test/responses/response_wrapped.xml.base64 +0 -150
  136. data/test/responses/signed_message_encrypted_signed_assertion.xml.base64 +0 -1
  137. data/test/responses/signed_message_encrypted_unsigned_assertion.xml.base64 +0 -1
  138. data/test/responses/signed_nameid_in_atts.xml +0 -47
  139. data/test/responses/signed_unqual_nameid_in_atts.xml +0 -47
  140. data/test/responses/simple_saml_php.xml +0 -71
  141. data/test/responses/starfield_response.xml.base64 +0 -1
  142. data/test/responses/test_sign.xml +0 -43
  143. data/test/responses/unsigned_encrypted_adfs.xml +0 -23
  144. data/test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64 +0 -1
  145. data/test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64 +0 -1
  146. data/test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64 +0 -1
  147. data/test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64 +0 -1
  148. data/test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64 +0 -1
  149. data/test/responses/unsigned_message_encrypted_signed_assertion.xml.base64 +0 -1
  150. data/test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64 +0 -1
  151. data/test/responses/valid_response.xml.base64 +0 -1
  152. data/test/responses/valid_response_with_formatted_x509certificate.xml.base64 +0 -1
  153. data/test/responses/valid_response_without_x509certificate.xml.base64 +0 -1
  154. data/test/saml_message_test.rb +0 -56
  155. data/test/settings_test.rb +0 -338
  156. data/test/slo_logoutrequest_test.rb +0 -467
  157. data/test/slo_logoutresponse_test.rb +0 -233
  158. data/test/test_helper.rb +0 -333
  159. data/test/utils_test.rb +0 -259
  160. data/test/xml_security_test.rb +0 -421
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: afcd8a95f66ec94e3bb68b5a5264a651ae623923
4
- data.tar.gz: eb05bc60959fde11ac20b0af0d14c07b7c5c1b38
3
+ metadata.gz: ded4e8f9560644f26e90079ecf0021f81fb8fb90
4
+ data.tar.gz: 034e0d8ee8d11aa443435b20d071015dfbcf5161
5
5
  SHA512:
6
- metadata.gz: e85dc90f8f4bd5433f0078a6b5e316109dc7bbbe1ba60d5f8075d1f5047a71c208144d1da51157cf7220f2c56fb8b421e7b70ac5c828bea713146fc6bc774a29
7
- data.tar.gz: 7cce3fd10ff5d7753d518b847b7f0ff0976b4ef5a1b7cc39c6e6cd3c8b32df7649e95ab929631b9838df5a4fb3eb3f3c50479f54253094b457a484710381b8f1
6
+ metadata.gz: 957e2b7598309e9b770019902f28bdec07a28a19a77abfb7e72d503ab3c8b4c57138451d3bb0bced671aca4d454d6637821a3931e91e6f4d79ef4d5d1a91a25e
7
+ data.tar.gz: 74d06dcdc7ba3f3c0dc797ad3e329987f0bd32bfc5b0bdee62f9c081688dd97bb4892ef42de795c09c59b2c48487b673476a6dd12aedca0770b600c770e2c4b7
@@ -0,0 +1,25 @@
1
+ name: ruby-saml CI
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ test:
7
+ name: Unit test
8
+ strategy:
9
+ fail-fast: false
10
+ matrix:
11
+ os: [ubuntu-latest, macos-latest]
12
+ ruby-version: [2.1.9, 2.2.10, 2.3.8, 2.4.6, 2.5.8, 2.6.6, 2.7.2, 3.0.1, jruby-9.1.17.0, jruby-9.2.17.0, truffleruby]
13
+ runs-on: ${{ matrix.os }}
14
+ steps:
15
+ - uses: actions/checkout@v2
16
+ - name: Set up Ruby ${{ matrix.ruby-version }}
17
+ uses: ruby/setup-ruby@v1
18
+ with:
19
+ ruby-version: ${{ matrix.ruby-version }}
20
+
21
+ - name: Install dependencies
22
+ run: bundle install
23
+
24
+ - name: Run tests
25
+ run: bundle exec rake
@@ -1,4 +1,47 @@
1
- # RubySaml Changelog
1
+ # Ruby SAML Changelog
2
+
3
+ ### 1.13.0 (Sept 06, 2021)
4
+ * [#611](https://github.com/onelogin/ruby-saml/pull/601) Replace MAX_BYTE_SIZE constant with setting: message_max_bytesize
5
+ * [#605](https://github.com/onelogin/ruby-saml/pull/605) :allowed_clock_drift is now bidrectional
6
+ * [#614](https://github.com/onelogin/ruby-saml/pull/614) Support :name_id_format option for IdpMetadataParser
7
+ * [#611](https://github.com/onelogin/ruby-saml/pull/611) IdpMetadataParser should always set idp_cert_multi, even when there is only one cert
8
+ * [#610](https://github.com/onelogin/ruby-saml/pull/610) New IDP sso/slo binding params which deprecate :embed_sign
9
+ * [#602](https://github.com/onelogin/ruby-saml/pull/602) Refactor the OneLogin::RubySaml::Metadata class
10
+ * [#586](https://github.com/onelogin/ruby-saml/pull/586) Support milliseconds in cacheDuration parsing
11
+ * [#585](https://github.com/onelogin/ruby-saml/pull/585) Do not append " | " to StatusCode unnecessarily
12
+ * [#607](https://github.com/onelogin/ruby-saml/pull/607) Clean up
13
+ * Add warning about the use of IdpMetadataParser class and SSRF
14
+ * CI: Migrate from Travis to Github Actions
15
+
16
+ ### 1.12.2 (Apr 08, 2021)
17
+ * [#575](https://github.com/onelogin/ruby-saml/pull/575) Fix SloLogoutresponse bug on LogoutRequest
18
+
19
+ ### 1.12.1 (Apr 05, 2021)
20
+ * Fix XPath typo incompatible with Rexml 3.2.5
21
+ * Refactor GCM support
22
+
23
+ ### 1.12.0 (Feb 18, 2021)
24
+ * Support AES-128-GCM, AES-192-GCM, and AES-256-GCM encryptions
25
+ * Parse & return SLO ResponseLocation in IDPMetadataParser & Settings
26
+ * Adding idp_sso_service_url and idp_slo_service_url settings
27
+ * [#536](https://github.com/onelogin/ruby-saml/pull/536) Adding feth method to be able retrieve attributes based on regex
28
+ * Reduce size of built gem by excluding the test folder
29
+ * Improve protection on Zlib deflate decompression bomb attack.
30
+ * Add ValidUntil and cacheDuration support on Metadata generator
31
+ * Add support for cacheDuration at the IdpMetadataParser
32
+ * Support customizable statusCode on generated LogoutResponse
33
+ * [#545](https://github.com/onelogin/ruby-saml/pull/545) More specific error messages for signature validation
34
+ * Support Process Transform
35
+ * Raise SettingError if invoking an action with no endpoint defined on the settings
36
+ * Made IdpMetadataParser more extensible for subclasses
37
+ *[#548](https://github.com/onelogin/ruby-saml/pull/548) Add :skip_audience option
38
+ * [#555](https://github.com/onelogin/ruby-saml/pull/555) Define 'soft' variable to prevent exception when doc cert is invalid
39
+ * Improve documentation
40
+
41
+ ### 1.11.0 (Jul 24, 2019)
42
+
43
+ * Deprecate settings.issuer in favor of settings.sp_entity_id
44
+ * Add support for certification expiration
2
45
 
3
46
  ### 1.10.2 (Apr 29, 2019)
4
47
 
data/README.md CHANGED
@@ -1,129 +1,61 @@
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
2
+ [![Build Status](https://github.com/onelogin/ruby-saml/actions/workflows/test.yml/badge.svg?query=branch%3Amaster)](https://github.com/onelogin/ruby-saml/actions/workflows/test.yml?query=branch%3Amaster)
3
+ [![Coverage Status](https://coveralls.io/repos/onelogin/ruby-saml/badge.svg?branch=master)](https://coveralls.io/r/onelogin/ruby-saml?branch=master)
2
4
 
3
- # Updating from 1.9.0 to 1.10.0
4
- 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
-
6
- ## Updating from 1.8.0 to 1.9.0
7
- Version `1.9.0` better supports Ruby 2.4+ and JRuby 9.2.0.0. `Settings` initialization now has a second parameter, `keep_security_settings` (default: false), which saves security settings attributes that are not explicitly overridden, if set to true.
8
-
9
- ## Updating from 1.7.X to 1.8.0
10
- On Version `1.8.0`, creating AuthRequests/LogoutRequests/LogoutResponses with nil RelayState param will not generate a URL with an empty RelayState parameter anymore. It also changes the invalid audience error message.
11
-
12
- ## Updating from 1.6.0 to 1.7.0
13
-
14
- Version `1.7.0` is a recommended update for all Ruby SAML users as it includes a fix for the [CVE-2017-11428](https://www.cvedetails.com/cve/CVE-2017-11428/) vulnerability.
15
-
16
- ## Updating from 1.5.0 to 1.6.0
17
-
18
- Version `1.6.0` changes the preferred way to construct instances of `Logoutresponse` and `SloLogoutrequest`. Previously the _SAMLResponse_, _RelayState_, and _SigAlg_ parameters of these message types were provided via the constructor's `options[:get_params]` parameter. Unfortunately this can result in incompatibility with other SAML implementations; signatures are specified to be computed based on the _sender's_ URI-encoding of the message, which can differ from that of Ruby SAML. In particular, Ruby SAML's URI-encoding does not match that of Microsoft ADFS, so messages from ADFS can fail signature validation.
19
-
20
- The new preferred way to provide _SAMLResponse_, _RelayState_, and _SigAlg_ is via the `options[:raw_get_params]` parameter. For example:
21
-
22
- ```ruby
23
- # In this example `query_params` is assumed to contain decoded query parameters,
24
- # and `raw_query_params` is assumed to contain encoded query parameters as sent by the IDP.
25
- settings = {
26
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
27
- settings.soft = false
28
- }
29
- options = {
30
- get_params: {
31
- "Signature" => query_params["Signature"],
32
- },
33
- raw_get_params: {
34
- "SAMLRequest" => raw_query_params["SAMLRequest"],
35
- "SigAlg" => raw_query_params["SigAlg"],
36
- "RelayState" => raw_query_params["RelayState"],
37
- },
38
- }
39
- slo_logout_request = OneLogin::RubySaml::SloLogoutrequest.new(query_params["SAMLRequest"], settings, options)
40
- raise "Invalid Logout Request" unless slo_logout_request.is_valid?
41
- ```
42
-
43
- The old form is still supported for backward compatibility, but all Ruby SAML users should prefer `options[:raw_get_params]` where possible to ensure compatibility with other SAML implementations.
44
-
45
- ## Updating from 1.4.2 to 1.4.3
46
-
47
- Version `1.4.3` introduces Recipient validation of SubjectConfirmation elements.
48
- The 'Recipient' value is compared with the settings.assertion_consumer_service_url
49
- value.
50
- If you want to skip that validation, add the :skip_recipient_check option to the
51
- initialize method of the Response object.
52
-
53
- Parsing metadata that contains more than one certificate will propagate the
54
- idp_cert_multi property rather than idp_cert. See [signature validation
55
- section](#signature-validation) for details.
56
-
57
- ## Updating from 1.3.x to 1.4.X
58
-
59
- Version `1.4.0` is a recommended update for all Ruby SAML users as it includes security improvements.
60
-
61
- ## Updating from 1.2.x to 1.3.X
62
-
63
- Version `1.3.0` is a recommended update for all Ruby SAML users as it includes security fixes. It adds security improvements in order to prevent Signature wrapping attacks. [CVE-2016-5697](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-5697)
64
-
65
- ## Updating from 1.1.x to 1.2.X
66
-
67
- Version `1.2` adds IDP metadata parsing improvements, uuid deprecation in favour of SecureRandom, refactor error handling and some minor improvements
68
-
69
- There is no compatibility issue detected.
70
-
71
- For more details, please review [the changelog](changelog.md).
72
-
73
- ## Updating from 1.0.x to 1.1.X
74
-
75
- Version `1.1` adds some improvements on signature validation and solves some namespace conflicts.
76
-
77
- ## Updating from 0.9.x to 1.0.X
78
-
79
- Version `1.0` is a recommended update for all Ruby SAML users as it includes security fixes.
80
-
81
- Version `1.0` adds security improvements like entity expansion limitation, more SAML message validations, and other important improvements like decrypt support.
82
-
83
- ### Important Changes
84
- Please note the `get_idp_metadata` method raises an exception when it is not able to fetch the idp metadata, so review your integration if you are using this functionality.
85
-
86
- ## Updating from 0.8.x to 0.9.x
87
- Version `0.9` adds many new features and improvements.
88
-
89
- ## Updating from 0.7.x to 0.8.x
90
- Version `0.8.x` changes the namespace of the gem from `OneLogin::Saml` to `OneLogin::RubySaml`. Please update your implementations of the gem accordingly.
5
+ Ruby SAML minor and tiny versions may introduce breaking changes. Please read
6
+ [UPGRADING.md](UPGRADING.md) for guidance on upgrading to new Ruby SAML versions.
91
7
 
92
8
  ## Overview
93
9
 
94
- The Ruby SAML library is for implementing the client side of a SAML authorization, i.e. it provides a means for managing authorization initialization and confirmation requests from identity providers.
10
+ The Ruby SAML library is for implementing the client side of a SAML authorization,
11
+ i.e. it provides a means for managing authorization initialization and confirmation
12
+ requests from identity providers.
95
13
 
96
14
  SAML authorization is a two step process and you are expected to implement support for both.
97
15
 
98
- We created a demo project for Rails4 that uses the latest version of this library: [ruby-saml-example](https://github.com/onelogin/ruby-saml-example)
16
+ We created a demo project for Rails 4 that uses the latest version of this library:
17
+ [ruby-saml-example](https://github.com/onelogin/ruby-saml-example)
18
+
19
+ ### Supported Ruby Versions
20
+
21
+ The following Ruby versions are covered by CI testing:
99
22
 
100
- ### Supported versions of Ruby
101
- * 1.8.7
102
- * 1.9.x
103
- * 2.0.x
104
23
  * 2.1.x
105
24
  * 2.2.x
106
25
  * 2.3.x
107
26
  * 2.4.x
108
27
  * 2.5.x
109
28
  * 2.6.x
110
- * JRuby 1.7.19
111
- * JRuby 9.0.0.0
112
- * JRuby 9.2.0.0
29
+ * 2.7.x
30
+ * 3.0.x
31
+ * JRuby 9.1.x
32
+ * JRuby 9.2.x
33
+ * TruffleRuby (latest)
34
+
35
+ In addition, the following may work but are untested:
36
+
37
+ * 1.8.7
38
+ * 1.9.x
39
+ * 2.0.x
40
+ * JRuby 1.7.x
41
+ * JRuby 9.0.x
113
42
 
114
43
  ## Adding Features, Pull Requests
44
+
115
45
  * Fork the repository
116
46
  * Make your feature addition or bug fix
117
47
  * Add tests for your new features. This is important so we don't break any features in a future version unintentionally.
118
- * Ensure all tests pass.
48
+ * Ensure all tests pass by running `bundle exec rake test`.
119
49
  * Do not change rakefile, version, or history.
120
50
  * Open a pull request, following [this template](https://gist.github.com/Lordnibbler/11002759).
121
51
 
122
52
  ## Security Guidelines
123
53
 
124
- If you believe you have discovered a security vulnerability in this gem, please report it at https://www.onelogin.com/security with a description. We follow responsible disclosure guidelines, and will work with you to quickly find a resolution.
54
+ If you believe you have discovered a security vulnerability in this gem, please report it
55
+ at https://www.onelogin.com/security with a description. We follow responsible disclosure
56
+ guidelines, and will work with you to quickly find a resolution.
125
57
 
126
- ### Security warning
58
+ ### Security Warning
127
59
 
128
60
  Some tools may incorrectly report ruby-saml is a potential security vulnerability.
129
61
  ruby-saml depends on Nokogiri, and it's possible to use Nokogiri in a dangerous way
@@ -133,15 +65,26 @@ can create an XML External Entity (XXE) vulnerability if the XML data is not tru
133
65
  However, ruby-saml never enables this dangerous Nokogiri configuration;
134
66
  ruby-saml never enables DTDLOAD, and it never disables NONET.
135
67
 
68
+ The OneLogin::RubySaml::IdpMetadataParser class does not validate in any way the URL
69
+ that is introduced in order to be parsed.
70
+
71
+ Usually the same administrator that handles the Service Provider also sets the URL to
72
+ the IdP, which should be a trusted resource.
73
+
74
+ But there are other scenarios, like a SAAS app where the administrator of the app
75
+ delegates this functionality to other users. In this case, extra precaution should
76
+ be taken in order to validate such URL inputs and avoid attacks like SSRF.
136
77
 
137
78
  ## Getting Started
138
- In order to use the toolkit you will need to install the gem (either manually or using Bundler), and require the library in your Ruby application:
79
+
80
+ In order to use Ruby SAML you will need to install the gem (either manually or using Bundler),
81
+ and require the library in your Ruby application:
139
82
 
140
83
  Using `Gemfile`
141
84
 
142
85
  ```ruby
143
86
  # latest stable
144
- gem 'ruby-saml', '~> 1.9.0'
87
+ gem 'ruby-saml', '~> 1.11.0'
145
88
 
146
89
  # or track master for bleeding-edge
147
90
  gem 'ruby-saml', :github => 'onelogin/ruby-saml'
@@ -153,7 +96,8 @@ Using RubyGems
153
96
  gem install ruby-saml
154
97
  ```
155
98
 
156
- When requiring the gem, you can add the whole toolkit
99
+ You may require the entire Ruby SAML gem:
100
+
157
101
  ```ruby
158
102
  require 'onelogin/ruby-saml'
159
103
  ```
@@ -166,7 +110,9 @@ require 'onelogin/ruby-saml/authrequest'
166
110
 
167
111
  ### Installation on Ruby 1.8.7
168
112
 
169
- This gem uses Nokogiri as a dependency, which dropped support for Ruby 1.8.x in Nokogiri 1.6. When installing this gem on Ruby 1.8.7, you will need to make sure a version of Nokogiri prior to 1.6 is installed or specified if it hasn't been already.
113
+ This gem uses Nokogiri as a dependency, which dropped support for Ruby 1.8.x in Nokogiri 1.6.
114
+ When installing this gem on Ruby 1.8.7, you will need to make sure a version of Nokogiri
115
+ prior to 1.6 is installed or specified if it hasn't been already.
170
116
 
171
117
  Using `Gemfile`
172
118
 
@@ -195,7 +141,10 @@ OneLogin::RubySaml::Logging.logger = Logger.new('/var/log/ruby-saml.log')
195
141
 
196
142
  ## The Initialization Phase
197
143
 
198
- This is the first request you will get from the identity provider. It will hit your application at a specific URL that you've announced as your SAML initialization point. The response to this initialization is a redirect back to the identity provider, which can look something like this (ignore the saml_settings method call for now):
144
+ This is the first request you will get from the identity provider. It will hit your application
145
+ at a specific URL that you've announced as your SAML initialization point. The response to
146
+ this initialization is a redirect back to the identity provider, which can look something
147
+ like this (ignore the saml_settings method call for now):
199
148
 
200
149
  ```ruby
201
150
  def init
@@ -215,7 +164,10 @@ def init
215
164
  end
216
165
  ```
217
166
 
218
- Once you've redirected back to the identity provider, it will ensure that the user has been authorized and redirect back to your application for final consumption. This can look something like this (the `authorize_success` and `authorize_failure` methods are specific to your application):
167
+ Once you've redirected back to the identity provider, it will ensure that the user has been
168
+ authorized and redirect back to your application for final consumption.
169
+ This can look something like this (the `authorize_success` and `authorize_failure`
170
+ methods are specific to your application):
219
171
 
220
172
  ```ruby
221
173
  def consume
@@ -228,20 +180,24 @@ def consume
228
180
  session[:attributes] = response.attributes
229
181
  else
230
182
  authorize_failure # This method shows an error message
183
+ # List of errors is available in response.errors array
231
184
  end
232
185
  end
233
186
  ```
234
187
 
235
- In the above there are a few assumptions, one being that `response.nameid` is an email address. This is all handled with how you specify the settings that are in play via the `saml_settings` method. That could be implemented along the lines of this:
188
+ In the above there are a few assumptions, one being that `response.nameid` is an email address.
189
+ This is all handled with how you specify the settings that are in play via the `saml_settings` method.
190
+ That could be implemented along the lines of this:
236
191
 
237
192
  ```
238
193
  response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
239
194
  response.settings = saml_settings
240
195
  ```
241
196
 
242
- If the assertion of the SAMLResponse is not encrypted, you can initialize the Response without the `:settings` parameter and set it later.
243
- If the SAMLResponse contains an encrypted assertion, you need to provide the settings in the
244
- initialize method in order to obtain the decrypted assertion, using the service provider private key in order to decrypt.
197
+ If the assertion of the SAMLResponse is not encrypted, you can initialize the Response
198
+ without the `:settings` parameter and set it later. If the SAMLResponse contains an encrypted
199
+ assertion, you need to provide the settings in the initialize method in order to obtain the
200
+ decrypted assertion, using the service provider private key in order to decrypt.
245
201
  If you don't know what expect, always use the former (set the settings on initialize).
246
202
 
247
203
  ```ruby
@@ -251,8 +207,10 @@ def saml_settings
251
207
  settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
252
208
  settings.sp_entity_id = "http://#{request.host}/saml/metadata"
253
209
  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}"
210
+ settings.idp_sso_service_url = "https://app.onelogin.com/trust/saml2/http-post/sso/#{OneLoginAppId}"
211
+ settings.idp_sso_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" # or :post, :redirect
212
+ settings.idp_slo_service_url = "https://app.onelogin.com/trust/saml2/http-redirect/slo/#{OneLoginAppId}"
213
+ settings.idp_slo_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" # or :post, :redirect
256
214
  settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
257
215
  settings.idp_cert_fingerprint_algorithm = "http://www.w3.org/2000/09/xmldsig#sha1"
258
216
  settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
@@ -265,26 +223,30 @@ def saml_settings
265
223
  "urn:oasis:names:tc:SAML:2.0:ac:classes:Password"
266
224
  ]
267
225
 
268
- # Optional bindings (defaults to Redirect for logout POST for acs)
269
- settings.single_logout_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
270
- settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
226
+ # Optional bindings (defaults to Redirect for logout POST for ACS)
227
+ settings.single_logout_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" # or :post, :redirect
228
+ settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" # or :post, :redirect
271
229
 
272
230
  settings
273
231
  end
274
232
  ```
275
233
 
276
- The use of settings.issuer is deprecated in favour of settings.sp_entity_id
234
+ The use of settings.issuer is deprecated in favour of settings.sp_entity_id since version 1.11.0
277
235
 
278
- 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:
236
+ Some assertion validations can be skipped by passing parameters to `OneLogin::RubySaml::Response.new()`.
237
+ For example, you can skip the `AuthnStatement`, `Conditions`, `Recipient`, or the `SubjectConfirmation`
238
+ validations by initializing the response with different options:
279
239
 
280
240
  ```ruby
281
241
  response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_authnstatement: true}) # skips AuthnStatement
282
242
  response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_conditions: true}) # skips conditions
283
243
  response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_subject_confirmation: true}) # skips subject confirmation
284
- 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
244
+ response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_recipient_check: true}) # doesn't skip subject confirmation, but skips the recipient check which is a sub check of the subject_confirmation check
245
+ response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_audience: true}) # skips audience check
285
246
  ```
286
247
 
287
- 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:
248
+ All that's left is to wrap everything in a controller and reference it in the initialization and
249
+ consumption URLs in OneLogin. A full controller example could look like this:
288
250
 
289
251
  ```ruby
290
252
  # This controller expects you to use the URLs /saml/init and /saml/consume in your OneLogin application.
@@ -305,6 +267,7 @@ class SamlController < ApplicationController
305
267
  session[:attributes] = response.attributes
306
268
  else
307
269
  authorize_failure # This method shows an error message
270
+ # List of errors is available in response.errors array
308
271
  end
309
272
  end
310
273
 
@@ -315,7 +278,7 @@ class SamlController < ApplicationController
315
278
 
316
279
  settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
317
280
  settings.sp_entity_id = "http://#{request.host}/saml/metadata"
318
- settings.idp_sso_target_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}"
281
+ settings.idp_sso_service_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}"
319
282
  settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
320
283
  settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
321
284
 
@@ -336,44 +299,56 @@ class SamlController < ApplicationController
336
299
  end
337
300
  ```
338
301
 
302
+ ## Signature Validation
339
303
 
340
- ## Signature validation
304
+ Ruby SAML allows different ways to validate the signature of the SAMLResponse:
305
+ - You can provide the IdP X.509 public certificate at the `idp_cert` setting.
306
+ - You can provide the IdP X.509 public certificate in fingerprint format using the
307
+ `idp_cert_fingerprint` setting parameter and additionally the `idp_cert_fingerprint_algorithm` parameter.
341
308
 
342
- On the ruby-saml toolkit there are different ways to validate the signature of the SAMLResponse:
343
- - You can provide the IdP x509 public certificate at the 'idp_cert' setting.
344
- - You can provide the IdP x509 public certificate in fingerprint format using the 'idp_cert_fingerprint' setting parameter and additionally the 'idp_cert_fingerprint_algorithm' parameter.
309
+ When validating the signature of redirect binding, the fingerprint is useless and the certificate
310
+ of the IdP is required in order to execute the validation. You can pass the option
311
+ `:relax_signature_validation` to `SloLogoutrequest` and `Logoutresponse` if want to avoid signature
312
+ validation if no certificate of the IdP is provided.
345
313
 
346
- When validating the signature of redirect binding, the fingerprint is useless and the certficate of the IdP is required in order to execute the validation.
347
- You can pass the option :relax_signature_validation to SloLogoutrequest and Logoutresponse if want to avoid signature validation if no certificate of the IdP is provided.
314
+ In production also we highly recommend to register on the settings the IdP certificate instead
315
+ of using the fingerprint method. The fingerprint, is a hash, so at the end is open to a collision
316
+ attack that can end on a signature validation bypass. Other SAML toolkits deprecated that mechanism,
317
+ we maintain it for compatibility and also to be used on test environment.
348
318
 
349
- In production also we highly recommend to register on the settings the IdP certificate instead of using the fingerprint method. The fingerprint, is a hash, so at the end is open to a collision attack that can end on a signature validation bypass. Other SAML toolkits deprecated that mechanism, we maintain it for compatibility and also to be used on test environment.
319
+ ## Handling Multiple IdP Certificates
350
320
 
351
- In some scenarios the IdP uses different certificates for signing/encryption, or is under key rollover phase and more than one certificate is published on IdP metadata.
321
+ If the IdP metadata XML includes multiple certificates, you may specify the `idp_cert_multi`
322
+ parameter. When used, the `idp_cert` and `idp_cert_fingerprint` parameters are ignored.
323
+ This is useful in the following scenarios:
352
324
 
353
- In order to handle that the toolkit offers the 'idp_cert_multi' parameter.
354
- When used, 'idp_cert' and 'idp_cert_fingerprint' values are ignored.
325
+ * The IdP uses different certificates for signing versus encryption.
326
+ * The IdP is undergoing a key rollover and is publishing the old and new certificates in parallel.
355
327
 
356
- That 'idp_cert_multi' must be a Hash as follows:
328
+ The `idp_cert_multi` must be a `Hash` as follows. The `:signing` and `:encryption` arrays below,
329
+ add the IdP X.509 public certificates which were published in the IdP metadata.
330
+
331
+ ```ruby
357
332
  {
358
333
  :signing => [],
359
334
  :encryption => []
360
335
  }
361
-
362
- And on 'signing' and 'encryption' arrays, add the different IdP x509 public certificates published on the IdP metadata.
363
-
336
+ ```
364
337
 
365
338
  ## Metadata Based Configuration
366
339
 
367
- The method above requires a little extra work to manually specify attributes about the IdP. (And your SP application) There's an easier method -- use a metadata exchange. Metadata is just an XML file that defines the capabilities of both the IdP and the SP application. It also contains the X.509 public
368
- key certificates which add to the trusted relationship. The IdP administrator can also configure custom settings for an SP based on the metadata.
340
+ The method above requires a little extra work to manually specify attributes about both the IdP and your SP application.
341
+ There's an easier method: use a metadata exchange. Metadata is an XML file that defines the capabilities of both the IdP
342
+ and the SP application. It also contains the X.509 public key certificates which add to the trusted relationship.
343
+ The IdP administrator can also configure custom settings for an SP based on the metadata.
369
344
 
370
- Using ```idp_metadata_parser.parse_remote``` IdP metadata will be added to the settings without further ado.
345
+ Using `IdpMetadataParser#parse_remote`, the IdP metadata will be added to the settings.
371
346
 
372
347
  ```ruby
373
348
  def saml_settings
374
349
 
375
350
  idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
376
- # Returns OneLogin::RubySaml::Settings prepopulated with idp metadata
351
+ # Returns OneLogin::RubySaml::Settings pre-populated with IdP metadata
377
352
  settings = idp_metadata_parser.parse_remote("https://example.com/auth/saml2/idp/metadata")
378
353
 
379
354
  settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
@@ -385,11 +360,12 @@ def saml_settings
385
360
  settings
386
361
  end
387
362
  ```
363
+
388
364
  The following attributes are set:
389
365
  * idp_entity_id
390
366
  * name_identifier_format
391
- * idp_sso_target_url
392
- * idp_slo_target_url
367
+ * idp_sso_service_url
368
+ * idp_slo_service_url
393
369
  * idp_attribute_names
394
370
  * idp_cert
395
371
  * idp_cert_fingerprint
@@ -403,11 +379,11 @@ IdpMetadataParser by its Entity Id value:
403
379
 
404
380
  ```ruby
405
381
  validate_cert = true
406
- settings = idp_metadata_parser.parse_remote(
407
- "https://example.com/auth/saml2/idp/metadata",
408
- validate_cert,
409
- entity_id: "http//example.com/target/entity"
410
- )
382
+ settings = idp_metadata_parser.parse_remote(
383
+ "https://example.com/auth/saml2/idp/metadata",
384
+ validate_cert,
385
+ entity_id: "http//example.com/target/entity"
386
+ )
411
387
  ```
412
388
 
413
389
  ### Parsing Metadata into an Hash
@@ -422,7 +398,7 @@ If you are using `saml:AttributeStatement` to transfer data like the username, y
422
398
  `single_value_compatibility` (when activated, only the first value is returned)
423
399
 
424
400
  ```ruby
425
- response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
401
+ response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
426
402
  response.settings = saml_settings
427
403
 
428
404
  response.attributes[:username]
@@ -455,6 +431,9 @@ Imagine this `saml:AttributeStatement`
455
431
  <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
456
432
  <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="1"/>
457
433
  </saml:Attribute>
434
+ <saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname">
435
+ <saml:AttributeValue>usersName</saml:AttributeValue>
436
+ </saml:Attribute>
458
437
  </saml:AttributeStatement>
459
438
  ```
460
439
 
@@ -465,7 +444,8 @@ pp(response.attributes) # is an OneLogin::RubySaml::Attributes object
465
444
  "another_value"=>["value1", "value2"],
466
445
  "role"=>["role1", "role2", "role3"],
467
446
  "attribute_with_nil_value"=>[nil],
468
- "attribute_with_nils_and_empty_strings"=>["", "valuePresent", nil, nil]}>
447
+ "attribute_with_nils_and_empty_strings"=>["", "valuePresent", nil, nil]
448
+ "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"=>["usersName"]}>
469
449
 
470
450
  # Active single_value_compatibility
471
451
  OneLogin::RubySaml::Attributes.single_value_compatibility = true
@@ -482,6 +462,9 @@ pp(response.attributes.single(:role))
482
462
  pp(response.attributes.multi(:role))
483
463
  # => ["role1", "role2", "role3"]
484
464
 
465
+ pp(response.attributes.fetch(:role))
466
+ # => "role1"
467
+
485
468
  pp(response.attributes[:attribute_with_nil_value])
486
469
  # => nil
487
470
 
@@ -497,7 +480,10 @@ pp(response.attributes.single(:not_exists))
497
480
  pp(response.attributes.multi(:not_exists))
498
481
  # => nil
499
482
 
500
- # Deactive single_value_compatibility
483
+ pp(response.attributes.fetch(/givenname/))
484
+ # => "usersName"
485
+
486
+ # Deprecated single_value_compatibility
501
487
  OneLogin::RubySaml::Attributes.single_value_compatibility = false
502
488
 
503
489
  pp(response.attributes[:uid])
@@ -512,6 +498,9 @@ pp(response.attributes.single(:role))
512
498
  pp(response.attributes.multi(:role))
513
499
  # => ["role1", "role2", "role3"]
514
500
 
501
+ pp(response.attributes.fetch(:role))
502
+ # => ["role1", "role2", "role3"]
503
+
515
504
  pp(response.attributes[:attribute_with_nil_value])
516
505
  # => [nil]
517
506
 
@@ -526,82 +515,181 @@ pp(response.attributes.single(:not_exists))
526
515
 
527
516
  pp(response.attributes.multi(:not_exists))
528
517
  # => nil
518
+
519
+ pp(response.attributes.fetch(/givenname/))
520
+ # => ["usersName"]
529
521
  ```
530
522
 
531
523
  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
524
  To add a `saml:AuthnContextDeclRef`, define `settings.authn_context_decl_ref`.
533
525
 
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
526
+ 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
527
  building the authrequest object.
536
528
 
529
+ ## Service Provider Metadata
530
+
531
+ To form a trusted pair relationship with the IdP, the SP (you) need to provide metadata XML
532
+ to the IdP for various good reasons. (Caching, certificate lookups, relaying party permissions, etc)
537
533
 
538
- ## Signing
534
+ The class `OneLogin::RubySaml::Metadata` takes care of this by reading the Settings and returning XML. All you have to do is add a controller to return the data, then give this URL to the IdP administrator.
539
535
 
540
- The Ruby Toolkit supports 2 different kinds of signature: Embeded and `GET` parameters
536
+ The metadata will be polled by the IdP every few minutes, so updating your settings should propagate
537
+ to the IdP settings.
541
538
 
542
- In order to be able to sign, define the private key and the public cert of the service provider:
539
+ ```ruby
540
+ class SamlController < ApplicationController
541
+ # ... the rest of your controller definitions ...
542
+ def metadata
543
+ settings = Account.get_saml_settings
544
+ meta = OneLogin::RubySaml::Metadata.new
545
+ render :xml => meta.generate(settings), :content_type => "application/samlmetadata+xml"
546
+ end
547
+ end
548
+ ```
549
+
550
+ You can add `ValidUntil` and `CacheDuration` to the SP Metadata XML using instead:
543
551
 
544
552
  ```ruby
545
- settings.certificate = "CERTIFICATE TEXT WITH HEAD AND FOOT"
546
- settings.private_key = "PRIVATE KEY TEXT WITH HEAD AND FOOT"
553
+ # Valid until => 2 days from now
554
+ # Cache duration = 604800s = 1 week
555
+ valid_until = Time.now + 172800
556
+ cache_duration = 604800
557
+ meta.generate(settings, false, valid_until, cache_duration)
547
558
  ```
548
559
 
549
- The settings related to sign are stored in the `security` attribute of the settings:
560
+ ## Signing and Decryption
561
+
562
+ Ruby SAML supports the following functionality:
563
+
564
+ 1. Signing your SP Metadata XML
565
+ 2. Signing your SP SAML messages
566
+ 3. Decrypting IdP Assertion messages upon receipt (EncryptedAssertion)
567
+ 4. Verifying signatures on SAML messages and IdP Assertions
568
+
569
+ In order to use functions 1-3 above, you must first define your SP public certificate and private key:
550
570
 
551
571
  ```ruby
552
- settings.security[:authn_requests_signed] = true # Enable or not signature on AuthNRequest
553
- settings.security[:logout_requests_signed] = true # Enable or not signature on Logout Request
554
- settings.security[:logout_responses_signed] = true # Enable or not signature on Logout Response
555
- settings.security[:want_assertions_signed] = true # Enable or not the requirement of signed assertion
556
- settings.security[:metadata_signed] = true # Enable or not signature on Metadata
572
+ settings.certificate = "CERTIFICATE TEXT WITH BEGIN/END HEADER AND FOOTER"
573
+ settings.private_key = "PRIVATE KEY TEXT WITH BEGIN/END HEADER AND FOOTER"
574
+ ```
575
+
576
+ Note that the same certificate (and its associated private key) are used to perform
577
+ all decryption and signing-related functions (1-4) above. Ruby SAML does not currently allow
578
+ to specify different certificates for each function.
557
579
 
580
+ You may also globally set the SP signature and digest method, to be used in SP signing (functions 1 and 2 above):
581
+
582
+ ```ruby
558
583
  settings.security[:digest_method] = XMLSecurity::Document::SHA1
559
584
  settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
585
+ ```
560
586
 
561
- # Embeded signature or HTTP GET parameter signature
562
- # Note that metadata signature is always embedded regardless of this value.
563
- settings.security[:embed_sign] = false
564
- settings.security[:check_idp_cert_expiration] = false # Enable or not IdP x509 cert expiration check
565
- settings.security[:check_sp_cert_expiration] = false # Enable or not SP x509 cert expiration check
587
+ #### Signing SP Metadata
588
+
589
+ You may add a `<ds:Signature>` digital signature element to your SP Metadata XML using the following setting:
590
+
591
+ ```ruby
592
+ settings.certificate = "CERTIFICATE TEXT WITH BEGIN/END HEADER AND FOOTER"
593
+ settings.private_key = "PRIVATE KEY TEXT WITH BEGIN/END HEADER AND FOOTER"
594
+
595
+ settings.security[:metadata_signed] = true # Enable signature on Metadata
566
596
  ```
567
597
 
568
- Notice that the RelayState parameter is used when creating the Signature on the HTTP-Redirect Binding.
598
+ #### Signing SP SAML Messages
599
+
600
+ Ruby SAML supports SAML request signing. The Service Provider will sign the
601
+ request/responses with its private key. The Identity Provider will then validate the signature
602
+ of the received request/responses with the public X.509 cert of the Service Provider.
603
+
604
+ To enable, please first set your certificate and private key. This will add `<md:KeyDescriptor use="signing">`
605
+ to your SP Metadata XML, to be read by the IdP.
606
+
607
+ ```ruby
608
+ settings.certificate = "CERTIFICATE TEXT WITH BEGIN/END HEADER AND FOOTER"
609
+ settings.private_key = "PRIVATE KEY TEXT WITH BEGIN/END HEADER AND FOOTER"
610
+ ```
611
+
612
+ Next, you may specify the specific SP SAML messages you would like to sign:
613
+
614
+ ```ruby
615
+ settings.security[:authn_requests_signed] = true # Enable signature on AuthNRequest
616
+ settings.security[:logout_requests_signed] = true # Enable signature on Logout Request
617
+ settings.security[:logout_responses_signed] = true # Enable signature on Logout Response
618
+ ```
619
+
620
+ Signatures will be handled automatically for both `HTTP-Redirect` and `HTTP-Redirect` Binding.
621
+ Note that the RelayState parameter is used when creating the Signature on the `HTTP-Redirect` Binding.
569
622
  Remember to provide it to the Signature builder if you are sending a `GET RelayState` parameter or the
570
623
  signature validation process will fail at the Identity Provider.
571
624
 
572
- 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
574
- Service Provider.
625
+ #### Decrypting IdP SAML Assertions
626
+
627
+ Ruby SAML supports EncryptedAssertion. The Identity Provider will encrypt the Assertion with the
628
+ public cert of the Service Provider. The Service Provider will decrypt the EncryptedAssertion with its private key.
629
+
630
+ You may enable EncryptedAssertion as follows. This will add `<md:KeyDescriptor use="encrytion">` to your
631
+ SP Metadata XML, to be read by the IdP.
632
+
633
+ ```ruby
634
+ settings.certificate = "CERTIFICATE TEXT WITH BEGIN/END HEADER AND FOOTER"
635
+ settings.private_key = "PRIVATE KEY TEXT WITH BEGIN/END HEADER AND FOOTER"
636
+
637
+ settings.security[:want_assertions_encrypted] = true # Invalidate SAML messages without an EncryptedAssertion
638
+ ```
575
639
 
576
- Notice that this toolkit uses 'settings.certificate' and 'settings.private_key' for the sign and decrypt processes.
640
+ #### Verifying Signature on IdP Assertions
577
641
 
578
- Enable/disable the soft mode with the `settings.soft` parameter. When set to `false`, saml validations errors will raise an exception.
642
+ You may require the IdP to sign its SAML Assertions using the following setting.
643
+ With will add `<md:SPSSODescriptor WantAssertionsSigned="true">` to your SP Metadata XML.
644
+ The signature will be checked against the `<md:KeyDescriptor use="signing">` element
645
+ present in the IdP's metadata.
579
646
 
580
- ## Decrypting
647
+ ```ruby
648
+ settings.security[:want_assertions_signed] = true # Require the IdP to sign its SAML Assertions
649
+ ```
581
650
 
582
- The Ruby Toolkit supports EncryptedAssertion.
651
+ #### Certificate and Signature Validation
583
652
 
584
- In order to be able to decrypt a SAML Response that contains a EncryptedAssertion you need define the private key and the public cert of the service provider, then share this with the Identity Provider.
653
+ You may require SP and IdP certificates to be non-expired using the following settings:
585
654
 
586
655
  ```ruby
587
- settings.certificate = "CERTIFICATE TEXT WITH HEAD AND FOOT"
588
- settings.private_key = "PRIVATE KEY TEXT WITH HEAD AND FOOT"
656
+ settings.security[:check_idp_cert_expiration] = true # Raise error if IdP X.509 cert is expired
657
+ settings.security[:check_sp_cert_expiration] = true # Raise error SP X.509 cert is expired
589
658
  ```
590
659
 
591
- The Identity Provider will encrypt the Assertion with the public cert of the Service Provider.
592
- The Service Provider will decrypt the EncryptedAssertion with its private key.
660
+ By default, Ruby SAML will raise a `OneLogin::RubySaml::ValidationError` if a signature or certificate
661
+ validation fails. You may disable such exceptions using the `settings.security[:soft]` parameter.
593
662
 
594
- Notice that this toolkit uses 'settings.certificate' and 'settings.private_key' for the sign and decrypt processes.
663
+ ```ruby
664
+ settings.security[:soft] = true # Do not raise error on failed signature/certificate validations
665
+ ```
595
666
 
667
+ #### Key Rollover
596
668
 
597
- ## Key rollover
669
+ To update the SP X.509 certificate and private key without disruption of service, you may define the parameter
670
+ `settings.certificate_new`. This will publish the new SP certificate in your metadata so that your IdP counterparties
671
+ may cache it in preparation for rollover.
598
672
 
599
- If you plan to update the SP x509cert and privateKey you can define the parameter 'certificate_new' at the settings and that new SP public certificate will be published on the SP metadata so Identity Providers can read them and get ready for rollover.
673
+ For example, if you to rollover from `CERT A` to `CERT B`. Before rollover, your settings should look as follows.
674
+ Both `CERT A` and `CERT B` will now appear in your SP metadata, however `CERT A` will still be used for signing
675
+ and encryption at this time.
600
676
 
677
+ ```ruby
678
+ settings.certificate = "CERT A"
679
+ settings.private_key = "PRIVATE KEY FOR CERT A"
680
+ settings.certificate_new = "CERT B"
681
+ ```
682
+
683
+ After the IdP has cached `CERT B`, you may then change your settings as follows:
684
+
685
+ ```ruby
686
+ settings.certificate = "CERT B"
687
+ settings.private_key = "PRIVATE KEY FOR CERT B"
688
+ ```
601
689
 
602
690
  ## Single Log Out
603
691
 
604
- The Ruby Toolkit supports SP-initiated Single Logout and IdP-Initiated Single Logout.
692
+ Ruby SAML supports SP-initiated Single Logout and IdP-Initiated Single Logout.
605
693
 
606
694
  Here is an example that we could add to our previous controller to generate and send a SAML Logout Request to the IdP:
607
695
 
@@ -611,22 +699,28 @@ def sp_logout_request
611
699
  # LogoutRequest accepts plain browser requests w/o paramters
612
700
  settings = saml_settings
613
701
 
614
- if settings.idp_slo_target_url.nil?
702
+ if settings.idp_slo_service_url.nil?
615
703
  logger.info "SLO IdP Endpoint not found in settings, executing then a normal logout'"
616
704
  delete_session
617
705
  else
618
706
 
619
- # Since we created a new SAML request, save the transaction_id
620
- # to compare it with the response we get back
621
- 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]}'"
707
+ logout_request = OneLogin::RubySaml::Logoutrequest.new
708
+ logger.info "New SP SLO for userid '#{session[:userid]}' transactionid '#{logout_request.uuid}'"
624
709
 
625
710
  if settings.name_identifier_value.nil?
626
711
  settings.name_identifier_value = session[:userid]
627
712
  end
628
713
 
629
- relayState = url_for controller: 'saml', action: 'index'
714
+ # Ensure user is logged out before redirect to IdP, in case anything goes wrong during single logout process (as recommended by saml2int [SDP-SP34])
715
+ logged_user = session[:userid]
716
+ logger.info "Delete session for '#{session[:userid]}'"
717
+ delete_session
718
+
719
+ # Save the transaction_id to compare it with the response we get back
720
+ session[:transaction_id] = logout_request.uuid
721
+ session[:logged_out_user] = logged_user
722
+
723
+ relayState = url_for(controller: 'saml', action: 'index')
630
724
  redirect_to(logout_request.create(settings, :RelayState => relayState))
631
725
  end
632
726
  end
@@ -653,7 +747,7 @@ def process_logout_response
653
747
  logger.error "The SAML Logout Response is invalid"
654
748
  else
655
749
  # Actually log out this session
656
- logger.info "Delete session for '#{session[:userid]}'"
750
+ logger.info "SLO completed for '#{session[:logged_out_user]}'"
657
751
  delete_session
658
752
  end
659
753
  end
@@ -662,6 +756,8 @@ end
662
756
  def delete_session
663
757
  session[:userid] = nil
664
758
  session[:attributes] = nil
759
+ session[:transaction_id] = nil
760
+ session[:logged_out_user] = nil
665
761
  end
666
762
  ```
667
763
 
@@ -674,7 +770,7 @@ def idp_logout_request
674
770
  logout_request = OneLogin::RubySaml::SloLogoutrequest.new(params[:SAMLRequest])
675
771
  if !logout_request.is_valid?
676
772
  logger.error "IdP initiated LogoutRequest was not valid!"
677
- render :inline => logger.error
773
+ return render :inline => logger.error
678
774
  end
679
775
  logger.info "IdP initiated Logout for #{logout_request.name_id}"
680
776
 
@@ -706,30 +802,6 @@ def logout
706
802
  end
707
803
  ```
708
804
 
709
-
710
-
711
- ## Service Provider Metadata
712
-
713
- To form a trusted pair relationship with the IdP, the SP (you) need to provide metadata XML
714
- to the IdP for various good reasons. (Caching, certificate lookups, relaying party permissions, etc)
715
-
716
- The class `OneLogin::RubySaml::Metadata` takes care of this by reading the Settings and returning XML. All you have to do is add a controller to return the data, then give this URL to the IdP administrator.
717
-
718
- The metadata will be polled by the IdP every few minutes, so updating your settings should propagate
719
- to the IdP settings.
720
-
721
- ```ruby
722
- class SamlController < ApplicationController
723
- # ... the rest of your controller definitions ...
724
- def metadata
725
- settings = Account.get_saml_settings
726
- meta = OneLogin::RubySaml::Metadata.new
727
- render :xml => meta.generate(settings), :content_type => "application/samlmetadata+xml"
728
- end
729
- end
730
- ```
731
-
732
-
733
805
  ## Clock Drift
734
806
 
735
807
  Server clocks tend to drift naturally. If during validation of the response you get the error "Current time is earlier than NotBefore condition", this may be due to clock differences between your system and that of the Identity Provider.
@@ -744,13 +816,33 @@ response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], :allowed_cloc
744
816
 
745
817
  Make sure to keep the value as comfortably small as possible to keep security risks to a minimum.
746
818
 
819
+ ## Deflation Limit
820
+
821
+ To protect against decompression bombs (a form of DoS attack), SAML messages are limited to 250,000 bytes by default.
822
+ Sometimes legitimate SAML messages will exceed this limit,
823
+ for example due to custom claims like including groups a user is a member of.
824
+ If you want to customize this limit, you need to provide a different setting when initializing the response object.
825
+ Example:
826
+
827
+ ```ruby
828
+ def consume
829
+ response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], { settings: saml_settings })
830
+ ...
831
+ end
832
+
833
+ private
834
+
835
+ def saml_settings
836
+ OneLogin::RubySaml::Settings.new(message_max_bytesize: 500_000)
837
+ end
838
+ ```
839
+
747
840
  ## Attribute Service
748
841
 
749
842
  To request attributes from the IdP the SP needs to provide an attribute service within it's metadata and reference the index in the assertion.
750
843
 
751
844
  ```ruby
752
845
  settings = OneLogin::RubySaml::Settings.new
753
-
754
846
  settings.attributes_index = 5
755
847
  settings.attribute_consuming_service.configure do
756
848
  service_name "Service"
@@ -761,3 +853,27 @@ end
761
853
  ```
762
854
 
763
855
  The `attribute_value` option additionally accepts an array of possible values.
856
+
857
+ ## Custom Metadata Fields
858
+
859
+ Some IdPs may require to add SPs to add additional fields (Organization, ContactPerson, etc.)
860
+ into the SP metadata. This can be achieved by extending the `OneLogin::RubySaml::Metadata`
861
+ class and overriding the `#add_extras` method as per the following example:
862
+
863
+ ```ruby
864
+ class MyMetadata < OneLogin::RubySaml::Metadata
865
+ def add_extras(root, _settings)
866
+ org = root.add_element("md:Organization")
867
+ org.add_element("md:OrganizationName", 'xml:lang' => "en-US").text = 'ACME Inc.'
868
+ org.add_element("md:OrganizationDisplayName", 'xml:lang' => "en-US").text = 'ACME'
869
+ org.add_element("md:OrganizationURL", 'xml:lang' => "en-US").text = 'https://www.acme.com'
870
+
871
+ cp = root.add_element("md:ContactPerson", 'contactType' => 'technical')
872
+ cp.add_element("md:GivenName").text = 'ACME SAML Team'
873
+ cp.add_element("md:EmailAddress").text = 'saml@acme.com'
874
+ end
875
+ end
876
+
877
+ # Output XML with custom metadata
878
+ MyMetadata.new.generate(settings)
879
+ ```