ruby-saml 1.10.1 → 1.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (159) hide show
  1. checksums.yaml +7 -7
  2. data/.travis.yml +21 -20
  3. data/README.md +95 -26
  4. data/changelog.md +40 -0
  5. data/lib/onelogin/ruby-saml/attributes.rb +24 -1
  6. data/lib/onelogin/ruby-saml/authrequest.rb +11 -6
  7. data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +70 -24
  8. data/lib/onelogin/ruby-saml/logging.rb +3 -3
  9. data/lib/onelogin/ruby-saml/logoutrequest.rb +11 -5
  10. data/lib/onelogin/ruby-saml/logoutresponse.rb +21 -2
  11. data/lib/onelogin/ruby-saml/metadata.rb +11 -3
  12. data/lib/onelogin/ruby-saml/response.rb +64 -23
  13. data/lib/onelogin/ruby-saml/saml_message.rb +6 -0
  14. data/lib/onelogin/ruby-saml/setting_error.rb +6 -0
  15. data/lib/onelogin/ruby-saml/settings.rb +72 -7
  16. data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +20 -1
  17. data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +31 -17
  18. data/lib/onelogin/ruby-saml/utils.rb +69 -0
  19. data/lib/onelogin/ruby-saml/version.rb +1 -1
  20. data/lib/xml_security.rb +34 -6
  21. data/ruby-saml.gemspec +9 -5
  22. metadata +161 -383
  23. data/test/certificates/certificate.der +0 -0
  24. data/test/certificates/certificate1 +0 -12
  25. data/test/certificates/certificate_without_head_foot +0 -1
  26. data/test/certificates/formatted_certificate +0 -14
  27. data/test/certificates/formatted_chained_certificate +0 -42
  28. data/test/certificates/formatted_private_key +0 -12
  29. data/test/certificates/formatted_rsa_private_key +0 -12
  30. data/test/certificates/invalid_certificate1 +0 -1
  31. data/test/certificates/invalid_certificate2 +0 -1
  32. data/test/certificates/invalid_certificate3 +0 -12
  33. data/test/certificates/invalid_chained_certificate1 +0 -1
  34. data/test/certificates/invalid_private_key1 +0 -1
  35. data/test/certificates/invalid_private_key2 +0 -1
  36. data/test/certificates/invalid_private_key3 +0 -10
  37. data/test/certificates/invalid_rsa_private_key1 +0 -1
  38. data/test/certificates/invalid_rsa_private_key2 +0 -1
  39. data/test/certificates/invalid_rsa_private_key3 +0 -10
  40. data/test/certificates/ruby-saml-2.crt +0 -15
  41. data/test/certificates/ruby-saml.crt +0 -14
  42. data/test/certificates/ruby-saml.key +0 -15
  43. data/test/idp_metadata_parser_test.rb +0 -587
  44. data/test/logging_test.rb +0 -62
  45. data/test/logout_requests/invalid_slo_request.xml +0 -6
  46. data/test/logout_requests/slo_request.xml +0 -4
  47. data/test/logout_requests/slo_request.xml.base64 +0 -1
  48. data/test/logout_requests/slo_request_deflated.xml.base64 +0 -1
  49. data/test/logout_requests/slo_request_with_name_id_format.xml +0 -4
  50. data/test/logout_requests/slo_request_with_session_index.xml +0 -5
  51. data/test/logout_responses/logoutresponse_fixtures.rb +0 -86
  52. data/test/logoutrequest_test.rb +0 -260
  53. data/test/logoutresponse_test.rb +0 -409
  54. data/test/metadata/idp_descriptor.xml +0 -26
  55. data/test/metadata/idp_descriptor_2.xml +0 -56
  56. data/test/metadata/idp_descriptor_3.xml +0 -14
  57. data/test/metadata/idp_descriptor_4.xml +0 -72
  58. data/test/metadata/idp_metadata_different_sign_and_encrypt_cert.xml +0 -72
  59. data/test/metadata/idp_metadata_multi_certs.xml +0 -75
  60. data/test/metadata/idp_metadata_multi_signing_certs.xml +0 -52
  61. data/test/metadata/idp_metadata_same_sign_and_encrypt_cert.xml +0 -71
  62. data/test/metadata/idp_multiple_descriptors.xml +0 -59
  63. data/test/metadata/idp_multiple_descriptors_2.xml +0 -59
  64. data/test/metadata/no_idp_descriptor.xml +0 -21
  65. data/test/metadata_test.rb +0 -331
  66. data/test/request_test.rb +0 -340
  67. data/test/response_test.rb +0 -1620
  68. data/test/responses/adfs_response_sha1.xml +0 -46
  69. data/test/responses/adfs_response_sha256.xml +0 -46
  70. data/test/responses/adfs_response_sha384.xml +0 -46
  71. data/test/responses/adfs_response_sha512.xml +0 -46
  72. data/test/responses/adfs_response_xmlns.xml +0 -45
  73. data/test/responses/attackxee.xml +0 -13
  74. data/test/responses/invalids/duplicated_attributes.xml.base64 +0 -1
  75. data/test/responses/invalids/empty_destination.xml.base64 +0 -1
  76. data/test/responses/invalids/empty_nameid.xml.base64 +0 -1
  77. data/test/responses/invalids/encrypted_new_attack.xml.base64 +0 -1
  78. data/test/responses/invalids/invalid_audience.xml.base64 +0 -1
  79. data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +0 -1
  80. data/test/responses/invalids/invalid_issuer_message.xml.base64 +0 -1
  81. data/test/responses/invalids/invalid_signature_position.xml.base64 +0 -1
  82. data/test/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 +0 -1
  83. data/test/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 +0 -1
  84. data/test/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 +0 -1
  85. data/test/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 +0 -1
  86. data/test/responses/invalids/multiple_assertions.xml.base64 +0 -2
  87. data/test/responses/invalids/multiple_signed.xml.base64 +0 -1
  88. data/test/responses/invalids/no_authnstatement.xml.base64 +0 -1
  89. data/test/responses/invalids/no_conditions.xml.base64 +0 -1
  90. data/test/responses/invalids/no_id.xml.base64 +0 -1
  91. data/test/responses/invalids/no_issuer_assertion.xml.base64 +0 -1
  92. data/test/responses/invalids/no_issuer_response.xml.base64 +0 -1
  93. data/test/responses/invalids/no_nameid.xml.base64 +0 -1
  94. data/test/responses/invalids/no_saml2.xml.base64 +0 -1
  95. data/test/responses/invalids/no_signature.xml.base64 +0 -1
  96. data/test/responses/invalids/no_status.xml.base64 +0 -1
  97. data/test/responses/invalids/no_status_code.xml.base64 +0 -1
  98. data/test/responses/invalids/no_subjectconfirmation_data.xml.base64 +0 -1
  99. data/test/responses/invalids/no_subjectconfirmation_method.xml.base64 +0 -1
  100. data/test/responses/invalids/response_invalid_signed_element.xml.base64 +0 -1
  101. data/test/responses/invalids/response_with_concealed_signed_assertion.xml +0 -51
  102. data/test/responses/invalids/response_with_doubled_signed_assertion.xml +0 -49
  103. data/test/responses/invalids/signature_wrapping_attack.xml.base64 +0 -1
  104. data/test/responses/invalids/status_code_responder.xml.base64 +0 -1
  105. data/test/responses/invalids/status_code_responer_and_msg.xml.base64 +0 -1
  106. data/test/responses/invalids/wrong_spnamequalifier.xml.base64 +0 -1
  107. data/test/responses/no_signature_ns.xml +0 -48
  108. data/test/responses/open_saml_response.xml +0 -56
  109. data/test/responses/response_assertion_wrapped.xml.base64 +0 -93
  110. data/test/responses/response_audience_self_closed_tag.xml.base64 +0 -1
  111. data/test/responses/response_double_status_code.xml.base64 +0 -1
  112. data/test/responses/response_encrypted_attrs.xml.base64 +0 -1
  113. data/test/responses/response_encrypted_nameid.xml.base64 +0 -1
  114. data/test/responses/response_eval.xml +0 -7
  115. data/test/responses/response_no_cert_and_encrypted_attrs.xml +0 -29
  116. data/test/responses/response_node_text_attack.xml.base64 +0 -1
  117. data/test/responses/response_node_text_attack2.xml.base64 +0 -1
  118. data/test/responses/response_node_text_attack3.xml.base64 +0 -1
  119. data/test/responses/response_unsigned_xml_base64 +0 -1
  120. data/test/responses/response_with_ampersands.xml +0 -139
  121. data/test/responses/response_with_ampersands.xml.base64 +0 -93
  122. data/test/responses/response_with_ds_namespace_at_the_root.xml.base64 +0 -1
  123. data/test/responses/response_with_multiple_attribute_statements.xml +0 -72
  124. data/test/responses/response_with_multiple_attribute_values.xml +0 -67
  125. data/test/responses/response_with_retrieval_method.xml +0 -26
  126. data/test/responses/response_with_saml2_namespace.xml.base64 +0 -102
  127. data/test/responses/response_with_signed_assertion.xml.base64 +0 -66
  128. data/test/responses/response_with_signed_assertion_2.xml.base64 +0 -1
  129. data/test/responses/response_with_signed_assertion_3.xml +0 -30
  130. data/test/responses/response_with_signed_message_and_assertion.xml +0 -34
  131. data/test/responses/response_with_undefined_recipient.xml.base64 +0 -1
  132. data/test/responses/response_without_attributes.xml.base64 +0 -79
  133. data/test/responses/response_without_reference_uri.xml.base64 +0 -1
  134. data/test/responses/response_wrapped.xml.base64 +0 -150
  135. data/test/responses/signed_message_encrypted_signed_assertion.xml.base64 +0 -1
  136. data/test/responses/signed_message_encrypted_unsigned_assertion.xml.base64 +0 -1
  137. data/test/responses/signed_nameid_in_atts.xml +0 -47
  138. data/test/responses/signed_unqual_nameid_in_atts.xml +0 -47
  139. data/test/responses/simple_saml_php.xml +0 -71
  140. data/test/responses/starfield_response.xml.base64 +0 -1
  141. data/test/responses/test_sign.xml +0 -43
  142. data/test/responses/unsigned_encrypted_adfs.xml +0 -23
  143. data/test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64 +0 -1
  144. data/test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64 +0 -1
  145. data/test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64 +0 -1
  146. data/test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64 +0 -1
  147. data/test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64 +0 -1
  148. data/test/responses/unsigned_message_encrypted_signed_assertion.xml.base64 +0 -1
  149. data/test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64 +0 -1
  150. data/test/responses/valid_response.xml.base64 +0 -1
  151. data/test/responses/valid_response_with_formatted_x509certificate.xml.base64 +0 -1
  152. data/test/responses/valid_response_without_x509certificate.xml.base64 +0 -1
  153. data/test/saml_message_test.rb +0 -56
  154. data/test/settings_test.rb +0 -329
  155. data/test/slo_logoutrequest_test.rb +0 -448
  156. data/test/slo_logoutresponse_test.rb +0 -233
  157. data/test/test_helper.rb +0 -331
  158. data/test/utils_test.rb +0 -259
  159. data/test/xml_security_test.rb +0 -421
data/test/logging_test.rb DELETED
@@ -1,62 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
2
-
3
- require 'onelogin/ruby-saml/logging'
4
-
5
- class LoggingTest < Minitest::Test
6
-
7
- describe "Logging" do
8
- before do
9
- OneLogin::RubySaml::Logging.logger = nil
10
- end
11
-
12
- after do
13
- OneLogin::RubySaml::Logging.logger = ::TEST_LOGGER
14
- end
15
-
16
- describe "given no specific logging setup" do
17
- it "prints to stdout" do
18
- OneLogin::RubySaml::Logging::DEFAULT_LOGGER.expects(:debug).with('hi mom')
19
- OneLogin::RubySaml::Logging.debug('hi mom')
20
- end
21
- end
22
-
23
- describe "given a Rails app" do
24
- let(:logger) { mock('Logger') }
25
-
26
- before do
27
- ::Rails = mock('Rails module')
28
- ::Rails.stubs(:logger).returns(logger)
29
- end
30
-
31
- after do
32
- Object.instance_eval { remove_const(:Rails) }
33
- end
34
-
35
- it "delegates to Rails" do
36
- logger.expects(:debug).with('hi mom')
37
- logger.expects(:info).with('sup?')
38
-
39
- OneLogin::RubySaml::Logging.debug('hi mom')
40
- OneLogin::RubySaml::Logging.info('sup?')
41
- end
42
- end
43
-
44
- describe "given a specific Logger" do
45
- let(:logger) { mock('Logger') }
46
-
47
- before { OneLogin::RubySaml::Logging.logger = logger }
48
-
49
- after do
50
- OneLogin::RubySaml::Logging.logger = ::TEST_LOGGER
51
- end
52
-
53
- it "delegates to the object" do
54
- logger.expects(:debug).with('hi mom')
55
- logger.expects(:info).with('sup?')
56
-
57
- OneLogin::RubySaml::Logging.debug('hi mom')
58
- OneLogin::RubySaml::Logging.info('sup?')
59
- end
60
- end
61
- end
62
- end
@@ -1,6 +0,0 @@
1
- <samlp:LogoutRequest Version='2.0' ID='_c0348950-935b-0131-1060-782bcb56fcaa' xmlns:samlp='urn:oasis:names:tc:SAML:2.0:protocol' IssueInstant='2014-03-21T19:20:13'>
2
- <saml:Issuer xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion'>https://app.onelogin.com/saml/metadata/SOMEACCOUNT</saml:Issuer>
3
- <saml:Issuer xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion'>https://app.onelogin.com/saml/metadata/SOMEACCOUNT</saml:Issuer>
4
- <saml:NameID xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion'>someone@example.org</saml:NameID>
5
- <saml:NameID xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion'>someone2@example.org</saml:NameID>
6
- </samlp:LogoutRequest>
@@ -1,4 +0,0 @@
1
- <samlp:LogoutRequest Version='2.0' ID='_c0348950-935b-0131-1060-782bcb56fcaa' xmlns:samlp='urn:oasis:names:tc:SAML:2.0:protocol' IssueInstant='2014-03-21T19:20:13'>
2
- <saml:Issuer xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion'>https://app.onelogin.com/saml/metadata/SOMEACCOUNT</saml:Issuer>
3
- <saml:NameID xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion'>someone@example.org</saml:NameID>
4
- </samlp:LogoutRequest>
@@ -1 +0,0 @@
1
- PHNhbWxwOkxvZ291dFJlcXVlc3QgVmVyc2lvbj0nMi4wJyBJRD0nX2MwMzQ4OTUwLTkzNWItMDEzMS0xMDYwLTc4MmJjYjU2ZmNhYScgeG1sbnM6c2FtbHA9J3VybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCcgSXNzdWVJbnN0YW50PScyMDE0LTAzLTIxVDE5OjIwOjEzJz4NCiAgPHNhbWw6SXNzdWVyIHhtbG5zOnNhbWw9J3VybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24nPmh0dHBzOi8vYXBwLm9uZWxvZ2luLmNvbS9zYW1sL21ldGFkYXRhL1NPTUVBQ0NPVU5UPC9zYW1sOklzc3Vlcj4NCiAgPHNhbWw6TmFtZUlEIHhtbG5zOnNhbWw9J3VybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24nPnNvbWVvbmVAZXhhbXBsZS5vcmc8L3NhbWw6TmFtZUlEPg0KPC9zYW1scDpMb2dvdXRSZXF1ZXN0Pg==
@@ -1 +0,0 @@
1
- ndG7asMwFIDhvdB38KZJlmTHaSxs05B0COQCTdq1yKrqGqxLdWTI41dJOpgOHToKDv93DqpA6MHxre3sGJ7V16ggJK/KQ29NjbKUomSzrtGbpPlsURYUl3nRYspyhhmdU/ywyFrZFvMPKQRKznowwK/JGo3ecCugB26EVsCD5MflbstjlDtvg5V2iHWAUW0MBGFCBCmbYZrjjJ1YyTPKWY6a+7skqS5Rfh32E+ZvRQAoH+IlqPkMwQEnRDiXWqMG2/UmlVaTS4VoFcS7CIIcD7un5Wp1eNmfKjIhJzvsI7NZ/2cHsFpF+1GdhXaDSq3vfpBbMyK396//aL4B
@@ -1,4 +0,0 @@
1
- <samlp:LogoutRequest Version='2.0' ID='_c0348950-935b-0131-1060-782bcb56fcaa' xmlns:samlp='urn:oasis:names:tc:SAML:2.0:protocol' IssueInstant='2014-03-21T19:20:13'>
2
- <saml:Issuer xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion'>https://app.onelogin.com/saml/metadata/SOMEACCOUNT</saml:Issuer>
3
- <saml:NameID xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion' Format='urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'>someone@example.org</saml:NameID>
4
- </samlp:LogoutRequest>
@@ -1,5 +0,0 @@
1
- <samlp:LogoutRequest Version='2.0' ID='_c0348950-935b-0131-1060-782bcb56fcaa' xmlns:samlp='urn:oasis:names:tc:SAML:2.0:protocol' IssueInstant='2014-03-21T19:20:13'>
2
- <saml:Issuer xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion'>https://app.onelogin.com/saml/metadata/SOMEACCOUNT</saml:Issuer>
3
- <saml:NameID xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion'>someone@example.org</saml:NameID>
4
- <samlp:SessionIndex>_ea853497-c58a-408a-bc23-c849752d9741</samlp:SessionIndex>
5
- </samlp:LogoutRequest>
@@ -1,86 +0,0 @@
1
- #encoding: utf-8
2
-
3
- def default_logout_response_opts
4
- {
5
- :uuid => "_28024690-000e-0130-b6d2-38f6b112be8b",
6
- :issue_instant => Time.now.strftime('%Y-%m-%dT%H:%M:%SZ'),
7
- :settings => settings
8
- }
9
- end
10
-
11
- def valid_logout_response_document(opts = {})
12
- opts = default_logout_response_opts.merge(opts)
13
-
14
- "<samlp:LogoutResponse
15
- xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"
16
- ID=\"#{random_id}\" Version=\"2.0\"
17
- IssueInstant=\"#{opts[:issue_instant]}\"
18
- Destination=\"#{opts[:settings].single_logout_service_url}\"
19
- InResponseTo=\"#{opts[:uuid]}\">
20
- <saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">#{opts[:settings].issuer}</saml:Issuer>
21
- <samlp:Status xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\">
22
- <samlp:StatusCode xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"
23
- Value=\"urn:oasis:names:tc:SAML:2.0:status:Success\">
24
- </samlp:StatusCode>
25
- </samlp:Status>
26
- </samlp:LogoutResponse>"
27
- end
28
-
29
- def unsuccessful_logout_response_document(opts = {})
30
- opts = default_logout_response_opts.merge(opts)
31
-
32
- "<samlp:LogoutResponse
33
- xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"
34
- ID=\"#{random_id}\" Version=\"2.0\"
35
- IssueInstant=\"#{opts[:issue_instant]}\"
36
- Destination=\"#{opts[:settings].single_logout_service_url}\"
37
- InResponseTo=\"#{opts[:uuid]}\">
38
- <saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">#{opts[:settings].issuer}</saml:Issuer>
39
- <samlp:Status xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\">
40
- <samlp:StatusCode xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"
41
- Value=\"urn:oasis:names:tc:SAML:2.0:status:Requester\">
42
- </samlp:StatusCode>
43
- </samlp:Status>
44
- </samlp:LogoutResponse>"
45
- end
46
-
47
- def unsuccessful_logout_response_with_message_document(opts = {})
48
- opts = default_logout_response_opts.merge(opts)
49
-
50
- "<samlp:LogoutResponse
51
- xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"
52
- ID=\"#{random_id}\" Version=\"2.0\"
53
- IssueInstant=\"#{opts[:issue_instant]}\"
54
- Destination=\"#{opts[:settings].single_logout_service_url}\"
55
- InResponseTo=\"#{opts[:uuid]}\">
56
- <saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">#{opts[:settings].issuer}</saml:Issuer>
57
- <samlp:Status xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\">
58
- <samlp:StatusCode xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"
59
- Value=\"urn:oasis:names:tc:SAML:2.0:status:Requester\">
60
- </samlp:StatusCode>
61
- <samlp:StatusMessage>Logoutrequest expired</samlp:StatusMessage>
62
- </samlp:Status>
63
- </samlp:LogoutResponse>"
64
- end
65
-
66
- def invalid_xml_logout_response_document
67
- "<samlp:SomethingAwful
68
- xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"
69
- ID=\"#{random_id}\" Version=\"2.0\">
70
- </samlp:SomethingAwful>"
71
- end
72
-
73
- def settings
74
- @settings ||= OneLogin::RubySaml::Settings.new(
75
- {
76
- :assertion_consumer_service_url => "http://app.muda.no/sso/consume",
77
- :single_logout_service_url => "http://app.muda.no/sso/consume_logout",
78
- :issuer => "http://app.muda.no",
79
- :sp_name_qualifier => "http://sso.muda.no",
80
- :idp_sso_target_url => "http://sso.muda.no/sso",
81
- :idp_slo_target_url => "http://sso.muda.no/slo",
82
- :idp_cert_fingerprint => "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00",
83
- :name_identifier_format => "urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
84
- }
85
- )
86
- end
@@ -1,260 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
2
-
3
- require 'onelogin/ruby-saml/logoutrequest'
4
-
5
- class RequestTest < Minitest::Test
6
-
7
- describe "Logoutrequest" do
8
- let(:settings) { OneLogin::RubySaml::Settings.new }
9
-
10
- before do
11
- settings.idp_slo_target_url = "http://unauth.com/logout"
12
- settings.name_identifier_value = "f00f00"
13
- end
14
-
15
- it "create the deflated SAMLRequest URL parameter" do
16
- unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings)
17
- assert_match /^http:\/\/unauth\.com\/logout\?SAMLRequest=/, unauth_url
18
-
19
- inflated = decode_saml_request_payload(unauth_url)
20
- assert_match /^<samlp:LogoutRequest/, inflated
21
- end
22
-
23
- it "support additional params" do
24
- unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :hello => nil })
25
- assert_match /&hello=$/, unauth_url
26
-
27
- unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :foo => "bar" })
28
- assert_match /&foo=bar$/, unauth_url
29
- end
30
-
31
- it "RelayState cases" do
32
- unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :RelayState => nil })
33
- assert !unauth_url.include?('RelayState')
34
-
35
- unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :RelayState => "http://example.com" })
36
- assert unauth_url.include?('&RelayState=http%3A%2F%2Fexample.com')
37
-
38
- unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { 'RelayState' => nil })
39
- assert !unauth_url.include?('RelayState')
40
-
41
- unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { 'RelayState' => "http://example.com" })
42
- assert unauth_url.include?('&RelayState=http%3A%2F%2Fexample.com')
43
- end
44
-
45
- it "set sessionindex" do
46
- settings.idp_slo_target_url = "http://example.com"
47
- sessionidx = OneLogin::RubySaml::Utils.uuid
48
- settings.sessionindex = sessionidx
49
-
50
- unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :nameid => "there" })
51
- inflated = decode_saml_request_payload(unauth_url)
52
-
53
- assert_match /<samlp:SessionIndex/, inflated
54
- assert_match %r(#{sessionidx}</samlp:SessionIndex>), inflated
55
- end
56
-
57
- it "set name_identifier_value" do
58
- settings.name_identifier_format = "transient"
59
- name_identifier_value = "abc123"
60
- settings.name_identifier_value = name_identifier_value
61
-
62
- unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :nameid => "there" })
63
- inflated = decode_saml_request_payload(unauth_url)
64
-
65
- assert_match /<saml:NameID/, inflated
66
- assert_match %r(#{name_identifier_value}</saml:NameID>), inflated
67
- end
68
-
69
- describe "when the target url doesn't contain a query string" do
70
- it "create the SAMLRequest parameter correctly" do
71
- unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings)
72
- assert_match /^http:\/\/unauth.com\/logout\?SAMLRequest/, unauth_url
73
- end
74
- end
75
-
76
- describe "when the target url contains a query string" do
77
- it "create the SAMLRequest parameter correctly" do
78
- settings.idp_slo_target_url = "http://example.com?field=value"
79
-
80
- unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings)
81
- assert_match /^http:\/\/example.com\?field=value&SAMLRequest/, unauth_url
82
- end
83
- end
84
-
85
- describe "consumation of logout may need to track the transaction" do
86
- it "have access to the request uuid" do
87
- settings.idp_slo_target_url = "http://example.com?field=value"
88
-
89
- unauth_req = OneLogin::RubySaml::Logoutrequest.new
90
- unauth_url = unauth_req.create(settings)
91
-
92
- inflated = decode_saml_request_payload(unauth_url)
93
- assert_match %r[ID='#{unauth_req.uuid}'], inflated
94
- end
95
- end
96
-
97
- describe "when the settings indicate to sign (embedded) logout request" do
98
-
99
- before do
100
- # sign the logout request
101
- settings.security[:logout_requests_signed] = true
102
- settings.security[:embed_sign] = true
103
- settings.certificate = ruby_saml_cert_text
104
- settings.private_key = ruby_saml_key_text
105
- end
106
-
107
- it "doesn't sign through create_xml_document" do
108
- unauth_req = OneLogin::RubySaml::Logoutrequest.new
109
- inflated = unauth_req.create_xml_document(settings).to_s
110
-
111
- refute_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
112
- refute_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
113
- refute_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
114
- end
115
-
116
- it "sign unsigned request" do
117
- unauth_req = OneLogin::RubySaml::Logoutrequest.new
118
- unauth_req_doc = unauth_req.create_xml_document(settings)
119
- inflated = unauth_req_doc.to_s
120
-
121
- refute_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
122
- refute_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
123
- refute_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
124
-
125
- inflated = unauth_req.sign_document(unauth_req_doc, settings).to_s
126
-
127
- assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
128
- assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
129
- assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
130
- end
131
-
132
- it "signs through create_logout_request_xml_doc" do
133
- unauth_req = OneLogin::RubySaml::Logoutrequest.new
134
- inflated = unauth_req.create_logout_request_xml_doc(settings).to_s
135
-
136
- assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
137
- assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
138
- assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
139
- end
140
-
141
- it "created a signed logout request" do
142
- settings.compress_request = true
143
-
144
- unauth_req = OneLogin::RubySaml::Logoutrequest.new
145
- unauth_url = unauth_req.create(settings)
146
-
147
- inflated = decode_saml_request_payload(unauth_url)
148
- assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
149
- assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
150
- assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
151
- end
152
-
153
- it "create a signed logout request with 256 digest and signature method" do
154
- settings.compress_request = false
155
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
156
- settings.security[:digest_method] = XMLSecurity::Document::SHA256
157
-
158
- params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings)
159
- request_xml = Base64.decode64(params["SAMLRequest"])
160
-
161
- assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], request_xml
162
- assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'/>], request_xml
163
- assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2001/04/xmlenc#sha256'/>], request_xml
164
- end
165
-
166
- it "create a signed logout request with 512 digest and signature method RSA_SHA384" do
167
- settings.compress_request = false
168
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA384
169
- settings.security[:digest_method] = XMLSecurity::Document::SHA512
170
-
171
- params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings)
172
- request_xml = Base64.decode64(params["SAMLRequest"])
173
-
174
- assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], request_xml
175
- assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#rsa-sha384'/>], request_xml
176
- assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2001/04/xmlenc#sha512'/>], request_xml
177
- end
178
- end
179
-
180
- describe "#create_params when the settings indicate to sign the logout request" do
181
-
182
- let(:cert) { OpenSSL::X509::Certificate.new(ruby_saml_cert_text) }
183
-
184
- before do
185
- # sign the logout request
186
- settings.security[:logout_requests_signed] = true
187
- settings.security[:embed_sign] = false
188
- settings.certificate = ruby_saml_cert_text
189
- settings.private_key = ruby_saml_key_text
190
- end
191
-
192
- it "create a signature parameter with RSA_SHA1 / SHA1 and validate it" do
193
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
194
-
195
- params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
196
- assert params['SAMLRequest']
197
- assert params[:RelayState]
198
- assert params['Signature']
199
- assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA1
200
-
201
- query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
202
- query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
203
- query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
204
-
205
- signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
206
- assert_equal signature_algorithm, OpenSSL::Digest::SHA1
207
- assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
208
- end
209
-
210
- it "create a signature parameter with RSA_SHA256 / SHA256 and validate it" do
211
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
212
-
213
- params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
214
- assert params['Signature']
215
- assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA256
216
-
217
- query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
218
- query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
219
- query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
220
-
221
- signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
222
- assert_equal signature_algorithm, OpenSSL::Digest::SHA256
223
- assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
224
- end
225
-
226
- it "create a signature parameter with RSA_SHA384 / SHA384 and validate it" do
227
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA384
228
-
229
- params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
230
- assert params['Signature']
231
- assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA384
232
-
233
- query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
234
- query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
235
- query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
236
-
237
- signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
238
- assert_equal signature_algorithm, OpenSSL::Digest::SHA384
239
- assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
240
- end
241
-
242
- it "create a signature parameter with RSA_SHA512 / SHA512 and validate it" do
243
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA512
244
-
245
- params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
246
- assert params['Signature']
247
- assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA512
248
-
249
- query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
250
- query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
251
- query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
252
-
253
- signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
254
- assert_equal signature_algorithm, OpenSSL::Digest::SHA512
255
- assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
256
- end
257
-
258
- end
259
- end
260
- end
@@ -1,409 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
2
-
3
- require 'onelogin/ruby-saml/logoutresponse'
4
- require 'logout_responses/logoutresponse_fixtures'
5
-
6
- class RubySamlTest < Minitest::Test
7
-
8
- describe "Logoutresponse" do
9
-
10
- let(:valid_logout_response_without_settings) { OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document) }
11
- let(:valid_logout_response) { OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings) }
12
-
13
- describe "#new" do
14
- it "raise an exception when response is initialized with nil" do
15
- assert_raises(ArgumentError) { OneLogin::RubySaml::Logoutresponse.new(nil) }
16
- end
17
- it "default to empty settings" do
18
- assert_nil valid_logout_response_without_settings.settings
19
- end
20
- it "accept constructor-injected settings" do
21
- refute_nil valid_logout_response.settings
22
- end
23
- it "accept constructor-injected options" do
24
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, nil, { :foo => :bar} )
25
- assert !logoutresponse.options.empty?
26
- end
27
- it "support base64 encoded responses" do
28
- generated_logout_response = valid_logout_response_document
29
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(Base64.encode64(generated_logout_response), settings)
30
- assert_equal generated_logout_response, logoutresponse.response
31
- end
32
- end
33
-
34
- describe "#validate_structure" do
35
- it "invalidates when the logout response has an invalid xml" do
36
- settings.soft = true
37
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(invalid_xml_logout_response_document, settings)
38
- assert !logoutresponse.send(:validate_structure)
39
- assert_includes logoutresponse.errors, "Invalid SAML Logout Response. Not match the saml-schema-protocol-2.0.xsd"
40
- end
41
-
42
- it "raise when the logout response has an invalid xml" do
43
- settings.soft = false
44
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(invalid_xml_logout_response_document, settings)
45
- assert_raises OneLogin::RubySaml::ValidationError do
46
- logoutresponse.send(:validate_structure)
47
- end
48
- end
49
- end
50
-
51
- describe "#validate" do
52
- describe "when soft=true" do
53
- before do
54
- settings.soft = true
55
- end
56
-
57
- it "validate the logout response" do
58
- in_relation_to_request_id = random_id
59
- opts = { :matches_request_id => in_relation_to_request_id}
60
-
61
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document({:uuid => in_relation_to_request_id}), settings, opts)
62
-
63
- assert logoutresponse.validate
64
-
65
- assert_equal settings.issuer, logoutresponse.issuer
66
- assert_equal in_relation_to_request_id, logoutresponse.in_response_to
67
-
68
- assert logoutresponse.success?
69
- assert_empty logoutresponse.errors
70
- end
71
-
72
- it "validate the logout response extended" do
73
- in_relation_to_request_id = random_id
74
- settings.idp_entity_id = 'http://app.muda.no'
75
- opts = { :matches_request_id => in_relation_to_request_id}
76
-
77
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document({:uuid => in_relation_to_request_id}), settings, opts)
78
- assert logoutresponse.validate
79
- assert_equal in_relation_to_request_id, logoutresponse.in_response_to
80
- assert logoutresponse.success?
81
- assert_empty logoutresponse.errors
82
- end
83
-
84
- it "invalidate logout response when initiated with blank" do
85
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new("", settings)
86
- assert !logoutresponse.validate
87
- assert_includes logoutresponse.errors, "Blank logout response"
88
- end
89
-
90
- it "invalidate logout response when initiated with no idp cert or fingerprint" do
91
- settings.idp_cert_fingerprint = nil
92
- settings.idp_cert = nil
93
- settings.idp_cert_multi = nil
94
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings)
95
- assert !logoutresponse.validate
96
- assert_includes logoutresponse.errors, "No fingerprint or certificate on settings of the logout response"
97
- end
98
-
99
- it "invalidate logout response with wrong id when given option :matches_request_id" do
100
- expected_request_id = "_some_other_expected_uuid"
101
- opts = { :matches_request_id => expected_request_id}
102
-
103
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings, opts)
104
-
105
- assert !logoutresponse.validate
106
- refute_equal expected_request_id, logoutresponse.in_response_to
107
- assert_includes logoutresponse.errors, "The InResponseTo of the Logout Response: #{logoutresponse.in_response_to}, does not match the ID of the Logout Request sent by the SP: #{expected_request_id}"
108
- end
109
-
110
- it "invalidate logout response with unexpected request status" do
111
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
112
-
113
- assert !logoutresponse.success?
114
- assert !logoutresponse.validate
115
- assert_includes logoutresponse.errors, "The status code of the Logout Response was not Success, was Requester"
116
- end
117
-
118
- it "invalidate logout response with unexpected request status and status message" do
119
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_with_message_document, settings)
120
-
121
- assert !logoutresponse.success?
122
- assert !logoutresponse.validate
123
- assert_includes logoutresponse.errors, "The status code of the Logout Response was not Success, was Requester -> Logoutrequest expired"
124
- end
125
-
126
- it "invalidate logout response when in lack of issuer setting" do
127
- bad_settings = settings
128
- bad_settings.issuer = nil
129
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, bad_settings)
130
- assert !logoutresponse.validate
131
- assert_includes logoutresponse.errors, "No issuer in settings of the logout response"
132
- end
133
-
134
- it "invalidate logout response with wrong issuer" do
135
- in_relation_to_request_id = random_id
136
- settings.idp_entity_id = 'http://invalid.issuer.example.com/'
137
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document({:uuid => in_relation_to_request_id}), settings)
138
- assert !logoutresponse.validate
139
- assert_includes logoutresponse.errors, "Doesn't match the issuer, expected: <#{logoutresponse.settings.idp_entity_id}>, but was: <http://app.muda.no>"
140
- end
141
-
142
- it "collect errors when collect_errors=true" do
143
- settings.idp_entity_id = 'http://invalid.issuer.example.com/'
144
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
145
- collect_errors = true
146
- assert !logoutresponse.validate(collect_errors)
147
- assert_includes logoutresponse.errors, "The status code of the Logout Response was not Success, was Requester"
148
- assert_includes logoutresponse.errors, "Doesn't match the issuer, expected: <#{logoutresponse.settings.idp_entity_id}>, but was: <http://app.muda.no>"
149
- end
150
-
151
- end
152
-
153
- describe "when soft=false" do
154
- before do
155
- settings.soft = false
156
- end
157
-
158
- it "validates good logout response" do
159
- in_relation_to_request_id = random_id
160
-
161
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document({:uuid => in_relation_to_request_id}), settings)
162
- assert logoutresponse.validate
163
- assert_empty logoutresponse.errors
164
- end
165
-
166
- it "raises validation error when response initiated with blank" do
167
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new("", settings)
168
-
169
- assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
170
- assert_includes logoutresponse.errors, "Blank logout response"
171
- end
172
-
173
- it "raises validation error when initiated with no idp cert or fingerprint" do
174
- settings.idp_cert_fingerprint = nil
175
- settings.idp_cert = nil
176
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings)
177
- assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
178
- assert_includes logoutresponse.errors, "No fingerprint or certificate on settings of the logout response"
179
- end
180
-
181
- it "raises validation error when matching for wrong request id" do
182
-
183
- expected_request_id = "_some_other_expected_id"
184
- opts = { :matches_request_id => expected_request_id}
185
-
186
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings, opts)
187
- assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
188
- assert_includes logoutresponse.errors, "The InResponseTo of the Logout Response: #{logoutresponse.in_response_to}, does not match the ID of the Logout Request sent by the SP: #{expected_request_id}"
189
- end
190
-
191
- it "raise validation error for wrong request status" do
192
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
193
-
194
- assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
195
- assert_includes logoutresponse.errors, "The status code of the Logout Response was not Success, was Requester"
196
- end
197
-
198
- it "raise validation error when in bad state" do
199
- # no settings
200
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
201
- assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
202
- assert_includes logoutresponse.errors, "The status code of the Logout Response was not Success, was Requester"
203
- end
204
-
205
- it "raise validation error when in lack of issuer setting" do
206
- settings.issuer = nil
207
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
208
- assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
209
- assert_includes logoutresponse.errors, "No issuer in settings of the logout response"
210
- end
211
-
212
- it "raise validation error when logout response with wrong issuer" do
213
- in_relation_to_request_id = random_id
214
- settings.idp_entity_id = 'http://invalid.issuer.example.com/'
215
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document({:uuid => in_relation_to_request_id}), settings)
216
- assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
217
- assert_includes logoutresponse.errors, "Doesn't match the issuer, expected: <#{logoutresponse.settings.idp_entity_id}>, but was: <http://app.muda.no>"
218
- end
219
- end
220
-
221
- describe "#validate_signature" do
222
- let (:params) { OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, random_id, "Custom Logout Message", :RelayState => 'http://example.com') }
223
-
224
- before do
225
- settings.soft = true
226
- settings.idp_slo_target_url = "http://example.com?field=value"
227
- settings.security[:logout_responses_signed] = true
228
- settings.security[:embed_sign] = false
229
- settings.certificate = ruby_saml_cert_text
230
- settings.private_key = ruby_saml_key_text
231
- settings.idp_cert = ruby_saml_cert_text
232
- end
233
-
234
- it "return true when no idp_cert is provided and option :relax_signature_validation is present" do
235
- settings.idp_cert = nil
236
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
237
- params['RelayState'] = params[:RelayState]
238
- options = {}
239
- options[:get_params] = params
240
- options[:relax_signature_validation] = true
241
- logoutresponse_sign_test = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
242
- assert logoutresponse_sign_test.send(:validate_signature)
243
- end
244
-
245
- it "return false when no idp_cert is provided and no option :relax_signature_validation is present" do
246
- settings.idp_cert = nil
247
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
248
- params['RelayState'] = params[:RelayState]
249
- options = {}
250
- options[:get_params] = params
251
- logoutresponse_sign_test = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
252
- assert !logoutresponse_sign_test.send(:validate_signature)
253
- end
254
-
255
- it "return true when valid RSA_SHA1 Signature" do
256
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
257
- params['RelayState'] = params[:RelayState]
258
- options = {}
259
- options[:get_params] = params
260
- logoutresponse_sign_test = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
261
- assert logoutresponse_sign_test.send(:validate_signature)
262
- end
263
-
264
- it "return true when valid RSA_SHA256 Signature" do
265
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
266
- params['RelayState'] = params[:RelayState]
267
- options = {}
268
- options[:get_params] = params
269
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
270
- assert logoutresponse.send(:validate_signature)
271
- end
272
-
273
- it "return false when invalid RSA_SHA1 Signature" do
274
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
275
- params['RelayState'] = 'http://invalid.example.com'
276
- options = {}
277
- options[:get_params] = params
278
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
279
- assert !logoutresponse.send(:validate_signature)
280
- end
281
-
282
- it "raise when invalid RSA_SHA1 Signature" do
283
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
284
- settings.soft = false
285
- params['RelayState'] = 'http://invalid.example.com'
286
- options = {}
287
- options[:get_params] = params
288
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
289
-
290
- assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.send(:validate_signature) }
291
- assert logoutresponse.errors.include? "Invalid Signature on Logout Response"
292
- end
293
-
294
- it "raise when get_params encoding differs from what this library generates" do
295
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
296
- settings.soft = false
297
- options = {}
298
- options[:get_params] = params
299
- options[:get_params]['RelayState'] = 'http://example.com'
300
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
301
- # Assemble query string.
302
- query = OneLogin::RubySaml::Utils.build_query(
303
- :type => 'SAMLResponse',
304
- :data => params['SAMLResponse'],
305
- :relay_state => params['RelayState'],
306
- :sig_alg => params['SigAlg']
307
- )
308
- # Modify the query string so that it encodes the same values,
309
- # but with different percent-encoding. Sanity-check that they
310
- # really are equialent before moving on.
311
- original_query = query.dup
312
- query.gsub!("example", "ex%61mple")
313
- refute_equal(query, original_query)
314
- assert_equal(CGI.unescape(query), CGI.unescape(original_query))
315
- # Make normalised signature based on our modified params.
316
- sign_algorithm = XMLSecurity::BaseDocument.new.algorithm(settings.security[:signature_method])
317
- signature = settings.get_sp_key.sign(sign_algorithm.new, query)
318
- params['Signature'] = Base64.encode64(signature).gsub(/\n/, "")
319
- # Re-create the Logoutresponse based on these modified parameters,
320
- # and ask it to validate the signature. It will do it incorrectly,
321
- # because it will compute it based on re-encoded query parameters,
322
- # rather than their original encodings.
323
- options[:get_params] = params
324
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
325
- assert_raises(OneLogin::RubySaml::ValidationError, "Invalid Signature on Logout Request") do
326
- logoutresponse.send(:validate_signature)
327
- end
328
- end
329
-
330
- it "return true even if raw_get_params encoding differs from what this library generates" do
331
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
332
- settings.soft = false
333
- options = {}
334
- options[:get_params] = params
335
- options[:get_params]['RelayState'] = 'http://example.com'
336
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
337
- # Assemble query string.
338
- query = OneLogin::RubySaml::Utils.build_query(
339
- :type => 'SAMLResponse',
340
- :data => params['SAMLResponse'],
341
- :relay_state => params['RelayState'],
342
- :sig_alg => params['SigAlg']
343
- )
344
- # Modify the query string so that it encodes the same values,
345
- # but with different percent-encoding. Sanity-check that they
346
- # really are equialent before moving on.
347
- original_query = query.dup
348
- query.gsub!("example", "ex%61mple")
349
- refute_equal(query, original_query)
350
- assert_equal(CGI.unescape(query), CGI.unescape(original_query))
351
- # Make normalised signature based on our modified params.
352
- sign_algorithm = XMLSecurity::BaseDocument.new.algorithm(settings.security[:signature_method])
353
- signature = settings.get_sp_key.sign(sign_algorithm.new, query)
354
- params['Signature'] = Base64.encode64(signature).gsub(/\n/, "")
355
- # Re-create the Logoutresponse based on these modified parameters,
356
- # and ask it to validate the signature. Provide the altered parameter
357
- # in its raw URI-encoded form, so that we don't have to guess the value
358
- # that contributed to the signature.
359
- options[:get_params] = params
360
- options[:get_params].delete("RelayState")
361
- options[:raw_get_params] = {
362
- "RelayState" => "http%3A%2F%2Fex%61mple.com",
363
- }
364
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
365
- assert logoutresponse.send(:validate_signature)
366
- end
367
- end
368
-
369
- describe "#validate_signature" do
370
- let (:params) { OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, random_id, "Custom Logout Message", :RelayState => 'http://example.com') }
371
-
372
- before do
373
- settings.soft = true
374
- settings.idp_slo_target_url = "http://example.com?field=value"
375
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
376
- settings.security[:logout_responses_signed] = true
377
- settings.security[:embed_sign] = false
378
- settings.certificate = ruby_saml_cert_text
379
- settings.private_key = ruby_saml_key_text
380
- settings.idp_cert = nil
381
- end
382
-
383
- it "return true when at least a idp_cert is valid" do
384
- params['RelayState'] = params[:RelayState]
385
- options = {}
386
- options[:get_params] = params
387
- settings.idp_cert_multi = {
388
- :signing => [ruby_saml_cert_text2, ruby_saml_cert_text],
389
- :encryption => []
390
- }
391
- logoutresponse_sign_test = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
392
- assert logoutresponse_sign_test.send(:validate_signature)
393
- end
394
-
395
- it "return false when none cert on idp_cert_multi is valid" do
396
- params['RelayState'] = params[:RelayState]
397
- options = {}
398
- options[:get_params] = params
399
- settings.idp_cert_multi = {
400
- :signing => [ruby_saml_cert_text2, ruby_saml_cert_text2],
401
- :encryption => []
402
- }
403
- logoutresponse_sign_test = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
404
- assert !logoutresponse_sign_test.send(:validate_signature)
405
- end
406
- end
407
- end
408
- end
409
- end