ruby-saml 0.9.4 → 1.0.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.
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