ruby-saml 0.8.16 → 0.9

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 (90) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.travis.yml +1 -6
  4. data/Gemfile +2 -12
  5. data/README.md +363 -35
  6. data/Rakefile +14 -0
  7. data/changelog.md +22 -9
  8. data/lib/onelogin/ruby-saml/attribute_service.rb +34 -0
  9. data/lib/onelogin/ruby-saml/attributes.rb +26 -64
  10. data/lib/onelogin/ruby-saml/authrequest.rb +47 -89
  11. data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +87 -0
  12. data/lib/onelogin/ruby-saml/logoutrequest.rb +34 -93
  13. data/lib/onelogin/ruby-saml/logoutresponse.rb +25 -24
  14. data/lib/onelogin/ruby-saml/metadata.rb +46 -16
  15. data/lib/onelogin/ruby-saml/response.rb +62 -322
  16. data/lib/onelogin/ruby-saml/saml_message.rb +78 -0
  17. data/lib/onelogin/ruby-saml/settings.rb +54 -121
  18. data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +26 -61
  19. data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +27 -84
  20. data/lib/onelogin/ruby-saml/utils.rb +32 -199
  21. data/lib/onelogin/ruby-saml/version.rb +1 -1
  22. data/lib/ruby-saml.rb +5 -2
  23. data/lib/schemas/{saml20assertion_schema.xsd → saml-schema-assertion-2.0.xsd} +283 -283
  24. data/lib/schemas/saml-schema-authn-context-2.0.xsd +23 -0
  25. data/lib/schemas/saml-schema-authn-context-types-2.0.xsd +821 -0
  26. data/lib/schemas/saml-schema-metadata-2.0.xsd +339 -0
  27. data/lib/schemas/{saml20protocol_schema.xsd → saml-schema-protocol-2.0.xsd} +302 -302
  28. data/lib/schemas/sstc-metadata-attr.xsd +35 -0
  29. data/lib/schemas/sstc-saml-attribute-ext.xsd +25 -0
  30. data/lib/schemas/sstc-saml-metadata-algsupport-v1.0.xsd +41 -0
  31. data/lib/schemas/sstc-saml-metadata-ui-v1.0.xsd +89 -0
  32. data/lib/schemas/{xenc_schema.xsd → xenc-schema.xsd} +1 -11
  33. data/lib/schemas/xml.xsd +287 -0
  34. data/lib/schemas/{xmldsig_schema.xsd → xmldsig-core-schema.xsd} +0 -9
  35. data/lib/xml_security.rb +83 -235
  36. data/ruby-saml.gemspec +1 -0
  37. data/test/idp_metadata_parser_test.rb +54 -0
  38. data/test/logoutrequest_test.rb +68 -144
  39. data/test/logoutresponse_test.rb +43 -25
  40. data/test/metadata_test.rb +87 -0
  41. data/test/request_test.rb +103 -90
  42. data/test/response_test.rb +181 -471
  43. data/test/responses/idp_descriptor.xml +3 -0
  44. data/test/responses/logoutresponse_fixtures.rb +5 -5
  45. data/test/responses/response_no_cert_and_encrypted_attrs.xml +29 -0
  46. data/test/responses/response_with_multiple_attribute_values.xml +1 -1
  47. data/test/responses/slo_request.xml +4 -0
  48. data/test/settings_test.rb +25 -112
  49. data/test/slo_logoutrequest_test.rb +41 -44
  50. data/test/slo_logoutresponse_test.rb +87 -167
  51. data/test/test_helper.rb +27 -102
  52. data/test/xml_security_test.rb +114 -337
  53. metadata +34 -84
  54. data/lib/onelogin/ruby-saml/setting_error.rb +0 -6
  55. data/test/certificates/certificate.der +0 -0
  56. data/test/certificates/formatted_certificate +0 -14
  57. data/test/certificates/formatted_chained_certificate +0 -42
  58. data/test/certificates/formatted_private_key +0 -12
  59. data/test/certificates/formatted_rsa_private_key +0 -12
  60. data/test/certificates/invalid_certificate1 +0 -1
  61. data/test/certificates/invalid_certificate2 +0 -1
  62. data/test/certificates/invalid_certificate3 +0 -12
  63. data/test/certificates/invalid_chained_certificate1 +0 -1
  64. data/test/certificates/invalid_private_key1 +0 -1
  65. data/test/certificates/invalid_private_key2 +0 -1
  66. data/test/certificates/invalid_private_key3 +0 -10
  67. data/test/certificates/invalid_rsa_private_key1 +0 -1
  68. data/test/certificates/invalid_rsa_private_key2 +0 -1
  69. data/test/certificates/invalid_rsa_private_key3 +0 -10
  70. data/test/certificates/ruby-saml-2.crt +0 -15
  71. data/test/requests/logoutrequest_fixtures.rb +0 -47
  72. data/test/responses/encrypted_new_attack.xml.base64 +0 -1
  73. data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +0 -1
  74. data/test/responses/invalids/invalid_issuer_message.xml.base64 +0 -1
  75. data/test/responses/invalids/multiple_signed.xml.base64 +0 -1
  76. data/test/responses/invalids/no_signature.xml.base64 +0 -1
  77. data/test/responses/invalids/response_with_concealed_signed_assertion.xml +0 -51
  78. data/test/responses/invalids/response_with_doubled_signed_assertion.xml +0 -49
  79. data/test/responses/invalids/signature_wrapping_attack.xml.base64 +0 -1
  80. data/test/responses/response_node_text_attack.xml.base64 +0 -1
  81. data/test/responses/response_with_concealed_signed_assertion.xml +0 -51
  82. data/test/responses/response_with_doubled_signed_assertion.xml +0 -49
  83. data/test/responses/response_with_multiple_attribute_statements.xml +0 -72
  84. data/test/responses/response_with_signed_assertion_3.xml +0 -30
  85. data/test/responses/response_with_signed_message_and_assertion.xml +0 -34
  86. data/test/responses/response_with_undefined_recipient.xml.base64 +0 -1
  87. data/test/responses/response_wrapped.xml.base64 +0 -150
  88. data/test/responses/valid_response.xml.base64 +0 -1
  89. data/test/responses/valid_response_without_x509certificate.xml.base64 +0 -1
  90. data/test/utils_test.rb +0 -231
@@ -1,14 +1,9 @@
1
1
  require 'rubygems'
2
- require 'minitest/autorun'
3
- require 'shoulda'
2
+ require 'bundler'
3
+ require 'test/unit'
4
4
  require 'mocha/setup'
5
- require 'timecop'
6
5
 
7
- if RUBY_VERSION < '1.9'
8
- require 'uuid'
9
- else
10
- require 'securerandom'
11
- end
6
+ Bundler.require :default, :test
12
7
 
13
8
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
9
  $LOAD_PATH.unshift(File.dirname(__FILE__))
@@ -16,7 +11,7 @@ require 'ruby-saml'
16
11
 
17
12
  ENV["ruby-saml/testing"] = "1"
18
13
 
19
- class Minitest::Test
14
+ class Test::Unit::TestCase
20
15
  def fixture(document, base64 = true)
21
16
  response = Dir.glob(File.join(File.dirname(__FILE__), "responses", "#{document}*")).first
22
17
  if base64 && response =~ /\.xml$/
@@ -26,48 +21,32 @@ class Minitest::Test
26
21
  end
27
22
  end
28
23
 
29
- def random_id
30
- RUBY_VERSION < '1.9' ? "_#{UUID.new.generate}" : "_#{SecureRandom.uuid}"
31
- end
32
-
33
- def read_invalid_response(response)
34
- File.read(File.join(File.dirname(__FILE__), "responses", "invalids", response))
35
- end
36
-
37
- def read_response(response)
38
- File.read(File.join(File.dirname(__FILE__), "responses", response))
39
- end
40
-
41
- def read_certificate(certificate)
42
- File.read(File.join(File.dirname(__FILE__), "certificates", certificate))
43
- end
44
-
45
24
  def response_document
46
- @response_document ||= read_response('response1.xml.base64')
25
+ @response_document ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response1.xml.base64'))
47
26
  end
48
27
 
49
28
  def response_document_2
50
- @response_document2 ||= read_response('response2.xml.base64')
29
+ @response_document2 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response2.xml.base64'))
51
30
  end
52
31
 
53
32
  def response_document_3
54
- @response_document3 ||= read_response('response3.xml.base64')
33
+ @response_document3 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response3.xml.base64'))
55
34
  end
56
35
 
57
36
  def response_document_4
58
- @response_document4 ||= read_response('response4.xml.base64')
37
+ @response_document4 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response4.xml.base64'))
59
38
  end
60
39
 
61
40
  def response_document_5
62
- @response_document5 ||= read_response('response5.xml.base64')
41
+ @response_document5 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response5.xml.base64'))
63
42
  end
64
43
 
65
44
  def r1_response_document_6
66
- @response_document6 ||= read_response('r1_response6.xml.base64')
45
+ @response_document6 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'r1_response6.xml.base64'))
67
46
  end
68
47
 
69
48
  def ampersands_response
70
- @ampersands_resposne ||= read_response('response_with_ampersands.xml.base64')
49
+ @ampersands_response ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response_with_ampersands.xml.base64'))
71
50
  end
72
51
 
73
52
  def response_document_6
@@ -77,20 +56,8 @@ class Minitest::Test
77
56
  Base64.encode64(doc)
78
57
  end
79
58
 
80
- def response_document_wrapped
81
- @response_document_wrapped ||= read_response("response_wrapped.xml.base64")
82
- end
83
-
84
- def response_document_valid_signed
85
- response_document_valid_signed ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'valid_response.xml.base64'))
86
- end
87
-
88
- def response_document_valid_signed_without_x509certificate
89
- @response_document_valid_signed_without_x509certificate ||= read_response("valid_response_without_x509certificate.xml.base64")
90
- end
91
-
92
- def response_document_without_recipient
93
- @response_document_without_recipient ||= read_response("response_with_undefined_recipient.xml.base64")
59
+ def response_document_7
60
+ @response_document7 ||= Base64.encode64(File.read(File.join(File.dirname(__FILE__), 'responses', 'response_no_cert_and_encrypted_attrs.xml')))
94
61
  end
95
62
 
96
63
  def wrapped_response_2
@@ -101,87 +68,45 @@ class Minitest::Test
101
68
  @signature_fingerprint1 ||= "C5:19:85:D9:47:F1:BE:57:08:20:25:05:08:46:EB:27:F6:CA:B7:83"
102
69
  end
103
70
 
104
- def signature_fingerprint_valid_res
105
- @signature_fingerprint1 ||= "4b68c453c7d994aad9025c99d5efcf566287fe8d"
106
- end
107
-
108
71
  def signature_1
109
- @signature1 ||= read_certificate('certificate1')
72
+ @signature1 ||= File.read(File.join(File.dirname(__FILE__), 'certificates', 'certificate1'))
110
73
  end
111
74
 
112
75
  def r1_signature_2
113
- @signature2 ||= read_certificate('r1_certificate2_base64')
76
+ @signature2 ||= File.read(File.join(File.dirname(__FILE__), 'certificates', 'r1_certificate2_base64'))
114
77
  end
115
78
 
116
- def valid_cert
117
- @signature_valid_cert ||= read_certificate('ruby-saml.crt')
79
+ def idp_metadata
80
+ @idp_metadata ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'idp_descriptor.xml'))
118
81
  end
119
82
 
120
- def valid_key
121
- @signature_valid_cert ||= read_certificate('ruby-saml.key')
122
- end
123
-
124
- def response_with_multiple_attribute_statements
125
- @response_with_multiple_attribute_statements = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_statements))
126
- end
127
-
128
- def response_multiple_attr_values
129
- @response_multiple_attr_values = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
83
+ def logout_request_document
84
+ unless @logout_request_document
85
+ xml = File.read(File.join(File.dirname(__FILE__), 'responses', 'slo_request.xml'))
86
+ deflated = Zlib::Deflate.deflate(xml, 9)[2..-5]
87
+ @logout_request_document = Base64.encode64(deflated)
88
+ end
89
+ @logout_request_document
130
90
  end
131
91
 
132
92
  def ruby_saml_cert
133
93
  @ruby_saml_cert ||= OpenSSL::X509::Certificate.new(ruby_saml_cert_text)
134
94
  end
135
95
 
136
- def ruby_saml_cert2
137
- @ruby_saml_cert2 ||= OpenSSL::X509::Certificate.new(ruby_saml_cert_text2)
138
- end
139
-
140
96
  def ruby_saml_cert_fingerprint
141
97
  @ruby_saml_cert_fingerprint ||= Digest::SHA1.hexdigest(ruby_saml_cert.to_der).scan(/../).join(":")
142
98
  end
143
99
 
144
100
  def ruby_saml_cert_text
145
- read_certificate("ruby-saml.crt")
146
- end
147
-
148
- def ruby_saml_cert_text2
149
- read_certificate("ruby-saml-2.crt")
150
- end
151
-
152
- def ruby_saml_key_text
153
- read_certificate("ruby-saml.key")
101
+ File.read(File.join(File.dirname(__FILE__), 'certificates', 'ruby-saml.crt'))
154
102
  end
155
103
 
156
104
  def ruby_saml_key
157
105
  @ruby_saml_key ||= OpenSSL::PKey::RSA.new(ruby_saml_key_text)
158
106
  end
159
107
 
160
- def read_certificate(certificate)
161
- File.read(File.join(File.dirname(__FILE__), "certificates", certificate))
162
- end
163
-
164
- def decode_saml_request_payload(unauth_url)
165
- payload = CGI.unescape(unauth_url.split("SAMLRequest=").last)
166
- decoded = Base64.decode64(payload)
167
-
168
- zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
169
- inflated = zstream.inflate(decoded)
170
- zstream.finish
171
- zstream.close
172
- inflated
108
+ def ruby_saml_key_text
109
+ File.read(File.join(File.dirname(__FILE__), 'certificates', 'ruby-saml.key'))
173
110
  end
174
111
 
175
- # decodes a base64 encoded SAML response for use in SloLogoutresponse tests
176
- #
177
- def decode_saml_response_payload(unauth_url)
178
- payload = CGI.unescape(unauth_url.split("SAMLResponse=").last)
179
- decoded = Base64.decode64(payload)
180
-
181
- zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
182
- inflated = zstream.inflate(decoded)
183
- zstream.finish
184
- zstream.close
185
- inflated
186
- end
187
112
  end
@@ -1,197 +1,112 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
1
+ require 'test_helper'
2
+ require 'xml_security'
2
3
 
3
- class XmlSecurityTest < Minitest::Test
4
+ class XmlSecurityTest < Test::Unit::TestCase
4
5
  include XMLSecurity
5
6
 
6
- describe "XmlSecurity" do
7
-
8
- let(:decoded_response) { Base64.decode64(response_document_without_recipient) }
9
- let(:document) { XMLSecurity::SignedDocument.new(decoded_response) }
10
- let(:settings) { OneLogin::RubySaml::Settings.new() }
11
-
12
- before do
13
- @base64cert = document.elements["//ds:X509Certificate"].text
7
+ context "XmlSecurity" do
8
+ setup do
9
+ @document = XMLSecurity::SignedDocument.new(Base64.decode64(response_document))
10
+ @base64cert = @document.elements["//ds:X509Certificate"].text
14
11
  end
15
12
 
16
- it "should run validate without throwing NS related exceptions" do
17
- assert !document.validate_signature(@base64cert, true)
13
+ should "should run validate without throwing NS related exceptions" do
14
+ assert !@document.validate_signature(@base64cert, true)
18
15
  end
19
16
 
20
- it "should run validate with throwing NS related exceptions" do
21
- assert_raises(OneLogin::RubySaml::ValidationError) do
22
- document.validate_signature(@base64cert, false)
17
+ should "should run validate with throwing NS related exceptions" do
18
+ assert_raise(OneLogin::RubySaml::ValidationError) do
19
+ @document.validate_signature(@base64cert, false)
23
20
  end
24
21
  end
25
22
 
26
- it "not raise an error when softly validating the document multiple times" do
27
- 2.times { assert_equal document.validate_signature(@base64cert, true), false }
23
+ should "not raise an error when softly validating the document multiple times" do
24
+ assert_nothing_raised do
25
+ 2.times { @document.validate_signature(@base64cert, true) }
26
+ end
28
27
  end
29
28
 
30
- it "not raise an error when softly validating the document and the X509Certificate is missing" do
31
- decoded_response.sub!(/<ds:X509Certificate>.*<\/ds:X509Certificate>/, "")
32
- mod_document = XMLSecurity::SignedDocument.new(decoded_response)
33
- assert !mod_document.validate_document("a fingerprint", true) # The fingerprint isn't relevant to this test
29
+ should "not raise an error when softly validating the document and the X509Certificate is missing" do
30
+ response = Base64.decode64(response_document)
31
+ response.sub!(/<ds:X509Certificate>.*<\/ds:X509Certificate>/, "")
32
+ document = XMLSecurity::SignedDocument.new(response)
33
+ assert_nothing_raised do
34
+ assert !document.validate_document("a fingerprint", true) # The fingerprint isn't relevant to this test
35
+ end
34
36
  end
35
37
 
36
- it "should raise Fingerprint mismatch" do
37
- exception = assert_raises(OneLogin::RubySaml::ValidationError) do
38
- document.validate_document("no:fi:ng:er:pr:in:t", false)
38
+ should "should raise Fingerprint mismatch" do
39
+ exception = assert_raise(OneLogin::RubySaml::ValidationError) do
40
+ @document.validate_document("no:fi:ng:er:pr:in:t", false)
39
41
  end
40
42
  assert_equal("Fingerprint mismatch", exception.message)
43
+ assert @document.errors.include? "Fingerprint mismatch"
41
44
  end
42
45
 
43
- it "should raise Digest mismatch" do
44
- exception = assert_raises(OneLogin::RubySaml::ValidationError) do
45
- document.validate_signature(@base64cert, false)
46
+ should "should raise Digest mismatch" do
47
+ exception = assert_raise(OneLogin::RubySaml::ValidationError) do
48
+ @document.validate_signature(@base64cert, false)
46
49
  end
47
50
  assert_equal("Digest mismatch", exception.message)
51
+ assert @document.errors.include? "Digest mismatch"
48
52
  end
49
53
 
50
- it "should raise Key validation error" do
51
- decoded_response.sub!("<ds:DigestValue>pJQ7MS/ek4KRRWGmv/H43ReHYMs=</ds:DigestValue>",
54
+ should "should raise Key validation error" do
55
+ response = Base64.decode64(response_document)
56
+ response.sub!("<ds:DigestValue>pJQ7MS/ek4KRRWGmv/H43ReHYMs=</ds:DigestValue>",
52
57
  "<ds:DigestValue>b9xsAXLsynugg3Wc1CI3kpWku+0=</ds:DigestValue>")
53
- mod_document = XMLSecurity::SignedDocument.new(decoded_response)
54
- base64cert = mod_document.elements["//ds:X509Certificate"].text
55
- exception = assert_raises(OneLogin::RubySaml::ValidationError) do
56
- mod_document.validate_signature(base64cert, false)
58
+ document = XMLSecurity::SignedDocument.new(response)
59
+ base64cert = document.elements["//ds:X509Certificate"].text
60
+ exception = assert_raise(OneLogin::RubySaml::ValidationError) do
61
+ document.validate_signature(base64cert, false)
57
62
  end
58
63
  assert_equal("Key validation error", exception.message)
64
+ assert document.errors.include? "Key validation error"
59
65
  end
60
66
 
61
- it "correctly obtain the digest method with alternate namespace declaration" do
62
- adfs_document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_xmlns, false))
63
- base64cert = adfs_document.elements["//X509Certificate"].text
64
- assert adfs_document.validate_signature(base64cert, false)
67
+ should "correctly obtain the digest method with alternate namespace declaration" do
68
+ document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_xmlns, false))
69
+ base64cert = document.elements["//X509Certificate"].text
70
+ assert document.validate_signature(base64cert, false)
65
71
  end
66
72
 
67
- it "raise validation error when the X509Certificate is missing and no cert provided" do
68
- decoded_response.sub!(/<ds:X509Certificate>.*<\/ds:X509Certificate>/, "")
69
- mod_document = XMLSecurity::SignedDocument.new(decoded_response)
70
- exception = assert_raises(OneLogin::RubySaml::ValidationError) do
71
- mod_document.validate_document("a fingerprint", false) # The fingerprint isn't relevant to this test
73
+ should "raise validation error when the X509Certificate is missing" do
74
+ response = Base64.decode64(response_document)
75
+ response.sub!(/<ds:X509Certificate>.*<\/ds:X509Certificate>/, "")
76
+ document = XMLSecurity::SignedDocument.new(response)
77
+ exception = assert_raise(OneLogin::RubySaml::ValidationError) do
78
+ document.validate_document("a fingerprint", false) # The fingerprint isn't relevant to this test
72
79
  end
73
- assert_equal("Certificate element missing in response (ds:X509Certificate) and not cert provided at settings", exception.message)
74
- end
75
-
76
- it "invalidaties when the X509Certificate is missing and the cert is provided but mismatches" do
77
- decoded_response.sub!(/<ds:X509Certificate>.*<\/ds:X509Certificate>/, "")
78
- mod_document = XMLSecurity::SignedDocument.new(decoded_response)
79
- cert = OpenSSL::X509::Certificate.new(ruby_saml_cert)
80
- assert !mod_document.validate_document("a fingerprint", true, :cert => cert) # The fingerprint isn't relevant to this test
81
- end
82
- end
83
-
84
- describe "#canon_algorithm" do
85
- it "C14N_EXCLUSIVE_1_0" do
86
- canon_algorithm = Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
87
- assert_equal canon_algorithm, XMLSecurity::BaseDocument.new.canon_algorithm("http://www.w3.org/2001/10/xml-exc-c14n#")
88
- assert_equal canon_algorithm, XMLSecurity::BaseDocument.new.canon_algorithm("http://www.w3.org/2001/10/xml-exc-c14n#WithComments")
89
- assert_equal canon_algorithm, XMLSecurity::BaseDocument.new.canon_algorithm("other")
90
- end
91
-
92
- it "C14N_1_0" do
93
- canon_algorithm = Nokogiri::XML::XML_C14N_1_0
94
- assert_equal canon_algorithm, XMLSecurity::BaseDocument.new.canon_algorithm("http://www.w3.org/TR/2001/REC-xml-c14n-20010315")
95
- assert_equal canon_algorithm, XMLSecurity::BaseDocument.new.canon_algorithm("http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments")
96
- end
97
-
98
- it "XML_C14N_1_1" do
99
- canon_algorithm = Nokogiri::XML::XML_C14N_1_1
100
- assert_equal canon_algorithm, XMLSecurity::BaseDocument.new.canon_algorithm("http://www.w3.org/2006/12/xml-c14n11")
101
- assert_equal canon_algorithm, XMLSecurity::BaseDocument.new.canon_algorithm("http://www.w3.org/2006/12/xml-c14n11#WithComments")
80
+ assert_equal("Certificate element missing in response (ds:X509Certificate)", exception.message)
102
81
  end
103
82
  end
104
83
 
105
- describe "#algorithm" do
106
- it "SHA1" do
107
- alg = OpenSSL::Digest::SHA1
108
- assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2000/09/xmldsig#rsa-sha1")
109
- assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2000/09/xmldsig#sha1")
110
- assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("other")
84
+ context "Algorithms" do
85
+ should "validate using SHA1" do
86
+ @document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha1, false))
87
+ assert @document.validate_document("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")
111
88
  end
112
89
 
113
- it "SHA256" do
114
- alg = OpenSSL::Digest::SHA256
115
- assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256")
116
- assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2001/04/xmldsig-more#sha256")
90
+ should "validate using SHA256" do
91
+ @document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha256, false))
92
+ assert @document.validate_document("28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA")
117
93
  end
118
94
 
119
- it "SHA384" do
120
- alg = OpenSSL::Digest::SHA384
121
- assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha384")
122
- assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2001/04/xmldsig-more#sha384")
95
+ should "validate using SHA384" do
96
+ @document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha384, false))
97
+ assert @document.validate_document("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")
123
98
  end
124
99
 
125
- it "SHA512" do
126
- alg = OpenSSL::Digest::SHA512
127
- assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512")
128
- assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2001/04/xmldsig-more#sha512")
100
+ should "validate using SHA512" do
101
+ @document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha512, false))
102
+ assert @document.validate_document("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")
129
103
  end
130
104
  end
131
105
 
132
- describe "Fingerprint Algorithms" do
133
- let(:response_fingerprint_test) { OneLogin::RubySaml::Response.new(fixture(:adfs_response_sha1, false)) }
134
-
135
- it "validate using SHA1" do
136
- sha1_fingerprint = "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72"
137
- sha1_fingerprint_downcase = "f13c6b80905a030e6c913e5d15faddb016454872"
138
-
139
- assert response_fingerprint_test.document.validate_document(sha1_fingerprint)
140
- assert response_fingerprint_test.document.validate_document(sha1_fingerprint, true, :fingerprint_alg => XMLSecurity::Document::SHA1)
141
-
142
- assert response_fingerprint_test.document.validate_document(sha1_fingerprint_downcase)
143
- assert response_fingerprint_test.document.validate_document(sha1_fingerprint_downcase, true, :fingerprint_alg => XMLSecurity::Document::SHA1)
144
- end
145
-
146
- it "validate using SHA256" do
147
- sha256_fingerprint = "C4:C6:BD:41:EC:AD:57:97:CE:7B:7D:80:06:C3:E4:30:53:29:02:0B:DD:2D:47:02:9E:BD:85:AD:93:02:45:21"
148
-
149
- assert !response_fingerprint_test.document.validate_document(sha256_fingerprint)
150
- assert response_fingerprint_test.document.validate_document(sha256_fingerprint, true, :fingerprint_alg => XMLSecurity::Document::SHA256)
151
- end
152
-
153
- it "validate using SHA384" do
154
- sha384_fingerprint = "98:FE:17:90:31:E7:68:18:8A:65:4D:DA:F5:76:E2:09:97:BE:8B:E3:7E:AA:8D:63:64:7C:0C:38:23:9A:AC:A2:EC:CE:48:A6:74:4D:E0:4C:50:80:40:B4:8D:55:14:14"
155
-
156
- assert !response_fingerprint_test.document.validate_document(sha384_fingerprint)
157
- assert response_fingerprint_test.document.validate_document(sha384_fingerprint, true, :fingerprint_alg => XMLSecurity::Document::SHA384)
158
- end
159
-
160
- it "validate using SHA512" do
161
- sha512_fingerprint = "5A:AE:BA:D0:BA:9D:1E:25:05:01:1E:1A:C9:E9:FF:DB:ED:FA:6E:F7:52:EB:45:49:BD:DB:06:D8:A3:7E:CC:63:3A:04:A2:DD:DF:EE:61:05:D9:58:95:2A:77:17:30:4B:EB:4A:9F:48:4A:44:1C:D0:9E:0B:1E:04:77:FD:A3:D2"
106
+ context "XmlSecurity::SignedDocument" do
162
107
 
163
- assert !response_fingerprint_test.document.validate_document(sha512_fingerprint)
164
- assert response_fingerprint_test.document.validate_document(sha512_fingerprint, true, :fingerprint_alg => XMLSecurity::Document::SHA512)
165
- end
166
-
167
- end
168
-
169
- describe "Signature Algorithms" do
170
- it "validate using SHA1" do
171
- document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha1, false))
172
- assert document.validate_document("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")
173
- end
174
-
175
- it "validate using SHA256" do
176
- document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha256, false))
177
- assert document.validate_document("28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA")
178
- end
179
-
180
- it "validate using SHA384" do
181
- document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha384, false))
182
- assert document.validate_document("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")
183
- end
184
-
185
- it "validate using SHA512" do
186
- document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha512, false))
187
- assert document.validate_document("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")
188
- end
189
- end
190
-
191
- describe "XmlSecurity::SignedDocument" do
192
-
193
- describe "#extract_inclusive_namespaces" do
194
- it "support explicit namespace resolution for exclusive canonicalization" do
108
+ context "#extract_inclusive_namespaces" do
109
+ should "support explicit namespace resolution for exclusive canonicalization" do
195
110
  response = fixture(:open_saml_response, false)
196
111
  document = XMLSecurity::SignedDocument.new(response)
197
112
  inclusive_namespaces = document.send(:extract_inclusive_namespaces)
@@ -199,7 +114,7 @@ class XmlSecurityTest < Minitest::Test
199
114
  assert_equal %w[ xs ], inclusive_namespaces
200
115
  end
201
116
 
202
- it "support implicit namespace resolution for exclusive canonicalization" do
117
+ should "support implicit namespace resolution for exclusive canonicalization" do
203
118
  response = fixture(:no_signature_ns, false)
204
119
  document = XMLSecurity::SignedDocument.new(response)
205
120
  inclusive_namespaces = document.send(:extract_inclusive_namespaces)
@@ -207,245 +122,107 @@ class XmlSecurityTest < Minitest::Test
207
122
  assert_equal %w[ #default saml ds xs xsi ], inclusive_namespaces
208
123
  end
209
124
 
210
- it 'support inclusive canonicalization' do
211
- skip('test not yet implemented')
125
+ should_eventually 'support inclusive canonicalization' do
126
+
212
127
  response = OneLogin::RubySaml::Response.new(fixture("tdnf_response.xml"))
213
128
  response.stubs(:conditions).returns(nil)
214
129
  assert !response.is_valid?
130
+ settings = OneLogin::RubySaml::Settings.new
215
131
  assert !response.is_valid?
216
132
  response.settings = settings
217
133
  assert !response.is_valid?
218
134
  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(" ", ":")
219
- assert response.is_valid?
135
+ assert response.validate!
220
136
  end
221
137
 
222
- it "return nil when inclusive namespace element is missing" do
138
+ should "return an empty list when inclusive namespace element is missing" do
223
139
  response = fixture(:no_signature_ns, false)
224
140
  response.slice! %r{<InclusiveNamespaces xmlns="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="#default saml ds xs xsi"/>}
225
141
 
226
142
  document = XMLSecurity::SignedDocument.new(response)
227
143
  inclusive_namespaces = document.send(:extract_inclusive_namespaces)
228
144
 
229
- assert inclusive_namespaces.nil?
145
+ assert inclusive_namespaces.empty?
230
146
  end
231
147
  end
232
148
 
233
- describe "XMLSecurity::DSIG" do
234
- before do
235
- settings.idp_sso_target_url = "https://idp.example.com/sso"
236
- settings.protocol_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
237
- settings.idp_slo_target_url = "https://idp.example.com/slo",
238
- settings.sp_entity_id = "https://sp.example.com/saml2"
239
- settings.assertion_consumer_service_url = "https://sp.example.com/acs"
240
- settings.single_logout_service_url = "https://sp.example.com/sls"
241
- end
149
+ context "XMLSecurity::DSIG" do
150
+ should "sign a AuthNRequest" do
151
+ settings = OneLogin::RubySaml::Settings.new({
152
+ :idp_sso_target_url => "https://idp.example.com/sso",
153
+ :protocol_binding => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
154
+ :issuer => "https://sp.example.com/saml2",
155
+ :assertion_consumer_service_url => "https://sp.example.com/acs"
156
+ })
242
157
 
243
- it "sign an AuthNRequest" do
244
158
  request = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
245
159
  request.sign_document(ruby_saml_key, ruby_saml_cert)
246
- # verify our signature
247
- signed_doc = XMLSecurity::SignedDocument.new(request.to_s)
248
- assert signed_doc.validate_document(ruby_saml_cert_fingerprint, false)
249
160
 
250
- request2 = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
251
- request2.sign_document(ruby_saml_key, ruby_saml_cert_text)
252
161
  # verify our signature
253
- signed_doc2 = XMLSecurity::SignedDocument.new(request2.to_s)
254
- assert signed_doc2.validate_document(ruby_saml_cert_fingerprint, false)
162
+ signed_doc = XMLSecurity::SignedDocument.new(request.to_s)
163
+ signed_doc.validate_document(ruby_saml_cert_fingerprint, false)
255
164
  end
256
165
 
257
- it "sign an AuthNRequest with certificate as text" do
258
- request = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
259
- request.sign_document(ruby_saml_key, ruby_saml_cert_text)
166
+ should "sign a LogoutRequest" do
167
+ settings = OneLogin::RubySaml::Settings.new({
168
+ :idp_slo_target_url => "https://idp.example.com/slo",
169
+ :protocol_binding => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
170
+ :issuer => "https://sp.example.com/saml2",
171
+ :single_logout_service_url => "https://sp.example.com/sls"
172
+ })
173
+
174
+ request = OneLogin::RubySaml::Logoutrequest.new.create_logout_request_xml_doc(settings)
175
+ request.sign_document(ruby_saml_key, ruby_saml_cert)
260
176
 
261
177
  # verify our signature
262
178
  signed_doc = XMLSecurity::SignedDocument.new(request.to_s)
263
- assert signed_doc.validate_document(ruby_saml_cert_fingerprint, false)
179
+ signed_doc.validate_document(ruby_saml_cert_fingerprint, false)
264
180
  end
265
181
 
266
- it "sign a LogoutRequest" do
267
- logout_request = OneLogin::RubySaml::Logoutrequest.new.create_logout_request_xml_doc(settings)
268
- logout_request.sign_document(ruby_saml_key, ruby_saml_cert)
269
- # verify our signature
270
- signed_doc = XMLSecurity::SignedDocument.new(logout_request.to_s)
271
- assert signed_doc.validate_document(ruby_saml_cert_fingerprint, false)
272
-
273
- logout_request2 = OneLogin::RubySaml::Logoutrequest.new.create_logout_request_xml_doc(settings)
274
- logout_request2.sign_document(ruby_saml_key, ruby_saml_cert_text)
275
- # verify our signature
276
- signed_doc2 = XMLSecurity::SignedDocument.new(logout_request2.to_s)
277
- signed_doc2.validate_document(ruby_saml_cert_fingerprint, false)
278
- assert signed_doc2.validate_document(ruby_saml_cert_fingerprint, false)
279
- end
182
+ should "sign a LogoutResponse" do
183
+ settings = OneLogin::RubySaml::Settings.new({
184
+ :idp_slo_target_url => "https://idp.example.com/slo",
185
+ :protocol_binding => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
186
+ :issuer => "https://sp.example.com/saml2",
187
+ :single_logout_service_url => "https://sp.example.com/sls"
188
+ })
280
189
 
281
- it "sign a LogoutResponse" do
282
- logout_response = OneLogin::RubySaml::SloLogoutresponse.new.create_logout_response_xml_doc(settings, 'request_id_example', "Custom Logout Message")
283
- logout_response.sign_document(ruby_saml_key, ruby_saml_cert)
284
- # verify our signature
285
- signed_doc = XMLSecurity::SignedDocument.new(logout_response.to_s)
286
- assert signed_doc.validate_document(ruby_saml_cert_fingerprint, false)
190
+ response = OneLogin::RubySaml::SloLogoutresponse.new.create_logout_response_xml_doc(settings, 'request_id_example', "Custom Logout Message")
191
+ response.sign_document(ruby_saml_key, ruby_saml_cert)
287
192
 
288
- logout_response2 = OneLogin::RubySaml::SloLogoutresponse.new.create_logout_response_xml_doc(settings, 'request_id_example', "Custom Logout Message")
289
- logout_response2.sign_document(ruby_saml_key, ruby_saml_cert_text)
290
193
  # verify our signature
291
- signed_doc2 = XMLSecurity::SignedDocument.new(logout_response2.to_s)
292
- signed_doc2.validate_document(ruby_saml_cert_fingerprint, false)
293
- assert signed_doc2.validate_document(ruby_saml_cert_fingerprint, false)
194
+ signed_doc = XMLSecurity::SignedDocument.new(response.to_s)
195
+ signed_doc.validate_document(ruby_saml_cert_fingerprint, false)
294
196
  end
295
197
  end
296
198
 
297
- describe "StarfieldTMS" do
298
- before do
199
+ context "StarfieldTMS" do
200
+ setup do
299
201
  @response = OneLogin::RubySaml::Response.new(fixture(:starfield_response))
300
- @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")
202
+ @response.settings = OneLogin::RubySaml::Settings.new(
203
+ :idp_cert_fingerprint => "8D:BA:53:8E:A3:B6:F9:F1:69:6C:BB:D9:D8:BD:41:B3:AC:4F:9D:4D"
204
+ )
301
205
  end
302
206
 
303
- it "be able to validate a good response" do
207
+ should "be able to validate a good response" do
304
208
  Timecop.freeze Time.parse('2012-11-28 17:55:00 UTC') do
305
209
  assert @response.validate!
306
210
  end
307
211
  end
308
212
 
309
- it "fail before response is valid" do
213
+ should "fail before response is valid" do
310
214
  Timecop.freeze Time.parse('2012-11-20 17:55:00 UTC') do
311
215
  assert ! @response.is_valid?
312
216
  end
313
217
  end
314
218
 
315
- it "fail after response expires" do
219
+ should "fail after response expires" do
316
220
  Timecop.freeze Time.parse('2012-11-30 17:55:00 UTC') do
317
221
  assert ! @response.is_valid?
318
222
  end
319
223
  end
320
224
  end
321
225
 
322
- describe '#validate_document' do
323
- describe 'with valid document' do
324
- describe 'when response has signed message and assertion' do
325
- let(:document_data) { read_response('response_with_signed_message_and_assertion.xml') }
326
- let(:document) { OneLogin::RubySaml::Response.new(document_data).document }
327
- let(:fingerprint) { '4b68c453c7d994aad9025c99d5efcf566287fe8d' }
328
-
329
- it 'is valid' do
330
- assert document.validate_document(fingerprint, true), 'Document should be valid'
331
- end
332
- end
333
-
334
- describe 'when response has signed assertion' do
335
- let(:document_data) { read_response('response_with_signed_assertion_3.xml') }
336
- let(:document) { OneLogin::RubySaml::Response.new(document_data).document }
337
- let(:fingerprint) { '4b68c453c7d994aad9025c99d5efcf566287fe8d' }
338
-
339
- it 'is valid' do
340
- assert document.validate_document(fingerprint, true), 'Document should be valid'
341
- end
342
- end
343
- end
344
-
345
- describe 'signature_wrapping_attack' do
346
- let(:document_data) { read_invalid_response("signature_wrapping_attack.xml.base64") }
347
- let(:document) { OneLogin::RubySaml::Response.new(document_data).document }
348
- let(:fingerprint) { 'afe71c28ef740bc87425be13a2263d37971da1f9' }
349
-
350
- it 'is invalid' do
351
- assert !document.validate_document(fingerprint, true), 'Document should be invalid'
352
- end
353
- end
354
-
355
- describe 'signature wrapping attack - doubled SAML response body' do
356
- let(:document_data) { read_invalid_response("response_with_doubled_signed_assertion.xml") }
357
- let(:document) { OneLogin::RubySaml::Response.new(document_data) }
358
- let(:fingerprint) { '4b68c453c7d994aad9025c99d5efcf566287fe8d' }
359
-
360
- it 'is valid, but the unsigned information is ignored in favour of the signed information' do
361
- assert document.document.validate_document(fingerprint, true), 'Document should be valid'
362
- assert_equal 'someone@example.org', document.name_id, 'Document should expose only signed, valid details'
363
- end
364
- end
365
-
366
- describe 'signature wrapping attack - concealed SAML response body' do
367
- let(:document_data) { read_invalid_response("response_with_concealed_signed_assertion.xml") }
368
- let(:document) { OneLogin::RubySaml::Response.new(document_data) }
369
- let(:fingerprint) { '4b68c453c7d994aad9025c99d5efcf566287fe8d' }
370
-
371
- it 'is valid, but fails to retrieve information' do
372
- assert document.document.validate_document(fingerprint, true), 'Document should be valid'
373
- assert document.name_id.nil?, 'Document should expose only signed, valid details'
374
- end
375
- end
376
-
377
- describe 'when response has no cert and you provide cert' do
378
- let(:document) { OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate).document }
379
- let(:idp_cert) { OpenSSL::X509::Certificate.new(ruby_saml_cert_text) }
380
- let(:options) { {} }
381
-
382
- it 'is valid' do
383
- options[:cert] = idp_cert
384
- assert document.document.validate_document(idp_cert, true, options), 'Document should be valid'
385
- end
386
-
387
- it 'is valid if cert text instead x509cert provided' do
388
- options[:cert] = ruby_saml_cert_text
389
- assert document.document.validate_document(idp_cert, true, options), 'Document should be valid'
390
- end
391
- end
392
-
393
- describe 'when response has no cert and you dont provide cert' do
394
- let(:document) { OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate).document }
395
- let(:options) { {} }
396
- let(:idp_cert) { nil }
397
-
398
- it 'is invalid' do
399
- options[:cert] = idp_cert
400
- assert !document.document.validate_document(idp_cert, true, options), 'Document should not be valid'
401
- end
402
-
403
- it 'is invalid and error raised' do
404
- options[:cert] = idp_cert
405
- assert_raises(OneLogin::RubySaml::ValidationError) do
406
- document.document.validate_document(idp_cert, false, options)
407
- end
408
- end
409
- end
410
- end
411
-
412
- describe '#validate_document_with_cert' do
413
- describe 'with valid document ' do
414
- describe 'when response has cert' do
415
- let(:document_data) { read_response('response_with_signed_message_and_assertion.xml') }
416
- let(:document) { OneLogin::RubySaml::Response.new(document_data).document }
417
- let(:idp_cert) { OpenSSL::X509::Certificate.new(ruby_saml_cert_text) }
418
- let(:fingerprint) { '4b68c453c7d994aad9025c99d5efcf566287fe8d' }
419
-
420
- it 'is valid' do
421
- assert document.validate_document_with_cert(idp_cert), 'Document should be valid'
422
- end
423
- end
424
-
425
- describe 'when response has no cert and you provide cert' do
426
- let(:document) { OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate).document }
427
- let(:idp_cert) { OpenSSL::X509::Certificate.new(ruby_saml_cert_text) }
428
-
429
- it 'is valid' do
430
- assert document.validate_document_with_cert(idp_cert), 'Document should be valid'
431
- end
432
- end
433
-
434
- describe 'when response has no cert and you dont provide cert' do
435
- let(:document) { OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate).document }
436
- let(:idp_cert) { nil }
437
-
438
- it 'is invalid' do
439
- assert !document.validate_document_with_cert(idp_cert), 'Document should not be valid'
440
- end
441
-
442
- it 'is invalid and error raised' do
443
- assert_raises(OneLogin::RubySaml::ValidationError) do
444
- document.validate_document_with_cert(idp_cert, false)
445
- end
446
- end
447
- end
448
- end
449
- end
450
226
  end
227
+
451
228
  end