ruby-saml 0.8.8 → 0.8.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ruby-saml might be problematic. Click here for more details.
- checksums.yaml +7 -7
- data/Gemfile +11 -1
- data/README.md +5 -2
- data/Rakefile +0 -14
- data/lib/onelogin/ruby-saml/authrequest.rb +86 -20
- data/lib/onelogin/ruby-saml/logoutrequest.rb +95 -20
- data/lib/onelogin/ruby-saml/logoutresponse.rb +5 -28
- data/lib/onelogin/ruby-saml/metadata.rb +5 -5
- data/lib/onelogin/ruby-saml/response.rb +187 -4
- data/lib/onelogin/ruby-saml/setting_error.rb +6 -0
- data/lib/onelogin/ruby-saml/settings.rb +146 -10
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +158 -0
- data/lib/onelogin/ruby-saml/utils.rb +169 -0
- data/lib/onelogin/ruby-saml/version.rb +1 -1
- data/lib/ruby-saml.rb +2 -1
- data/lib/xml_security.rb +330 -78
- data/test/certificates/ruby-saml-2.crt +15 -0
- data/test/certificates/ruby-saml.crt +14 -0
- data/test/certificates/ruby-saml.key +15 -0
- data/test/logoutrequest_test.rb +177 -44
- data/test/logoutresponse_test.rb +25 -29
- data/test/request_test.rb +100 -37
- data/test/response_test.rb +213 -111
- data/test/responses/adfs_response_xmlns.xml +45 -0
- data/test/responses/encrypted_new_attack.xml.base64 +1 -0
- data/test/responses/invalids/multiple_signed.xml.base64 +1 -0
- data/test/responses/invalids/no_signature.xml.base64 +1 -0
- data/test/responses/invalids/response_with_concealed_signed_assertion.xml +51 -0
- data/test/responses/invalids/response_with_doubled_signed_assertion.xml +49 -0
- data/test/responses/invalids/signature_wrapping_attack.xml.base64 +1 -0
- data/test/responses/logoutresponse_fixtures.rb +6 -6
- data/test/responses/response_with_concealed_signed_assertion.xml +51 -0
- data/test/responses/response_with_doubled_signed_assertion.xml +49 -0
- data/test/responses/response_with_signed_assertion_3.xml +30 -0
- data/test/responses/response_with_signed_message_and_assertion.xml +34 -0
- data/test/responses/response_with_undefined_recipient.xml.base64 +1 -0
- data/test/responses/response_wrapped.xml.base64 +150 -0
- data/test/responses/valid_response.xml.base64 +1 -0
- data/test/responses/valid_response_without_x509certificate.xml.base64 +1 -0
- data/test/settings_test.rb +7 -7
- data/test/slo_logoutresponse_test.rb +226 -0
- data/test/test_helper.rb +117 -12
- data/test/utils_test.rb +10 -10
- data/test/xml_security_test.rb +310 -68
- metadata +88 -45
data/test/utils_test.rb
CHANGED
@@ -1,38 +1,38 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
2
2
|
|
3
|
-
class UtilsTest < Test
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
class UtilsTest < Minitest::Test
|
4
|
+
describe "Utils" do
|
5
|
+
describe 'element_text' do
|
6
|
+
it 'returns the element text' do
|
7
7
|
element = REXML::Document.new('<element>element text</element>').elements.first
|
8
8
|
assert_equal 'element text', OneLogin::RubySaml::Utils.element_text(element)
|
9
9
|
end
|
10
10
|
|
11
|
-
|
11
|
+
it 'returns all segments of the element text' do
|
12
12
|
element = REXML::Document.new('<element>element <!-- comment -->text</element>').elements.first
|
13
13
|
assert_equal 'element text', OneLogin::RubySaml::Utils.element_text(element)
|
14
14
|
end
|
15
15
|
|
16
|
-
|
16
|
+
it 'returns normalized element text' do
|
17
17
|
element = REXML::Document.new('<element>element & text</element>').elements.first
|
18
18
|
assert_equal 'element & text', OneLogin::RubySaml::Utils.element_text(element)
|
19
19
|
end
|
20
20
|
|
21
|
-
|
21
|
+
it 'returns the CDATA element text' do
|
22
22
|
element = REXML::Document.new('<element><![CDATA[element & text]]></element>').elements.first
|
23
23
|
assert_equal 'element & text', OneLogin::RubySaml::Utils.element_text(element)
|
24
24
|
end
|
25
25
|
|
26
|
-
|
26
|
+
it 'returns the element text with newlines and additional whitespace' do
|
27
27
|
element = REXML::Document.new("<element> element \n text </element>").elements.first
|
28
28
|
assert_equal " element \n text ", OneLogin::RubySaml::Utils.element_text(element)
|
29
29
|
end
|
30
30
|
|
31
|
-
|
31
|
+
it 'returns nil when element is nil' do
|
32
32
|
assert_nil OneLogin::RubySaml::Utils.element_text(nil)
|
33
33
|
end
|
34
34
|
|
35
|
-
|
35
|
+
it 'returns empty string when element has no text' do
|
36
36
|
element = REXML::Document.new('<element></element>').elements.first
|
37
37
|
assert_equal '', OneLogin::RubySaml::Utils.element_text(element)
|
38
38
|
end
|
data/test/xml_security_test.rb
CHANGED
@@ -1,94 +1,197 @@
|
|
1
|
-
require
|
2
|
-
require 'xml_security'
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
3
2
|
|
4
|
-
class XmlSecurityTest < Test
|
3
|
+
class XmlSecurityTest < Minitest::Test
|
5
4
|
include XMLSecurity
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
11
14
|
end
|
12
15
|
|
13
|
-
|
14
|
-
assert
|
16
|
+
it "should run validate without throwing NS related exceptions" do
|
17
|
+
assert !document.validate_signature(@base64cert, true)
|
15
18
|
end
|
16
19
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
+
it "should run validate with throwing NS related exceptions" do
|
21
|
+
assert_raises(OneLogin::RubySaml::ValidationError) do
|
22
|
+
document.validate_signature(@base64cert, false)
|
20
23
|
end
|
21
24
|
end
|
22
25
|
|
23
|
-
|
24
|
-
|
25
|
-
2.times { @document.validate_signature(@base64cert, true) }
|
26
|
-
end
|
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 }
|
27
28
|
end
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|
34
|
+
end
|
35
|
+
|
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)
|
32
39
|
end
|
33
40
|
assert_equal("Fingerprint mismatch", exception.message)
|
34
41
|
end
|
35
42
|
|
36
|
-
|
37
|
-
exception =
|
38
|
-
|
43
|
+
it "should raise Digest mismatch" do
|
44
|
+
exception = assert_raises(OneLogin::RubySaml::ValidationError) do
|
45
|
+
document.validate_signature(@base64cert, false)
|
39
46
|
end
|
40
47
|
assert_equal("Digest mismatch", exception.message)
|
41
48
|
end
|
42
49
|
|
43
|
-
|
44
|
-
|
45
|
-
response.sub!("<ds:DigestValue>pJQ7MS/ek4KRRWGmv/H43ReHYMs=</ds:DigestValue>",
|
50
|
+
it "should raise Key validation error" do
|
51
|
+
decoded_response.sub!("<ds:DigestValue>pJQ7MS/ek4KRRWGmv/H43ReHYMs=</ds:DigestValue>",
|
46
52
|
"<ds:DigestValue>b9xsAXLsynugg3Wc1CI3kpWku+0=</ds:DigestValue>")
|
47
|
-
|
48
|
-
base64cert =
|
49
|
-
exception =
|
50
|
-
|
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)
|
51
57
|
end
|
52
58
|
assert_equal("Key validation error", exception.message)
|
53
59
|
end
|
54
60
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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)
|
65
|
+
end
|
66
|
+
|
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
|
61
72
|
end
|
62
|
-
assert_equal("Certificate element missing in response (ds:X509Certificate)", exception.message)
|
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")
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
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")
|
111
|
+
end
|
112
|
+
|
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")
|
117
|
+
end
|
118
|
+
|
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")
|
123
|
+
end
|
124
|
+
|
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")
|
63
129
|
end
|
64
130
|
end
|
65
131
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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)
|
70
144
|
end
|
71
145
|
|
72
|
-
|
73
|
-
|
74
|
-
|
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)
|
75
151
|
end
|
76
152
|
|
77
|
-
|
78
|
-
|
79
|
-
|
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)
|
80
158
|
end
|
81
159
|
|
82
|
-
|
83
|
-
|
84
|
-
|
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"
|
162
|
+
|
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)
|
85
165
|
end
|
166
|
+
|
86
167
|
end
|
87
168
|
|
88
|
-
|
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
|
89
184
|
|
90
|
-
|
91
|
-
|
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
|
92
195
|
response = fixture(:open_saml_response, false)
|
93
196
|
document = XMLSecurity::SignedDocument.new(response)
|
94
197
|
inclusive_namespaces = document.send(:extract_inclusive_namespaces)
|
@@ -96,7 +199,7 @@ class XmlSecurityTest < Test::Unit::TestCase
|
|
96
199
|
assert_equal %w[ xs ], inclusive_namespaces
|
97
200
|
end
|
98
201
|
|
99
|
-
|
202
|
+
it "support implicit namespace resolution for exclusive canonicalization" do
|
100
203
|
response = fixture(:no_signature_ns, false)
|
101
204
|
document = XMLSecurity::SignedDocument.new(response)
|
102
205
|
inclusive_namespaces = document.send(:extract_inclusive_namespaces)
|
@@ -104,57 +207,196 @@ class XmlSecurityTest < Test::Unit::TestCase
|
|
104
207
|
assert_equal %w[ #default saml ds xs xsi ], inclusive_namespaces
|
105
208
|
end
|
106
209
|
|
107
|
-
|
108
|
-
|
210
|
+
it 'support inclusive canonicalization' do
|
211
|
+
skip('test not yet implemented')
|
109
212
|
response = OneLogin::RubySaml::Response.new(fixture("tdnf_response.xml"))
|
110
213
|
response.stubs(:conditions).returns(nil)
|
111
214
|
assert !response.is_valid?
|
112
|
-
settings = OneLogin::RubySaml::Settings.new
|
113
215
|
assert !response.is_valid?
|
114
216
|
response.settings = settings
|
115
217
|
assert !response.is_valid?
|
116
218
|
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(" ", ":")
|
117
|
-
assert response.
|
219
|
+
assert response.is_valid?
|
118
220
|
end
|
119
221
|
|
120
|
-
|
222
|
+
it "return nil when inclusive namespace element is missing" do
|
121
223
|
response = fixture(:no_signature_ns, false)
|
122
224
|
response.slice! %r{<InclusiveNamespaces xmlns="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="#default saml ds xs xsi"/>}
|
123
225
|
|
124
226
|
document = XMLSecurity::SignedDocument.new(response)
|
125
227
|
inclusive_namespaces = document.send(:extract_inclusive_namespaces)
|
126
228
|
|
127
|
-
assert inclusive_namespaces.
|
229
|
+
assert inclusive_namespaces.nil?
|
128
230
|
end
|
129
231
|
end
|
130
232
|
|
131
|
-
|
132
|
-
|
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
|
242
|
+
|
243
|
+
it "sign an AuthNRequest" do
|
244
|
+
request = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
|
245
|
+
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
|
+
|
250
|
+
request2 = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
|
251
|
+
request2.sign_document(ruby_saml_key, ruby_saml_cert_text)
|
252
|
+
# verify our signature
|
253
|
+
signed_doc2 = XMLSecurity::SignedDocument.new(request2.to_s)
|
254
|
+
assert signed_doc2.validate_document(ruby_saml_cert_fingerprint, false)
|
255
|
+
end
|
256
|
+
|
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)
|
260
|
+
|
261
|
+
# verify our signature
|
262
|
+
signed_doc = XMLSecurity::SignedDocument.new(request.to_s)
|
263
|
+
assert signed_doc.validate_document(ruby_saml_cert_fingerprint, false)
|
264
|
+
end
|
265
|
+
|
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
|
280
|
+
|
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)
|
287
|
+
|
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
|
+
# 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)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
describe "StarfieldTMS" do
|
298
|
+
before do
|
133
299
|
@response = OneLogin::RubySaml::Response.new(fixture(:starfield_response))
|
134
|
-
@response.settings = OneLogin::RubySaml::Settings.new(
|
135
|
-
:idp_cert_fingerprint => "8D:BA:53:8E:A3:B6:F9:F1:69:6C:BB:D9:D8:BD:41:B3:AC:4F:9D:4D"
|
136
|
-
)
|
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")
|
137
301
|
end
|
138
302
|
|
139
|
-
|
303
|
+
it "be able to validate a good response" do
|
140
304
|
Timecop.freeze Time.parse('2012-11-28 17:55:00 UTC') do
|
141
305
|
assert @response.validate!
|
142
306
|
end
|
143
307
|
end
|
144
308
|
|
145
|
-
|
309
|
+
it "fail before response is valid" do
|
146
310
|
Timecop.freeze Time.parse('2012-11-20 17:55:00 UTC') do
|
147
311
|
assert ! @response.is_valid?
|
148
312
|
end
|
149
313
|
end
|
150
314
|
|
151
|
-
|
315
|
+
it "fail after response expires" do
|
152
316
|
Timecop.freeze Time.parse('2012-11-30 17:55:00 UTC') do
|
153
317
|
assert ! @response.is_valid?
|
154
318
|
end
|
155
319
|
end
|
156
320
|
end
|
157
321
|
|
158
|
-
|
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' }
|
159
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
|
+
end
|
377
|
+
|
378
|
+
describe '#validate_document_with_cert' do
|
379
|
+
describe 'with valid document ' do
|
380
|
+
describe 'when response has cert' do
|
381
|
+
let(:document_data) { read_response('response_with_signed_message_and_assertion.xml') }
|
382
|
+
let(:document) { OneLogin::RubySaml::Response.new(document_data).document }
|
383
|
+
let(:idp_cert) { OpenSSL::X509::Certificate.new(ruby_saml_cert_text) }
|
384
|
+
let(:fingerprint) { '4b68c453c7d994aad9025c99d5efcf566287fe8d' }
|
385
|
+
|
386
|
+
it 'is valid' do
|
387
|
+
assert document.validate_document_with_cert(idp_cert), 'Document should be valid'
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
describe 'when response has no cert but you have local cert' do
|
392
|
+
let(:document) { OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate).document }
|
393
|
+
let(:idp_cert) { OpenSSL::X509::Certificate.new(ruby_saml_cert_text) }
|
394
|
+
|
395
|
+
it 'is valid' do
|
396
|
+
assert document.validate_document_with_cert(idp_cert), 'Document should be valid'
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
401
|
+
end
|
160
402
|
end
|