ruby-saml 0.9.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/LICENSE +1 -1
  4. data/README.md +71 -15
  5. data/changelog.md +15 -6
  6. data/lib/onelogin/ruby-saml.rb +1 -0
  7. data/lib/onelogin/ruby-saml/attribute_service.rb +25 -2
  8. data/lib/onelogin/ruby-saml/attributes.rb +42 -23
  9. data/lib/onelogin/ruby-saml/authrequest.rb +33 -8
  10. data/lib/onelogin/ruby-saml/http_error.rb +7 -0
  11. data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +65 -10
  12. data/lib/onelogin/ruby-saml/logging.rb +14 -10
  13. data/lib/onelogin/ruby-saml/logoutrequest.rb +39 -14
  14. data/lib/onelogin/ruby-saml/logoutresponse.rb +166 -39
  15. data/lib/onelogin/ruby-saml/metadata.rb +40 -23
  16. data/lib/onelogin/ruby-saml/response.rb +562 -88
  17. data/lib/onelogin/ruby-saml/saml_message.rb +80 -14
  18. data/lib/onelogin/ruby-saml/settings.rb +62 -23
  19. data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +210 -20
  20. data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +44 -13
  21. data/lib/onelogin/ruby-saml/utils.rb +163 -40
  22. data/lib/onelogin/ruby-saml/version.rb +1 -1
  23. data/lib/schemas/saml-schema-metadata-2.0.xsd +0 -2
  24. data/lib/xml_security.rb +87 -29
  25. data/ruby-saml.gemspec +1 -0
  26. data/test/certificates/{r1_certificate2_base64 → certificate_without_head_foot} +0 -0
  27. data/test/certificates/formatted_certificate +14 -0
  28. data/test/certificates/formatted_private_key +12 -0
  29. data/test/certificates/formatted_rsa_private_key +12 -0
  30. data/test/certificates/invalid_certificate1 +1 -0
  31. data/test/certificates/invalid_certificate2 +1 -0
  32. data/test/certificates/invalid_certificate3 +12 -0
  33. data/test/certificates/invalid_private_key1 +1 -0
  34. data/test/certificates/invalid_private_key2 +1 -0
  35. data/test/certificates/invalid_private_key3 +10 -0
  36. data/test/certificates/invalid_rsa_private_key1 +1 -0
  37. data/test/certificates/invalid_rsa_private_key2 +1 -0
  38. data/test/certificates/invalid_rsa_private_key3 +10 -0
  39. data/test/idp_metadata_parser_test.rb +41 -4
  40. data/test/logging_test.rb +62 -0
  41. data/test/logout_requests/invalid_slo_request.xml +6 -0
  42. data/test/{responses → logout_requests}/slo_request.xml +0 -0
  43. data/test/logout_requests/slo_request.xml.base64 +1 -0
  44. data/test/logout_requests/slo_request_deflated.xml.base64 +1 -0
  45. data/test/logout_requests/slo_request_with_session_index.xml +5 -0
  46. data/test/{responses → logout_responses}/logoutresponse_fixtures.rb +6 -6
  47. data/test/logoutrequest_test.rb +79 -52
  48. data/test/logoutresponse_test.rb +206 -59
  49. data/test/metadata_test.rb +77 -7
  50. data/test/request_test.rb +80 -65
  51. data/test/response_test.rb +862 -189
  52. data/test/responses/attackxee.xml +13 -0
  53. data/test/responses/invalids/invalid_audience.xml.base64 +1 -0
  54. data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +1 -0
  55. data/test/responses/invalids/invalid_issuer_message.xml.base64 +1 -0
  56. data/test/responses/invalids/invalid_signature_position.xml.base64 +1 -0
  57. data/test/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 +1 -0
  58. data/test/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 +1 -0
  59. data/test/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 +1 -0
  60. data/test/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 +1 -0
  61. data/test/responses/invalids/multiple_assertions.xml.base64 +2 -0
  62. data/test/responses/invalids/multiple_signed.xml.base64 +1 -0
  63. data/test/responses/invalids/no_id.xml.base64 +1 -0
  64. data/test/responses/invalids/no_saml2.xml.base64 +1 -0
  65. data/test/responses/invalids/no_signature.xml.base64 +1 -0
  66. data/test/responses/invalids/no_status.xml.base64 +1 -0
  67. data/test/responses/invalids/no_status_code.xml.base64 +1 -0
  68. data/test/responses/invalids/no_subjectconfirmation_data.xml.base64 +1 -0
  69. data/test/responses/invalids/no_subjectconfirmation_method.xml.base64 +1 -0
  70. data/test/responses/invalids/response_encrypted_attrs.xml.base64 +1 -0
  71. data/test/responses/invalids/response_invalid_signed_element.xml.base64 +1 -0
  72. data/test/responses/invalids/status_code_responder.xml.base64 +1 -0
  73. data/test/responses/invalids/status_code_responer_and_msg.xml.base64 +1 -0
  74. data/test/responses/{response4.xml.base64 → response_assertion_wrapped.xml.base64} +0 -0
  75. data/test/responses/response_encrypted_nameid.xml.base64 +1 -0
  76. data/test/responses/response_unsigned_xml_base64 +1 -0
  77. data/test/responses/{response5.xml.base64 → response_with_saml2_namespace.xml.base64} +0 -0
  78. data/test/responses/{response3.xml.base64 → response_with_signed_assertion.xml.base64} +0 -0
  79. data/test/responses/{r1_response6.xml.base64 → response_with_signed_assertion_2.xml.base64} +0 -0
  80. data/test/responses/{response1.xml.base64 → response_with_undefined_recipient.xml.base64} +0 -0
  81. data/test/responses/{response2.xml.base64 → response_without_attributes.xml.base64} +0 -0
  82. data/test/responses/{wrapped_response_2.xml.base64 → response_wrapped.xml.base64} +0 -0
  83. data/test/responses/signed_message_encrypted_signed_assertion.xml.base64 +1 -0
  84. data/test/responses/signed_message_encrypted_unsigned_assertion.xml.base64 +1 -0
  85. data/test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64 +1 -0
  86. data/test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64 +1 -0
  87. data/test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64 +1 -0
  88. data/test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64 +1 -0
  89. data/test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64 +1 -0
  90. data/test/responses/unsigned_message_encrypted_signed_assertion.xml.base64 +1 -0
  91. data/test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64 +1 -0
  92. data/test/responses/valid_response.xml.base64 +1 -0
  93. data/test/saml_message_test.rb +56 -0
  94. data/test/settings_test.rb +138 -1
  95. data/test/slo_logoutrequest_test.rb +239 -28
  96. data/test/slo_logoutresponse_test.rb +93 -71
  97. data/test/test_helper.rb +138 -31
  98. data/test/utils_test.rb +129 -25
  99. data/test/xml_security_test.rb +140 -71
  100. metadata +142 -25
  101. data/test/responses/response_node_text_attack.xml.base64 +0 -1
@@ -1,41 +1,145 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
1
+ require "test_helper"
2
2
 
3
3
  class UtilsTest < Minitest::Test
4
- describe "Utils" do
5
- describe 'element_text' do
6
- it 'returns the element text' do
7
- element = REXML::Document.new('<element>element text</element>').elements.first
8
- assert_equal 'element text', OneLogin::RubySaml::Utils.element_text(element)
9
- end
4
+ describe ".format_cert" do
5
+ let(:formatted_certificate) do
6
+ read_certificate("formatted_certificate")
7
+ end
10
8
 
11
- it 'returns all segments of the element text' do
12
- element = REXML::Document.new('<element>element <!-- comment -->text</element>').elements.first
13
- assert_equal 'element text', OneLogin::RubySaml::Utils.element_text(element)
14
- end
9
+ it "returns empty string when the cert is an empty string" do
10
+ cert = ""
11
+ assert_equal "", OneLogin::RubySaml::Utils.format_cert(cert)
12
+ end
13
+
14
+ it "returns nil when the cert is nil" do
15
+ cert = nil
16
+ assert_equal nil, OneLogin::RubySaml::Utils.format_cert(cert)
17
+ end
18
+
19
+ it "returns the certificate when it is valid" do
20
+ assert_equal formatted_certificate, OneLogin::RubySaml::Utils.format_cert(formatted_certificate)
21
+ end
22
+
23
+ it "reformats the certificate when there are spaces and no line breaks" do
24
+ invalid_certificate1 = read_certificate("invalid_certificate1")
25
+ assert_equal formatted_certificate, OneLogin::RubySaml::Utils.format_cert(invalid_certificate1)
26
+ end
27
+
28
+ it "reformats the certificate when there are spaces and no headers" do
29
+ invalid_certificate2 = read_certificate("invalid_certificate2")
30
+ assert_equal formatted_certificate, OneLogin::RubySaml::Utils.format_cert(invalid_certificate2)
31
+ end
32
+
33
+ it "reformats the certificate when there line breaks and no headers" do
34
+ invalid_certificate3 = read_certificate("invalid_certificate3")
35
+ assert_equal formatted_certificate, OneLogin::RubySaml::Utils.format_cert(invalid_certificate3)
36
+ end
37
+ end
38
+
39
+ describe ".format_private_key" do
40
+ let(:formatted_private_key) do
41
+ read_certificate("formatted_private_key")
42
+ end
43
+
44
+ it "returns empty string when the private key is an empty string" do
45
+ private_key = ""
46
+ assert_equal "", OneLogin::RubySaml::Utils.format_private_key(private_key)
47
+ end
48
+
49
+ it "returns nil when the private key is nil" do
50
+ private_key = nil
51
+ assert_equal nil, OneLogin::RubySaml::Utils.format_private_key(private_key)
52
+ end
53
+
54
+ it "returns the private key when it is valid" do
55
+ assert_equal formatted_private_key, OneLogin::RubySaml::Utils.format_private_key(formatted_private_key)
56
+ end
57
+
58
+ it "reformats the private key when there are spaces and no line breaks" do
59
+ invalid_private_key1 = read_certificate("invalid_private_key1")
60
+ assert_equal formatted_private_key, OneLogin::RubySaml::Utils.format_private_key(invalid_private_key1)
61
+ end
15
62
 
16
- it 'returns normalized element text' do
17
- element = REXML::Document.new('<element>element &amp; text</element>').elements.first
18
- assert_equal 'element & text', OneLogin::RubySaml::Utils.element_text(element)
63
+ it "reformats the private key when there are spaces and no headers" do
64
+ invalid_private_key2 = read_certificate("invalid_private_key2")
65
+ assert_equal formatted_private_key, OneLogin::RubySaml::Utils.format_private_key(invalid_private_key2)
66
+ end
67
+
68
+ it "reformats the private key when there line breaks and no headers" do
69
+ invalid_private_key3 = read_certificate("invalid_private_key3")
70
+ assert_equal formatted_private_key, OneLogin::RubySaml::Utils.format_private_key(invalid_private_key3)
71
+ end
72
+
73
+ describe "an RSA public key" do
74
+ let(:formatted_rsa_private_key) do
75
+ read_certificate("formatted_rsa_private_key")
19
76
  end
20
77
 
21
- it 'returns the CDATA element text' do
22
- element = REXML::Document.new('<element><![CDATA[element & text]]></element>').elements.first
23
- assert_equal 'element & text', OneLogin::RubySaml::Utils.element_text(element)
78
+ it "returns the private key when it is valid" do
79
+ assert_equal formatted_rsa_private_key, OneLogin::RubySaml::Utils.format_private_key(formatted_rsa_private_key)
24
80
  end
25
81
 
26
- it 'returns the element text with newlines and additional whitespace' do
27
- element = REXML::Document.new("<element> element \n text </element>").elements.first
28
- assert_equal " element \n text ", OneLogin::RubySaml::Utils.element_text(element)
82
+ it "reformats the private key when there are spaces and no line breaks" do
83
+ invalid_rsa_private_key1 = read_certificate("invalid_rsa_private_key1")
84
+ assert_equal formatted_rsa_private_key, OneLogin::RubySaml::Utils.format_private_key(invalid_rsa_private_key1)
29
85
  end
30
86
 
31
- it 'returns nil when element is nil' do
32
- assert_nil OneLogin::RubySaml::Utils.element_text(nil)
87
+ it "reformats the private key when there are spaces and no headers" do
88
+ invalid_rsa_private_key2 = read_certificate("invalid_rsa_private_key2")
89
+ assert_equal formatted_private_key, OneLogin::RubySaml::Utils.format_private_key(invalid_rsa_private_key2)
33
90
  end
34
91
 
35
- it 'returns empty string when element has no text' do
36
- element = REXML::Document.new('<element></element>').elements.first
37
- assert_equal '', OneLogin::RubySaml::Utils.element_text(element)
92
+ it "reformats the private key when there line breaks and no headers" do
93
+ invalid_rsa_private_key3 = read_certificate("invalid_rsa_private_key3")
94
+ assert_equal formatted_private_key, OneLogin::RubySaml::Utils.format_private_key(invalid_rsa_private_key3)
38
95
  end
39
96
  end
40
97
  end
98
+
99
+ describe "build_query" do
100
+ it "returns the query string" do
101
+ params = {}
102
+ params[:type] = "SAMLRequest"
103
+ params[:data] = "PHNhbWxwOkF1dGhuUmVxdWVzdCBEZXN0aW5hdGlvbj0naHR0cDovL2V4YW1wbGUuY29tP2ZpZWxkPXZhbHVlJyBJRD0nXzk4NmUxZDEwLWVhY2ItMDEzMi01MGRkLTAwOTBmNWRlZGQ3NycgSXNzdWVJbnN0YW50PScyMDE1LTA2LTAxVDIwOjM0OjU5WicgVmVyc2lvbj0nMi4wJyB4bWxuczpzYW1sPSd1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uJyB4bWxuczpzYW1scD0ndXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sJy8+"
104
+ params[:relay_state] = "http://example.com"
105
+ params[:sig_alg] = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
106
+ query_string = OneLogin::RubySaml::Utils.build_query(params)
107
+ assert_equal "SAMLRequest=PHNhbWxwOkF1dGhuUmVxdWVzdCBEZXN0aW5hdGlvbj0naHR0cDovL2V4YW1wbGUuY29tP2ZpZWxkPXZhbHVlJyBJRD0nXzk4NmUxZDEwLWVhY2ItMDEzMi01MGRkLTAwOTBmNWRlZGQ3NycgSXNzdWVJbnN0YW50PScyMDE1LTA2LTAxVDIwOjM0OjU5WicgVmVyc2lvbj0nMi4wJyB4bWxuczpzYW1sPSd1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uJyB4bWxuczpzYW1scD0ndXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sJy8%2B&RelayState=http%3A%2F%2Fexample.com&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1", query_string
108
+ end
109
+ end
110
+
111
+ describe "#verify_signature" do
112
+ before do
113
+ @params = {}
114
+ @params[:cert] = ruby_saml_cert
115
+ @params[:sig_alg] = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
116
+ @params[:query_string] = "SAMLRequest=PHNhbWxwOkF1dGhuUmVxdWVzdCBEZXN0aW5hdGlvbj0naHR0cDovL2V4YW1wbGUuY29tP2ZpZWxkPXZhbHVlJyBJRD0nXzk4NmUxZDEwLWVhY2ItMDEzMi01MGRkLTAwOTBmNWRlZGQ3NycgSXNzdWVJbnN0YW50PScyMDE1LTA2LTAxVDIwOjM0OjU5WicgVmVyc2lvbj0nMi4wJyB4bWxuczpzYW1sPSd1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uJyB4bWxuczpzYW1scD0ndXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sJy8%2B&RelayState=http%3A%2F%2Fexample.com&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1"
117
+ end
118
+
119
+ it "returns true when the signature is valid" do
120
+ @params[:signature] = "uWJm/T4gKLYEsVu1j/ZmjDeHp9zYPXPXWTXHFJZf2KKnWg57fUw3x2l6KTyRQ+Xjigb+sfYdGnnwmIz6KngXYRnh7nO6inspRLWOwkqQFy9iR9LDlMcfpXV/0g3oAxBxO6tX8MUHqR2R62SYZRGd1rxC9apg4vQiP97+atOI8t4="
121
+ assert OneLogin::RubySaml::Utils.verify_signature(@params)
122
+ end
123
+
124
+ it "returns false when the signature is invalid" do
125
+ @params[:signature] = "uWJm/InVaLiDsVu1j/ZmjDeHp9zYPXPXWTXHFJZf2KKnWg57fUw3x2l6KTyRQ+Xjigb+sfYdGnnwmIz6KngXYRnh7nO6inspRLWOwkqQFy9iR9LDlMcfpXV/0g3oAxBxO6tX8MUHqR2R62SYZRGd1rxC9apg4vQiP97+atOI8t4="
126
+ assert !OneLogin::RubySaml::Utils.verify_signature(@params)
127
+ end
128
+ end
129
+
130
+ describe "#status_error_msg" do
131
+ it "returns a error msg with a status message" do
132
+ error_msg = "The status code of the Logout Response was not Success"
133
+ status_code = "urn:oasis:names:tc:SAML:2.0:status:Requester"
134
+ status_message = "The request could not be performed due to an error on the part of the requester."
135
+ status_error_msg = OneLogin::RubySaml::Utils.status_error_msg(error_msg, status_code, status_message)
136
+ assert_equal = "The status code of the Logout Response was not Success, was Requester -> The request could not be performed due to an error on the part of the requester.", status_error_msg
137
+
138
+ status_error_msg2 = OneLogin::RubySaml::Utils.status_error_msg(error_msg, status_code)
139
+ assert_equal = "The status code of the Logout Response was not Success, was Requester", status_error_msg2
140
+
141
+ status_error_msg3 = OneLogin::RubySaml::Utils.status_error_msg(error_msg)
142
+ assert_equal = "The status code of the Logout Response was not Success", status_error_msg3
143
+ end
144
+ end
41
145
  end
@@ -6,78 +6,125 @@ class XmlSecurityTest < Minitest::Test
6
6
  include XMLSecurity
7
7
 
8
8
  describe "XmlSecurity" do
9
+
10
+ let(:decoded_response) { Base64.decode64(response_document_without_recipient) }
11
+ let(:document) { XMLSecurity::SignedDocument.new(decoded_response) }
12
+ let(:settings) { OneLogin::RubySaml::Settings.new() }
13
+
9
14
  before do
10
- @document = XMLSecurity::SignedDocument.new(Base64.decode64(response_document))
11
- @base64cert = @document.elements["//ds:X509Certificate"].text
15
+ @base64cert = document.elements["//ds:X509Certificate"].text
12
16
  end
13
17
 
14
18
  it "should run validate without throwing NS related exceptions" do
15
- assert !@document.validate_signature(@base64cert, true)
19
+ assert !document.validate_signature(@base64cert, true)
16
20
  end
17
21
 
18
22
  it "should run validate with throwing NS related exceptions" do
19
23
  assert_raises(OneLogin::RubySaml::ValidationError) do
20
- @document.validate_signature(@base64cert, false)
24
+ document.validate_signature(@base64cert, false)
21
25
  end
22
26
  end
23
27
 
24
28
  it "not raise an error when softly validating the document multiple times" do
25
- 2.times { assert_equal @document.validate_signature(@base64cert, true), false }
29
+ 2.times { assert_equal document.validate_signature(@base64cert, true), false }
26
30
  end
27
31
 
28
32
  it "not raise an error when softly validating the document and the X509Certificate is missing" do
29
- response = Base64.decode64(response_document)
30
- response.sub!(/<ds:X509Certificate>.*<\/ds:X509Certificate>/, "")
31
- document = XMLSecurity::SignedDocument.new(response)
32
- assert !document.validate_document("a fingerprint", true) # The fingerprint isn't relevant to this test
33
+ decoded_response.sub!(/<ds:X509Certificate>.*<\/ds:X509Certificate>/, "")
34
+ mod_document = XMLSecurity::SignedDocument.new(decoded_response)
35
+ assert !mod_document.validate_document("a fingerprint", true) # The fingerprint isn't relevant to this test
33
36
  end
34
37
 
35
38
  it "should raise Fingerprint mismatch" do
36
39
  exception = assert_raises(OneLogin::RubySaml::ValidationError) do
37
- @document.validate_document("no:fi:ng:er:pr:in:t", false)
40
+ document.validate_document("no:fi:ng:er:pr:in:t", false)
38
41
  end
39
42
  assert_equal("Fingerprint mismatch", exception.message)
40
- assert @document.errors.include? "Fingerprint mismatch"
43
+ assert_includes document.errors, "Fingerprint mismatch"
41
44
  end
42
45
 
43
46
  it "should raise Digest mismatch" do
44
47
  exception = assert_raises(OneLogin::RubySaml::ValidationError) do
45
- @document.validate_signature(@base64cert, false)
48
+ document.validate_signature(@base64cert, false)
46
49
  end
47
50
  assert_equal("Digest mismatch", exception.message)
48
- assert @document.errors.include? "Digest mismatch"
51
+ assert_includes document.errors, "Digest mismatch"
49
52
  end
50
53
 
51
54
  it "should raise Key validation error" do
52
- response = Base64.decode64(response_document)
53
- response.sub!("<ds:DigestValue>pJQ7MS/ek4KRRWGmv/H43ReHYMs=</ds:DigestValue>",
55
+ decoded_response.sub!("<ds:DigestValue>pJQ7MS/ek4KRRWGmv/H43ReHYMs=</ds:DigestValue>",
54
56
  "<ds:DigestValue>b9xsAXLsynugg3Wc1CI3kpWku+0=</ds:DigestValue>")
55
- document = XMLSecurity::SignedDocument.new(response)
56
- base64cert = document.elements["//ds:X509Certificate"].text
57
+ mod_document = XMLSecurity::SignedDocument.new(decoded_response)
58
+ base64cert = mod_document.elements["//ds:X509Certificate"].text
57
59
  exception = assert_raises(OneLogin::RubySaml::ValidationError) do
58
- document.validate_signature(base64cert, false)
60
+ mod_document.validate_signature(base64cert, false)
59
61
  end
60
62
  assert_equal("Key validation error", exception.message)
61
- assert document.errors.include? "Key validation error"
63
+ assert_includes mod_document.errors, "Key validation error"
62
64
  end
63
65
 
64
66
  it "correctly obtain the digest method with alternate namespace declaration" do
65
- document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_xmlns, false))
66
- base64cert = document.elements["//X509Certificate"].text
67
- assert document.validate_signature(base64cert, false)
67
+ adfs_document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_xmlns, false))
68
+ base64cert = adfs_document.elements["//X509Certificate"].text
69
+ assert adfs_document.validate_signature(base64cert, false)
68
70
  end
69
71
 
70
72
  it "raise validation error when the X509Certificate is missing" do
71
- response = Base64.decode64(response_document)
72
- response.sub!(/<ds:X509Certificate>.*<\/ds:X509Certificate>/, "")
73
- document = XMLSecurity::SignedDocument.new(response)
73
+ decoded_response.sub!(/<ds:X509Certificate>.*<\/ds:X509Certificate>/, "")
74
+ mod_document = XMLSecurity::SignedDocument.new(decoded_response)
74
75
  exception = assert_raises(OneLogin::RubySaml::ValidationError) do
75
- document.validate_document("a fingerprint", false) # The fingerprint isn't relevant to this test
76
+ mod_document.validate_document("a fingerprint", false) # The fingerprint isn't relevant to this test
76
77
  end
77
78
  assert_equal("Certificate element missing in response (ds:X509Certificate)", exception.message)
78
79
  end
79
80
  end
80
81
 
82
+ describe "#canon_algorithm" do
83
+ it "C14N_EXCLUSIVE_1_0" do
84
+ canon_algorithm = Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
85
+ assert_equal canon_algorithm, XMLSecurity::BaseDocument.new.canon_algorithm("http://www.w3.org/2001/10/xml-exc-c14n#")
86
+ assert_equal canon_algorithm, XMLSecurity::BaseDocument.new.canon_algorithm("http://www.w3.org/2001/10/xml-exc-c14n#WithComments")
87
+ assert_equal canon_algorithm, XMLSecurity::BaseDocument.new.canon_algorithm("other")
88
+ end
89
+
90
+ it "C14N_1_0" do
91
+ canon_algorithm = Nokogiri::XML::XML_C14N_1_0
92
+ assert_equal canon_algorithm, XMLSecurity::BaseDocument.new.canon_algorithm("http://www.w3.org/TR/2001/REC-xml-c14n-20010315")
93
+ end
94
+
95
+ it "XML_C14N_1_1" do
96
+ canon_algorithm = Nokogiri::XML::XML_C14N_1_1
97
+ assert_equal canon_algorithm, XMLSecurity::BaseDocument.new.canon_algorithm("http://www.w3.org/2006/12/xml-c14n11")
98
+ end
99
+ end
100
+
101
+ describe "#algorithm" do
102
+ it "SHA1" do
103
+ alg = OpenSSL::Digest::SHA1
104
+ assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2000/09/xmldsig#rsa-sha1")
105
+ assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2000/09/xmldsig#sha1")
106
+ assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("other")
107
+ end
108
+
109
+ it "SHA256" do
110
+ alg = OpenSSL::Digest::SHA256
111
+ assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256")
112
+ assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2001/04/xmldsig-more#sha256")
113
+ end
114
+
115
+ it "SHA384" do
116
+ alg = OpenSSL::Digest::SHA384
117
+ assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha384")
118
+ assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2001/04/xmldsig-more#sha384")
119
+ end
120
+
121
+ it "SHA512" do
122
+ alg = OpenSSL::Digest::SHA512
123
+ assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512")
124
+ assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2001/04/xmldsig-more#sha512")
125
+ end
126
+ end
127
+
81
128
  describe "Fingerprint Algorithms" do
82
129
  let(:response_fingerprint_test) { OneLogin::RubySaml::Response.new(fixture(:adfs_response_sha1, false)) }
83
130
 
@@ -117,23 +164,23 @@ class XmlSecurityTest < Minitest::Test
117
164
 
118
165
  describe "Signature Algorithms" do
119
166
  it "validate using SHA1" do
120
- @document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha1, false))
121
- assert @document.validate_document("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")
167
+ document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha1, false))
168
+ assert document.validate_document("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")
122
169
  end
123
170
 
124
171
  it "validate using SHA256" do
125
- @document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha256, false))
126
- assert @document.validate_document("28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA")
172
+ document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha256, false))
173
+ assert document.validate_document("28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA")
127
174
  end
128
175
 
129
176
  it "validate using SHA384" do
130
- @document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha384, false))
131
- assert @document.validate_document("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")
177
+ document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha384, false))
178
+ assert document.validate_document("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")
132
179
  end
133
180
 
134
181
  it "validate using SHA512" do
135
- @document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha512, false))
136
- assert @document.validate_document("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")
182
+ document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha512, false))
183
+ assert document.validate_document("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")
137
184
  end
138
185
  end
139
186
 
@@ -161,12 +208,11 @@ class XmlSecurityTest < Minitest::Test
161
208
  response = OneLogin::RubySaml::Response.new(fixture("tdnf_response.xml"))
162
209
  response.stubs(:conditions).returns(nil)
163
210
  assert !response.is_valid?
164
- settings = OneLogin::RubySaml::Settings.new
165
211
  assert !response.is_valid?
166
212
  response.settings = settings
167
213
  assert !response.is_valid?
168
214
  settings.idp_cert_fingerprint = "e6 38 9a 20 b7 4f 13 db 6a bc b1 42 6a e7 52 1d d6 56 d4 1b".upcase.gsub(" ", ":")
169
- assert response.validate!
215
+ assert response.is_valid?
170
216
  end
171
217
 
172
218
  it "return an empty list when inclusive namespace element is missing" do
@@ -181,78 +227,101 @@ class XmlSecurityTest < Minitest::Test
181
227
  end
182
228
 
183
229
  describe "XMLSecurity::DSIG" do
184
- it "sign a AuthNRequest" do
185
- settings = OneLogin::RubySaml::Settings.new({
186
- :idp_sso_target_url => "https://idp.example.com/sso",
187
- :protocol_binding => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
188
- :issuer => "https://sp.example.com/saml2",
189
- :assertion_consumer_service_url => "https://sp.example.com/acs"
190
- })
230
+ before do
231
+ settings.idp_sso_target_url = "https://idp.example.com/sso"
232
+ settings.protocol_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
233
+ settings.idp_slo_target_url = "https://idp.example.com/slo",
234
+ settings.issuer = "https://sp.example.com/saml2"
235
+ settings.assertion_consumer_service_url = "https://sp.example.com/acs"
236
+ settings.single_logout_service_url = "https://sp.example.com/sls"
237
+ end
238
+
191
239
 
240
+ it "sign an AuthNRequest" do
192
241
  request = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
193
242
  request.sign_document(ruby_saml_key, ruby_saml_cert)
194
-
195
243
  # verify our signature
196
244
  signed_doc = XMLSecurity::SignedDocument.new(request.to_s)
197
245
  assert signed_doc.validate_document(ruby_saml_cert_fingerprint, false)
246
+
247
+ request2 = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
248
+ request2.sign_document(ruby_saml_key, ruby_saml_cert_text)
249
+ # verify our signature
250
+ signed_doc2 = XMLSecurity::SignedDocument.new(request2.to_s)
251
+ assert signed_doc2.validate_document(ruby_saml_cert_fingerprint, false)
198
252
  end
199
253
 
200
- it "sign a LogoutRequest" do
201
- settings = OneLogin::RubySaml::Settings.new({
202
- :idp_slo_target_url => "https://idp.example.com/slo",
203
- :protocol_binding => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
204
- :issuer => "https://sp.example.com/saml2",
205
- :single_logout_service_url => "https://sp.example.com/sls"
206
- })
207
-
208
- request = OneLogin::RubySaml::Logoutrequest.new.create_logout_request_xml_doc(settings)
209
- request.sign_document(ruby_saml_key, ruby_saml_cert)
254
+ it "sign an AuthNRequest with certificate as text" do
255
+ request = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
256
+ request.sign_document(ruby_saml_key, ruby_saml_cert_text)
210
257
 
211
258
  # verify our signature
212
259
  signed_doc = XMLSecurity::SignedDocument.new(request.to_s)
213
260
  assert signed_doc.validate_document(ruby_saml_cert_fingerprint, false)
214
261
  end
215
262
 
216
- it "sign a LogoutResponse" do
217
- settings = OneLogin::RubySaml::Settings.new({
218
- :idp_slo_target_url => "https://idp.example.com/slo",
219
- :protocol_binding => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
220
- :issuer => "https://sp.example.com/saml2",
221
- :single_logout_service_url => "https://sp.example.com/sls"
222
- })
263
+ it "sign a LogoutRequest" do
264
+ logout_request = OneLogin::RubySaml::Logoutrequest.new.create_logout_request_xml_doc(settings)
265
+ logout_request.sign_document(ruby_saml_key, ruby_saml_cert)
266
+ # verify our signature
267
+ signed_doc = XMLSecurity::SignedDocument.new(logout_request.to_s)
268
+ assert signed_doc.validate_document(ruby_saml_cert_fingerprint, false)
223
269
 
224
- response = OneLogin::RubySaml::SloLogoutresponse.new.create_logout_response_xml_doc(settings, 'request_id_example', "Custom Logout Message")
225
- response.sign_document(ruby_saml_key, ruby_saml_cert)
270
+ logout_request2 = OneLogin::RubySaml::Logoutrequest.new.create_logout_request_xml_doc(settings)
271
+ logout_request2.sign_document(ruby_saml_key, ruby_saml_cert_text)
272
+ # verify our signature
273
+ signed_doc2 = XMLSecurity::SignedDocument.new(logout_request2.to_s)
274
+ signed_doc2.validate_document(ruby_saml_cert_fingerprint, false)
275
+ assert signed_doc2.validate_document(ruby_saml_cert_fingerprint, false)
276
+ end
226
277
 
278
+ it "sign a LogoutResponse" do
279
+ logout_response = OneLogin::RubySaml::SloLogoutresponse.new.create_logout_response_xml_doc(settings, 'request_id_example', "Custom Logout Message")
280
+ logout_response.sign_document(ruby_saml_key, ruby_saml_cert)
227
281
  # verify our signature
228
- signed_doc = XMLSecurity::SignedDocument.new(response.to_s)
282
+ signed_doc = XMLSecurity::SignedDocument.new(logout_response.to_s)
229
283
  assert signed_doc.validate_document(ruby_saml_cert_fingerprint, false)
284
+
285
+ logout_response2 = OneLogin::RubySaml::SloLogoutresponse.new.create_logout_response_xml_doc(settings, 'request_id_example', "Custom Logout Message")
286
+ logout_response2.sign_document(ruby_saml_key, ruby_saml_cert_text)
287
+ # verify our signature
288
+ signed_doc2 = XMLSecurity::SignedDocument.new(logout_response2.to_s)
289
+ signed_doc2.validate_document(ruby_saml_cert_fingerprint, false)
290
+ assert signed_doc2.validate_document(ruby_saml_cert_fingerprint, false)
230
291
  end
231
292
  end
232
293
 
233
294
  describe "StarfieldTMS" do
295
+ let (:response) { OneLogin::RubySaml::Response.new(fixture(:starfield_response)) }
296
+
234
297
  before do
235
- @response = OneLogin::RubySaml::Response.new(fixture(:starfield_response))
236
- @response.settings = OneLogin::RubySaml::Settings.new(
237
- :idp_cert_fingerprint => "8D:BA:53:8E:A3:B6:F9:F1:69:6C:BB:D9:D8:BD:41:B3:AC:4F:9D:4D"
238
- )
298
+ response.settings = OneLogin::RubySaml::Settings.new( :idp_cert_fingerprint => "8D:BA:53:8E:A3:B6:F9:F1:69:6C:BB:D9:D8:BD:41:B3:AC:4F:9D:4D")
239
299
  end
240
300
 
241
301
  it "be able to validate a good response" do
242
302
  Timecop.freeze Time.parse('2012-11-28 17:55:00 UTC') do
243
- assert @response.validate!
303
+ response.stubs(:validate_subject_confirmation).returns(true)
304
+ assert response.is_valid?
244
305
  end
245
306
  end
246
307
 
247
308
  it "fail before response is valid" do
248
309
  Timecop.freeze Time.parse('2012-11-20 17:55:00 UTC') do
249
- assert ! @response.is_valid?
310
+ assert !response.is_valid?
311
+
312
+ contains_expected_error = response.errors.include? "Current time is earlier than NotBefore condition 2012-11-20 17:55:00 UTC < 2012-11-28 17:53:45 UTC)"
313
+ contains_expected_error ||= response.errors.include? "Current time is earlier than NotBefore condition Tue Nov 20 17:55:00 UTC 2012 < Wed Nov 28 17:53:45 UTC 2012)"
314
+ assert contains_expected_error
250
315
  end
251
316
  end
252
317
 
253
318
  it "fail after response expires" do
254
319
  Timecop.freeze Time.parse('2012-11-30 17:55:00 UTC') do
255
- assert ! @response.is_valid?
320
+ assert !response.is_valid?
321
+
322
+ contains_expected_error = response.errors.include? "Current time is on or after NotOnOrAfter condition (2012-11-30 17:55:00 UTC >= 2012-11-28 18:33:45 UTC)"
323
+ contains_expected_error ||= response.errors.include? "Current time is on or after NotOnOrAfter condition (Fri Nov 30 17:55:00 UTC 2012 >= Wed Nov 28 18:33:45 UTC 2012)"
324
+ assert contains_expected_error
256
325
  end
257
326
  end
258
327
  end