ruby-saml 1.11.0 → 1.12.0

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

Potentially problematic release.


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

Files changed (158) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +14 -12
  3. data/README.md +67 -19
  4. data/changelog.md +23 -0
  5. data/lib/onelogin/ruby-saml/attributes.rb +24 -1
  6. data/lib/onelogin/ruby-saml/authrequest.rb +9 -4
  7. data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +62 -24
  8. data/lib/onelogin/ruby-saml/logoutrequest.rb +7 -1
  9. data/lib/onelogin/ruby-saml/logoutresponse.rb +4 -0
  10. data/lib/onelogin/ruby-saml/metadata.rb +9 -1
  11. data/lib/onelogin/ruby-saml/response.rb +37 -15
  12. data/lib/onelogin/ruby-saml/saml_message.rb +6 -0
  13. data/lib/onelogin/ruby-saml/setting_error.rb +6 -0
  14. data/lib/onelogin/ruby-saml/settings.rb +34 -2
  15. data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +4 -0
  16. data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +27 -14
  17. data/lib/onelogin/ruby-saml/utils.rb +56 -0
  18. data/lib/onelogin/ruby-saml/version.rb +1 -1
  19. data/lib/xml_security.rb +34 -6
  20. data/ruby-saml.gemspec +8 -4
  21. metadata +22 -282
  22. data/test/certificates/certificate.der +0 -0
  23. data/test/certificates/certificate1 +0 -12
  24. data/test/certificates/certificate_without_head_foot +0 -1
  25. data/test/certificates/formatted_certificate +0 -14
  26. data/test/certificates/formatted_chained_certificate +0 -42
  27. data/test/certificates/formatted_private_key +0 -12
  28. data/test/certificates/formatted_rsa_private_key +0 -12
  29. data/test/certificates/invalid_certificate1 +0 -1
  30. data/test/certificates/invalid_certificate2 +0 -1
  31. data/test/certificates/invalid_certificate3 +0 -12
  32. data/test/certificates/invalid_chained_certificate1 +0 -1
  33. data/test/certificates/invalid_private_key1 +0 -1
  34. data/test/certificates/invalid_private_key2 +0 -1
  35. data/test/certificates/invalid_private_key3 +0 -10
  36. data/test/certificates/invalid_rsa_private_key1 +0 -1
  37. data/test/certificates/invalid_rsa_private_key2 +0 -1
  38. data/test/certificates/invalid_rsa_private_key3 +0 -10
  39. data/test/certificates/ruby-saml-2.crt +0 -15
  40. data/test/certificates/ruby-saml.crt +0 -14
  41. data/test/certificates/ruby-saml.key +0 -15
  42. data/test/idp_metadata_parser_test.rb +0 -594
  43. data/test/logging_test.rb +0 -62
  44. data/test/logout_requests/invalid_slo_request.xml +0 -6
  45. data/test/logout_requests/slo_request.xml +0 -4
  46. data/test/logout_requests/slo_request.xml.base64 +0 -1
  47. data/test/logout_requests/slo_request_deflated.xml.base64 +0 -1
  48. data/test/logout_requests/slo_request_with_name_id_format.xml +0 -4
  49. data/test/logout_requests/slo_request_with_session_index.xml +0 -5
  50. data/test/logout_responses/logoutresponse_fixtures.rb +0 -86
  51. data/test/logoutrequest_test.rb +0 -260
  52. data/test/logoutresponse_test.rb +0 -427
  53. data/test/metadata/idp_descriptor.xml +0 -26
  54. data/test/metadata/idp_descriptor_2.xml +0 -56
  55. data/test/metadata/idp_descriptor_3.xml +0 -14
  56. data/test/metadata/idp_descriptor_4.xml +0 -72
  57. data/test/metadata/idp_metadata_different_sign_and_encrypt_cert.xml +0 -72
  58. data/test/metadata/idp_metadata_multi_certs.xml +0 -75
  59. data/test/metadata/idp_metadata_multi_signing_certs.xml +0 -52
  60. data/test/metadata/idp_metadata_same_sign_and_encrypt_cert.xml +0 -71
  61. data/test/metadata/idp_multiple_descriptors.xml +0 -59
  62. data/test/metadata/idp_multiple_descriptors_2.xml +0 -59
  63. data/test/metadata/no_idp_descriptor.xml +0 -21
  64. data/test/metadata_test.rb +0 -331
  65. data/test/request_test.rb +0 -340
  66. data/test/response_test.rb +0 -1629
  67. data/test/responses/adfs_response_sha1.xml +0 -46
  68. data/test/responses/adfs_response_sha256.xml +0 -46
  69. data/test/responses/adfs_response_sha384.xml +0 -46
  70. data/test/responses/adfs_response_sha512.xml +0 -46
  71. data/test/responses/adfs_response_xmlns.xml +0 -45
  72. data/test/responses/attackxee.xml +0 -13
  73. data/test/responses/invalids/duplicated_attributes.xml.base64 +0 -1
  74. data/test/responses/invalids/empty_destination.xml.base64 +0 -1
  75. data/test/responses/invalids/empty_nameid.xml.base64 +0 -1
  76. data/test/responses/invalids/encrypted_new_attack.xml.base64 +0 -1
  77. data/test/responses/invalids/invalid_audience.xml.base64 +0 -1
  78. data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +0 -1
  79. data/test/responses/invalids/invalid_issuer_message.xml.base64 +0 -1
  80. data/test/responses/invalids/invalid_signature_position.xml.base64 +0 -1
  81. data/test/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 +0 -1
  82. data/test/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 +0 -1
  83. data/test/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 +0 -1
  84. data/test/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 +0 -1
  85. data/test/responses/invalids/multiple_assertions.xml.base64 +0 -2
  86. data/test/responses/invalids/multiple_signed.xml.base64 +0 -1
  87. data/test/responses/invalids/no_authnstatement.xml.base64 +0 -1
  88. data/test/responses/invalids/no_conditions.xml.base64 +0 -1
  89. data/test/responses/invalids/no_id.xml.base64 +0 -1
  90. data/test/responses/invalids/no_issuer_assertion.xml.base64 +0 -1
  91. data/test/responses/invalids/no_issuer_response.xml.base64 +0 -1
  92. data/test/responses/invalids/no_nameid.xml.base64 +0 -1
  93. data/test/responses/invalids/no_saml2.xml.base64 +0 -1
  94. data/test/responses/invalids/no_signature.xml.base64 +0 -1
  95. data/test/responses/invalids/no_status.xml.base64 +0 -1
  96. data/test/responses/invalids/no_status_code.xml.base64 +0 -1
  97. data/test/responses/invalids/no_subjectconfirmation_data.xml.base64 +0 -1
  98. data/test/responses/invalids/no_subjectconfirmation_method.xml.base64 +0 -1
  99. data/test/responses/invalids/response_invalid_signed_element.xml.base64 +0 -1
  100. data/test/responses/invalids/response_with_concealed_signed_assertion.xml +0 -51
  101. data/test/responses/invalids/response_with_doubled_signed_assertion.xml +0 -49
  102. data/test/responses/invalids/signature_wrapping_attack.xml.base64 +0 -1
  103. data/test/responses/invalids/status_code_responder.xml.base64 +0 -1
  104. data/test/responses/invalids/status_code_responer_and_msg.xml.base64 +0 -1
  105. data/test/responses/invalids/wrong_spnamequalifier.xml.base64 +0 -1
  106. data/test/responses/no_signature_ns.xml +0 -48
  107. data/test/responses/open_saml_response.xml +0 -56
  108. data/test/responses/response_assertion_wrapped.xml.base64 +0 -93
  109. data/test/responses/response_audience_self_closed_tag.xml.base64 +0 -1
  110. data/test/responses/response_double_status_code.xml.base64 +0 -1
  111. data/test/responses/response_encrypted_attrs.xml.base64 +0 -1
  112. data/test/responses/response_encrypted_nameid.xml.base64 +0 -1
  113. data/test/responses/response_eval.xml +0 -7
  114. data/test/responses/response_no_cert_and_encrypted_attrs.xml +0 -29
  115. data/test/responses/response_node_text_attack.xml.base64 +0 -1
  116. data/test/responses/response_node_text_attack2.xml.base64 +0 -1
  117. data/test/responses/response_node_text_attack3.xml.base64 +0 -1
  118. data/test/responses/response_unsigned_xml_base64 +0 -1
  119. data/test/responses/response_with_ampersands.xml +0 -139
  120. data/test/responses/response_with_ampersands.xml.base64 +0 -93
  121. data/test/responses/response_with_ds_namespace_at_the_root.xml.base64 +0 -1
  122. data/test/responses/response_with_multiple_attribute_statements.xml +0 -72
  123. data/test/responses/response_with_multiple_attribute_values.xml +0 -67
  124. data/test/responses/response_with_retrieval_method.xml +0 -26
  125. data/test/responses/response_with_saml2_namespace.xml.base64 +0 -102
  126. data/test/responses/response_with_signed_assertion.xml.base64 +0 -66
  127. data/test/responses/response_with_signed_assertion_2.xml.base64 +0 -1
  128. data/test/responses/response_with_signed_assertion_3.xml +0 -30
  129. data/test/responses/response_with_signed_message_and_assertion.xml +0 -34
  130. data/test/responses/response_with_undefined_recipient.xml.base64 +0 -1
  131. data/test/responses/response_without_attributes.xml.base64 +0 -79
  132. data/test/responses/response_without_reference_uri.xml.base64 +0 -1
  133. data/test/responses/response_wrapped.xml.base64 +0 -150
  134. data/test/responses/signed_message_encrypted_signed_assertion.xml.base64 +0 -1
  135. data/test/responses/signed_message_encrypted_unsigned_assertion.xml.base64 +0 -1
  136. data/test/responses/signed_nameid_in_atts.xml +0 -47
  137. data/test/responses/signed_unqual_nameid_in_atts.xml +0 -47
  138. data/test/responses/simple_saml_php.xml +0 -71
  139. data/test/responses/starfield_response.xml.base64 +0 -1
  140. data/test/responses/test_sign.xml +0 -43
  141. data/test/responses/unsigned_encrypted_adfs.xml +0 -23
  142. data/test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64 +0 -1
  143. data/test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64 +0 -1
  144. data/test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64 +0 -1
  145. data/test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64 +0 -1
  146. data/test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64 +0 -1
  147. data/test/responses/unsigned_message_encrypted_signed_assertion.xml.base64 +0 -1
  148. data/test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64 +0 -1
  149. data/test/responses/valid_response.xml.base64 +0 -1
  150. data/test/responses/valid_response_with_formatted_x509certificate.xml.base64 +0 -1
  151. data/test/responses/valid_response_without_x509certificate.xml.base64 +0 -1
  152. data/test/saml_message_test.rb +0 -56
  153. data/test/settings_test.rb +0 -338
  154. data/test/slo_logoutrequest_test.rb +0 -467
  155. data/test/slo_logoutresponse_test.rb +0 -233
  156. data/test/test_helper.rb +0 -333
  157. data/test/utils_test.rb +0 -259
  158. data/test/xml_security_test.rb +0 -421
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,427 +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.sp_entity_id, 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 sp_entity_id setting" do
127
- bad_settings = settings
128
- bad_settings.issuer = nil
129
- bad_settings.sp_entity_id = nil
130
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, bad_settings)
131
- assert !logoutresponse.validate
132
- assert_includes logoutresponse.errors, "No sp_entity_id in settings of the logout response"
133
- end
134
-
135
- it "invalidate logout response with wrong issuer" do
136
- in_relation_to_request_id = random_id
137
- settings.idp_entity_id = 'http://invalid.issuer.example.com/'
138
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document({:uuid => in_relation_to_request_id}), settings)
139
- assert !logoutresponse.validate
140
- assert_includes logoutresponse.errors, "Doesn't match the issuer, expected: <#{logoutresponse.settings.idp_entity_id}>, but was: <http://app.muda.no>"
141
- end
142
-
143
- it "collect errors when collect_errors=true" do
144
- settings.idp_entity_id = 'http://invalid.issuer.example.com/'
145
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
146
- collect_errors = true
147
- assert !logoutresponse.validate(collect_errors)
148
- assert_includes logoutresponse.errors, "The status code of the Logout Response was not Success, was Requester"
149
- assert_includes logoutresponse.errors, "Doesn't match the issuer, expected: <#{logoutresponse.settings.idp_entity_id}>, but was: <http://app.muda.no>"
150
- end
151
-
152
- end
153
-
154
- describe "when soft=false" do
155
- before do
156
- settings.soft = false
157
- end
158
-
159
- it "validates good logout response" do
160
- in_relation_to_request_id = random_id
161
-
162
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document({:uuid => in_relation_to_request_id}), settings)
163
- assert logoutresponse.validate
164
- assert_empty logoutresponse.errors
165
- end
166
-
167
- it "raises validation error when response initiated with blank" do
168
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new("", settings)
169
-
170
- assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
171
- assert_includes logoutresponse.errors, "Blank logout response"
172
- end
173
-
174
- it "raises validation error when initiated with no idp cert or fingerprint" do
175
- settings.idp_cert_fingerprint = nil
176
- settings.idp_cert = nil
177
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings)
178
- assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
179
- assert_includes logoutresponse.errors, "No fingerprint or certificate on settings of the logout response"
180
- end
181
-
182
- it "raises validation error when matching for wrong request id" do
183
-
184
- expected_request_id = "_some_other_expected_id"
185
- opts = { :matches_request_id => expected_request_id}
186
-
187
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings, opts)
188
- assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
189
- 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}"
190
- end
191
-
192
- it "raise validation error for wrong request status" do
193
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
194
-
195
- assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
196
- assert_includes logoutresponse.errors, "The status code of the Logout Response was not Success, was Requester"
197
- end
198
-
199
- it "raise validation error when in bad state" do
200
- # no settings
201
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
202
- assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
203
- assert_includes logoutresponse.errors, "The status code of the Logout Response was not Success, was Requester"
204
- end
205
-
206
- it "raise validation error when in lack of sp_entity_id setting" do
207
- settings.issuer = nil
208
- settings.sp_entity_id = nil
209
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
210
- assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
211
- assert_includes logoutresponse.errors, "No sp_entity_id in settings of the logout response"
212
- end
213
-
214
- it "raise validation error when logout response with wrong issuer" do
215
- in_relation_to_request_id = random_id
216
- settings.idp_entity_id = 'http://invalid.issuer.example.com/'
217
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document({:uuid => in_relation_to_request_id}), settings)
218
- assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
219
- assert_includes logoutresponse.errors, "Doesn't match the issuer, expected: <#{logoutresponse.settings.idp_entity_id}>, but was: <http://app.muda.no>"
220
- end
221
- end
222
-
223
- describe "#validate_signature" do
224
- let (:params) { OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, random_id, "Custom Logout Message", :RelayState => 'http://example.com') }
225
-
226
- before do
227
- settings.soft = true
228
- settings.idp_slo_target_url = "http://example.com?field=value"
229
- settings.security[:logout_responses_signed] = true
230
- settings.security[:embed_sign] = false
231
- settings.certificate = ruby_saml_cert_text
232
- settings.private_key = ruby_saml_key_text
233
- settings.idp_cert = ruby_saml_cert_text
234
- end
235
-
236
- it "return true when no idp_cert is provided and option :relax_signature_validation is present" do
237
- settings.idp_cert = nil
238
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
239
- params['RelayState'] = params[:RelayState]
240
- options = {}
241
- options[:get_params] = params
242
- options[:relax_signature_validation] = true
243
- logoutresponse_sign_test = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
244
- assert logoutresponse_sign_test.send(:validate_signature)
245
- end
246
-
247
- it "return false when no idp_cert is provided and no option :relax_signature_validation is present" do
248
- settings.idp_cert = nil
249
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
250
- params['RelayState'] = params[:RelayState]
251
- options = {}
252
- options[:get_params] = params
253
- logoutresponse_sign_test = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
254
- assert !logoutresponse_sign_test.send(:validate_signature)
255
- end
256
-
257
- it "return true when valid RSA_SHA1 Signature" do
258
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
259
- params['RelayState'] = params[:RelayState]
260
- options = {}
261
- options[:get_params] = params
262
- logoutresponse_sign_test = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
263
- assert logoutresponse_sign_test.send(:validate_signature)
264
- end
265
-
266
- it "return true when valid RSA_SHA256 Signature" do
267
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
268
- params['RelayState'] = params[:RelayState]
269
- options = {}
270
- options[:get_params] = params
271
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
272
- assert logoutresponse.send(:validate_signature)
273
- end
274
-
275
- it "return false when invalid RSA_SHA1 Signature" do
276
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
277
- params['RelayState'] = 'http://invalid.example.com'
278
- options = {}
279
- options[:get_params] = params
280
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
281
- assert !logoutresponse.send(:validate_signature)
282
- end
283
-
284
- it "raise when invalid RSA_SHA1 Signature" do
285
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
286
- settings.soft = false
287
- params['RelayState'] = 'http://invalid.example.com'
288
- options = {}
289
- options[:get_params] = params
290
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
291
-
292
- assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.send(:validate_signature) }
293
- assert logoutresponse.errors.include? "Invalid Signature on Logout Response"
294
- end
295
-
296
- it "raise when get_params encoding differs from what this library generates" do
297
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
298
- settings.soft = false
299
- options = {}
300
- options[:get_params] = params
301
- options[:get_params]['RelayState'] = 'http://example.com'
302
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
303
- # Assemble query string.
304
- query = OneLogin::RubySaml::Utils.build_query(
305
- :type => 'SAMLResponse',
306
- :data => params['SAMLResponse'],
307
- :relay_state => params['RelayState'],
308
- :sig_alg => params['SigAlg']
309
- )
310
- # Modify the query string so that it encodes the same values,
311
- # but with different percent-encoding. Sanity-check that they
312
- # really are equialent before moving on.
313
- original_query = query.dup
314
- query.gsub!("example", "ex%61mple")
315
- refute_equal(query, original_query)
316
- assert_equal(CGI.unescape(query), CGI.unescape(original_query))
317
- # Make normalised signature based on our modified params.
318
- sign_algorithm = XMLSecurity::BaseDocument.new.algorithm(settings.security[:signature_method])
319
- signature = settings.get_sp_key.sign(sign_algorithm.new, query)
320
- params['Signature'] = Base64.encode64(signature).gsub(/\n/, "")
321
- # Re-create the Logoutresponse based on these modified parameters,
322
- # and ask it to validate the signature. It will do it incorrectly,
323
- # because it will compute it based on re-encoded query parameters,
324
- # rather than their original encodings.
325
- options[:get_params] = params
326
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
327
- assert_raises(OneLogin::RubySaml::ValidationError, "Invalid Signature on Logout Request") do
328
- logoutresponse.send(:validate_signature)
329
- end
330
- end
331
-
332
- it "return true even if raw_get_params encoding differs from what this library generates" do
333
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
334
- settings.soft = false
335
- options = {}
336
- options[:get_params] = params
337
- options[:get_params]['RelayState'] = 'http://example.com'
338
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
339
- # Assemble query string.
340
- query = OneLogin::RubySaml::Utils.build_query(
341
- :type => 'SAMLResponse',
342
- :data => params['SAMLResponse'],
343
- :relay_state => params['RelayState'],
344
- :sig_alg => params['SigAlg']
345
- )
346
- # Modify the query string so that it encodes the same values,
347
- # but with different percent-encoding. Sanity-check that they
348
- # really are equialent before moving on.
349
- original_query = query.dup
350
- query.gsub!("example", "ex%61mple")
351
- refute_equal(query, original_query)
352
- assert_equal(CGI.unescape(query), CGI.unescape(original_query))
353
- # Make normalised signature based on our modified params.
354
- sign_algorithm = XMLSecurity::BaseDocument.new.algorithm(settings.security[:signature_method])
355
- signature = settings.get_sp_key.sign(sign_algorithm.new, query)
356
- params['Signature'] = Base64.encode64(signature).gsub(/\n/, "")
357
- # Re-create the Logoutresponse based on these modified parameters,
358
- # and ask it to validate the signature. Provide the altered parameter
359
- # in its raw URI-encoded form, so that we don't have to guess the value
360
- # that contributed to the signature.
361
- options[:get_params] = params
362
- options[:get_params].delete("RelayState")
363
- options[:raw_get_params] = {
364
- "RelayState" => "http%3A%2F%2Fex%61mple.com",
365
- }
366
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
367
- assert logoutresponse.send(:validate_signature)
368
- end
369
- end
370
-
371
- describe "#validate_signature" do
372
- let (:params) { OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, random_id, "Custom Logout Message", :RelayState => 'http://example.com') }
373
-
374
- before do
375
- settings.soft = true
376
- settings.idp_slo_target_url = "http://example.com?field=value"
377
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
378
- settings.security[:logout_responses_signed] = true
379
- settings.security[:embed_sign] = false
380
- settings.certificate = ruby_saml_cert_text
381
- settings.private_key = ruby_saml_key_text
382
- settings.idp_cert = nil
383
- end
384
-
385
- it "return true when at least a idp_cert is valid" do
386
- params['RelayState'] = params[:RelayState]
387
- options = {}
388
- options[:get_params] = params
389
- settings.idp_cert_multi = {
390
- :signing => [ruby_saml_cert_text2, ruby_saml_cert_text],
391
- :encryption => []
392
- }
393
- logoutresponse_sign_test = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
394
- assert logoutresponse_sign_test.send(:validate_signature)
395
- end
396
-
397
- it "return false when cert expired and check_idp_cert_expiration expired" do
398
- params['RelayState'] = params[:RelayState]
399
- options = {}
400
- options[:get_params] = params
401
- settings.security[:check_idp_cert_expiration] = true
402
- settings.idp_cert = nil
403
- settings.idp_cert_multi = {
404
- :signing => [ruby_saml_cert_text],
405
- :encryption => []
406
- }
407
- logoutresponse_sign_test = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
408
- assert !logoutresponse_sign_test.send(:validate_signature)
409
- assert_includes logoutresponse_sign_test.errors, "IdP x509 certificate expired"
410
- end
411
-
412
- it "return false when none cert on idp_cert_multi is valid" do
413
- params['RelayState'] = params[:RelayState]
414
- options = {}
415
- options[:get_params] = params
416
- settings.idp_cert_multi = {
417
- :signing => [ruby_saml_cert_text2, ruby_saml_cert_text2],
418
- :encryption => []
419
- }
420
- logoutresponse_sign_test = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
421
- assert !logoutresponse_sign_test.send(:validate_signature)
422
- assert_includes logoutresponse_sign_test.errors, "Invalid Signature on Logout Response"
423
- end
424
- end
425
- end
426
- end
427
- end