kl-ruby-saml 0.0.1
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.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.gitignore +14 -0
- data/.travis.yml +17 -0
- data/Gemfile +9 -0
- data/LICENSE +19 -0
- data/README.md +575 -0
- data/Rakefile +41 -0
- data/changelog.md +75 -0
- data/gemfiles/nokogiri-1.5.gemfile +5 -0
- data/lib/onelogin/ruby-saml.rb +17 -0
- data/lib/onelogin/ruby-saml/attribute_service.rb +57 -0
- data/lib/onelogin/ruby-saml/attributes.rb +128 -0
- data/lib/onelogin/ruby-saml/authrequest.rb +156 -0
- data/lib/onelogin/ruby-saml/http_error.rb +7 -0
- data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +161 -0
- data/lib/onelogin/ruby-saml/logging.rb +30 -0
- data/lib/onelogin/ruby-saml/logoutrequest.rb +131 -0
- data/lib/onelogin/ruby-saml/logoutresponse.rb +241 -0
- data/lib/onelogin/ruby-saml/metadata.rb +123 -0
- data/lib/onelogin/ruby-saml/response.rb +722 -0
- data/lib/onelogin/ruby-saml/saml_message.rb +158 -0
- data/lib/onelogin/ruby-saml/settings.rb +165 -0
- data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +258 -0
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +136 -0
- data/lib/onelogin/ruby-saml/utils.rb +172 -0
- data/lib/onelogin/ruby-saml/validation_error.rb +7 -0
- data/lib/onelogin/ruby-saml/version.rb +5 -0
- data/lib/ruby-saml.rb +1 -0
- data/lib/schemas/saml-schema-assertion-2.0.xsd +283 -0
- data/lib/schemas/saml-schema-authn-context-2.0.xsd +23 -0
- data/lib/schemas/saml-schema-authn-context-types-2.0.xsd +821 -0
- data/lib/schemas/saml-schema-metadata-2.0.xsd +337 -0
- data/lib/schemas/saml-schema-protocol-2.0.xsd +302 -0
- data/lib/schemas/sstc-metadata-attr.xsd +35 -0
- data/lib/schemas/sstc-saml-attribute-ext.xsd +25 -0
- data/lib/schemas/sstc-saml-metadata-algsupport-v1.0.xsd +41 -0
- data/lib/schemas/sstc-saml-metadata-ui-v1.0.xsd +89 -0
- data/lib/schemas/xenc-schema.xsd +136 -0
- data/lib/schemas/xml.xsd +287 -0
- data/lib/schemas/xmldsig-core-schema.xsd +309 -0
- data/lib/xml_security.rb +358 -0
- data/ruby-saml.gemspec +57 -0
- data/test/certificates/certificate1 +12 -0
- data/test/certificates/certificate_without_head_foot +1 -0
- data/test/certificates/formatted_certificate +14 -0
- data/test/certificates/formatted_private_key +12 -0
- data/test/certificates/formatted_rsa_private_key +12 -0
- data/test/certificates/invalid_certificate1 +1 -0
- data/test/certificates/invalid_certificate2 +1 -0
- data/test/certificates/invalid_certificate3 +12 -0
- data/test/certificates/invalid_private_key1 +1 -0
- data/test/certificates/invalid_private_key2 +1 -0
- data/test/certificates/invalid_private_key3 +10 -0
- data/test/certificates/invalid_rsa_private_key1 +1 -0
- data/test/certificates/invalid_rsa_private_key2 +1 -0
- data/test/certificates/invalid_rsa_private_key3 +10 -0
- data/test/certificates/ruby-saml.crt +14 -0
- data/test/certificates/ruby-saml.key +15 -0
- data/test/idp_metadata_parser_test.rb +95 -0
- data/test/logging_test.rb +62 -0
- data/test/logout_requests/invalid_slo_request.xml +6 -0
- data/test/logout_requests/slo_request.xml +4 -0
- data/test/logout_requests/slo_request.xml.base64 +1 -0
- data/test/logout_requests/slo_request_deflated.xml.base64 +1 -0
- data/test/logout_requests/slo_request_with_session_index.xml +5 -0
- data/test/logout_responses/logoutresponse_fixtures.rb +67 -0
- data/test/logoutrequest_test.rb +211 -0
- data/test/logoutresponse_test.rb +258 -0
- data/test/metadata_test.rb +203 -0
- data/test/request_test.rb +282 -0
- data/test/response_test.rb +1094 -0
- data/test/responses/adfs_response_sha1.xml +46 -0
- data/test/responses/adfs_response_sha256.xml +46 -0
- data/test/responses/adfs_response_sha384.xml +46 -0
- data/test/responses/adfs_response_sha512.xml +46 -0
- data/test/responses/adfs_response_xmlns.xml +45 -0
- data/test/responses/attackxee.xml +13 -0
- data/test/responses/idp_descriptor.xml +3 -0
- data/test/responses/invalids/invalid_audience.xml.base64 +1 -0
- data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +1 -0
- data/test/responses/invalids/invalid_issuer_message.xml.base64 +1 -0
- data/test/responses/invalids/invalid_signature_position.xml.base64 +1 -0
- data/test/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 +1 -0
- data/test/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 +1 -0
- data/test/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 +1 -0
- data/test/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 +1 -0
- data/test/responses/invalids/multiple_assertions.xml.base64 +2 -0
- data/test/responses/invalids/multiple_signed.xml.base64 +1 -0
- data/test/responses/invalids/no_id.xml.base64 +1 -0
- data/test/responses/invalids/no_saml2.xml.base64 +1 -0
- data/test/responses/invalids/no_signature.xml.base64 +1 -0
- data/test/responses/invalids/no_status.xml.base64 +1 -0
- data/test/responses/invalids/no_status_code.xml.base64 +1 -0
- data/test/responses/invalids/no_subjectconfirmation_data.xml.base64 +1 -0
- data/test/responses/invalids/no_subjectconfirmation_method.xml.base64 +1 -0
- data/test/responses/invalids/response_encrypted_attrs.xml.base64 +1 -0
- data/test/responses/invalids/response_invalid_signed_element.xml.base64 +1 -0
- data/test/responses/invalids/status_code_responder.xml.base64 +1 -0
- data/test/responses/invalids/status_code_responer_and_msg.xml.base64 +1 -0
- data/test/responses/no_signature_ns.xml +48 -0
- data/test/responses/open_saml_response.xml +56 -0
- data/test/responses/response_assertion_wrapped.xml.base64 +93 -0
- data/test/responses/response_encrypted_nameid.xml.base64 +1 -0
- data/test/responses/response_eval.xml +7 -0
- data/test/responses/response_no_cert_and_encrypted_attrs.xml +29 -0
- data/test/responses/response_unsigned_xml_base64 +1 -0
- data/test/responses/response_with_ampersands.xml +139 -0
- data/test/responses/response_with_ampersands.xml.base64 +93 -0
- data/test/responses/response_with_multiple_attribute_values.xml +67 -0
- data/test/responses/response_with_saml2_namespace.xml.base64 +102 -0
- data/test/responses/response_with_signed_assertion.xml.base64 +66 -0
- data/test/responses/response_with_signed_assertion_2.xml.base64 +1 -0
- data/test/responses/response_with_undefined_recipient.xml.base64 +1 -0
- data/test/responses/response_without_attributes.xml.base64 +79 -0
- data/test/responses/response_wrapped.xml.base64 +150 -0
- data/test/responses/signed_message_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/signed_message_encrypted_unsigned_assertion.xml.base64 +1 -0
- data/test/responses/simple_saml_php.xml +71 -0
- data/test/responses/starfield_response.xml.base64 +1 -0
- data/test/responses/test_sign.xml +43 -0
- data/test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64 +1 -0
- data/test/responses/unsigned_message_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64 +1 -0
- data/test/responses/valid_response.xml.base64 +1 -0
- data/test/saml_message_test.rb +56 -0
- data/test/settings_test.rb +218 -0
- data/test/slo_logoutrequest_test.rb +275 -0
- data/test/slo_logoutresponse_test.rb +185 -0
- data/test/test_helper.rb +252 -0
- data/test/utils_test.rb +145 -0
- data/test/xml_security_test.rb +329 -0
- metadata +415 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
|
2
|
+
|
|
3
|
+
require 'onelogin/ruby-saml/logoutrequest'
|
|
4
|
+
|
|
5
|
+
class RequestTest < Minitest::Test
|
|
6
|
+
|
|
7
|
+
describe "Logoutrequest" do
|
|
8
|
+
let(:settings) { OneLogin::RubySaml::Settings.new }
|
|
9
|
+
|
|
10
|
+
before do
|
|
11
|
+
settings.idp_slo_target_url = "http://unauth.com/logout"
|
|
12
|
+
settings.name_identifier_value = "f00f00"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "create the deflated SAMLRequest URL parameter" do
|
|
16
|
+
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings)
|
|
17
|
+
assert_match /^http:\/\/unauth\.com\/logout\?SAMLRequest=/, unauth_url
|
|
18
|
+
|
|
19
|
+
inflated = decode_saml_request_payload(unauth_url)
|
|
20
|
+
assert_match /^<samlp:LogoutRequest/, inflated
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "support additional params" do
|
|
24
|
+
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :hello => nil })
|
|
25
|
+
assert_match /&hello=$/, unauth_url
|
|
26
|
+
|
|
27
|
+
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :foo => "bar" })
|
|
28
|
+
assert_match /&foo=bar$/, unauth_url
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "set sessionindex" do
|
|
32
|
+
sessionidx = UUID.new.generate
|
|
33
|
+
settings.sessionindex = sessionidx
|
|
34
|
+
|
|
35
|
+
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :nameid => "there" })
|
|
36
|
+
inflated = decode_saml_request_payload(unauth_url)
|
|
37
|
+
|
|
38
|
+
assert_match /<samlp:SessionIndex/, inflated
|
|
39
|
+
assert_match %r(#{sessionidx}</samlp:SessionIndex>), inflated
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "set name_identifier_value" do
|
|
43
|
+
settings.name_identifier_format = "transient"
|
|
44
|
+
name_identifier_value = "abc123"
|
|
45
|
+
settings.name_identifier_value = name_identifier_value
|
|
46
|
+
|
|
47
|
+
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :nameid => "there" })
|
|
48
|
+
inflated = decode_saml_request_payload(unauth_url)
|
|
49
|
+
|
|
50
|
+
assert_match /<saml:NameID/, inflated
|
|
51
|
+
assert_match %r(#{name_identifier_value}</saml:NameID>), inflated
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
describe "when the target url doesn't contain a query string" do
|
|
55
|
+
it "create the SAMLRequest parameter correctly" do
|
|
56
|
+
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings)
|
|
57
|
+
assert_match /^http:\/\/unauth.com\/logout\?SAMLRequest/, unauth_url
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
describe "when the target url contains a query string" do
|
|
62
|
+
it "create the SAMLRequest parameter correctly" do
|
|
63
|
+
settings.idp_slo_target_url = "http://example.com?field=value"
|
|
64
|
+
|
|
65
|
+
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings)
|
|
66
|
+
assert_match /^http:\/\/example.com\?field=value&SAMLRequest/, unauth_url
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
describe "consumation of logout may need to track the transaction" do
|
|
71
|
+
it "have access to the request uuid" do
|
|
72
|
+
settings.idp_slo_target_url = "http://example.com?field=value"
|
|
73
|
+
|
|
74
|
+
unauth_req = OneLogin::RubySaml::Logoutrequest.new
|
|
75
|
+
unauth_url = unauth_req.create(settings)
|
|
76
|
+
|
|
77
|
+
inflated = decode_saml_request_payload(unauth_url)
|
|
78
|
+
assert_match %r[ID='#{unauth_req.uuid}'], inflated
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
describe "when the settings indicate to sign (embedded) logout request" do
|
|
83
|
+
|
|
84
|
+
before do
|
|
85
|
+
# sign the logout request
|
|
86
|
+
settings.security[:logout_requests_signed] = true
|
|
87
|
+
settings.security[:embed_sign] = true
|
|
88
|
+
settings.certificate = ruby_saml_cert_text
|
|
89
|
+
settings.private_key = ruby_saml_key_text
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "created a signed logout request" do
|
|
93
|
+
settings.compress_request = true
|
|
94
|
+
|
|
95
|
+
unauth_req = OneLogin::RubySaml::Logoutrequest.new
|
|
96
|
+
unauth_url = unauth_req.create(settings)
|
|
97
|
+
|
|
98
|
+
inflated = decode_saml_request_payload(unauth_url)
|
|
99
|
+
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
|
|
100
|
+
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
|
|
101
|
+
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it "create a signed logout request with 256 digest and signature method" do
|
|
105
|
+
settings.compress_request = false
|
|
106
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
|
|
107
|
+
settings.security[:digest_method] = XMLSecurity::Document::SHA256
|
|
108
|
+
|
|
109
|
+
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings)
|
|
110
|
+
request_xml = Base64.decode64(params["SAMLRequest"])
|
|
111
|
+
|
|
112
|
+
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], request_xml
|
|
113
|
+
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'/>], request_xml
|
|
114
|
+
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#sha256'/>], request_xml
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it "create a signed logout request with 512 digest and signature method RSA_SHA384" do
|
|
118
|
+
settings.compress_request = false
|
|
119
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA384
|
|
120
|
+
settings.security[:digest_method] = XMLSecurity::Document::SHA512
|
|
121
|
+
|
|
122
|
+
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings)
|
|
123
|
+
request_xml = Base64.decode64(params["SAMLRequest"])
|
|
124
|
+
|
|
125
|
+
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], request_xml
|
|
126
|
+
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#rsa-sha384'/>], request_xml
|
|
127
|
+
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#sha512'/>], request_xml
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
describe "#create_params when the settings indicate to sign the logout request" do
|
|
132
|
+
|
|
133
|
+
let(:cert) { OpenSSL::X509::Certificate.new(ruby_saml_cert_text) }
|
|
134
|
+
|
|
135
|
+
before do
|
|
136
|
+
# sign the logout request
|
|
137
|
+
settings.security[:logout_requests_signed] = true
|
|
138
|
+
settings.security[:embed_sign] = false
|
|
139
|
+
settings.certificate = ruby_saml_cert_text
|
|
140
|
+
settings.private_key = ruby_saml_key_text
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it "create a signature parameter with RSA_SHA1 / SHA1 and validate it" do
|
|
144
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
|
|
145
|
+
|
|
146
|
+
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
|
|
147
|
+
assert params['SAMLRequest']
|
|
148
|
+
assert params[:RelayState]
|
|
149
|
+
assert params['Signature']
|
|
150
|
+
assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA1
|
|
151
|
+
|
|
152
|
+
query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
|
|
153
|
+
query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
|
|
154
|
+
query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
|
|
155
|
+
|
|
156
|
+
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
|
157
|
+
assert_equal signature_algorithm, OpenSSL::Digest::SHA1
|
|
158
|
+
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it "create a signature parameter with RSA_SHA256 / SHA256 and validate it" do
|
|
162
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
|
|
163
|
+
|
|
164
|
+
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
|
|
165
|
+
assert params['Signature']
|
|
166
|
+
assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA256
|
|
167
|
+
|
|
168
|
+
query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
|
|
169
|
+
query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
|
|
170
|
+
query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
|
|
171
|
+
|
|
172
|
+
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
|
173
|
+
assert_equal signature_algorithm, OpenSSL::Digest::SHA256
|
|
174
|
+
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it "create a signature parameter with RSA_SHA384 / SHA384 and validate it" do
|
|
178
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA384
|
|
179
|
+
|
|
180
|
+
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
|
|
181
|
+
assert params['Signature']
|
|
182
|
+
assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA384
|
|
183
|
+
|
|
184
|
+
query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
|
|
185
|
+
query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
|
|
186
|
+
query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
|
|
187
|
+
|
|
188
|
+
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
|
189
|
+
assert_equal signature_algorithm, OpenSSL::Digest::SHA384
|
|
190
|
+
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
it "create a signature parameter with RSA_SHA512 / SHA512 and validate it" do
|
|
194
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA512
|
|
195
|
+
|
|
196
|
+
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
|
|
197
|
+
assert params['Signature']
|
|
198
|
+
assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA512
|
|
199
|
+
|
|
200
|
+
query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
|
|
201
|
+
query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
|
|
202
|
+
query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
|
|
203
|
+
|
|
204
|
+
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
|
205
|
+
assert_equal signature_algorithm, OpenSSL::Digest::SHA512
|
|
206
|
+
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
|
2
|
+
|
|
3
|
+
require 'onelogin/ruby-saml/logoutresponse'
|
|
4
|
+
require 'logout_responses/logoutresponse_fixtures'
|
|
5
|
+
|
|
6
|
+
class RubySamlTest < Minitest::Test
|
|
7
|
+
|
|
8
|
+
describe "Logoutresponse" do
|
|
9
|
+
|
|
10
|
+
let(:valid_logout_response_without_settings) { OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document) }
|
|
11
|
+
let(:valid_logout_response) { OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings) }
|
|
12
|
+
|
|
13
|
+
describe "#new" do
|
|
14
|
+
it "raise an exception when response is initialized with nil" do
|
|
15
|
+
assert_raises(ArgumentError) { OneLogin::RubySaml::Logoutresponse.new(nil) }
|
|
16
|
+
end
|
|
17
|
+
it "default to empty settings" do
|
|
18
|
+
assert_nil valid_logout_response_without_settings.settings
|
|
19
|
+
end
|
|
20
|
+
it "accept constructor-injected settings" do
|
|
21
|
+
refute_nil valid_logout_response.settings
|
|
22
|
+
end
|
|
23
|
+
it "accept constructor-injected options" do
|
|
24
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, nil, { :foo => :bar} )
|
|
25
|
+
assert !logoutresponse.options.empty?
|
|
26
|
+
end
|
|
27
|
+
it "support base64 encoded responses" do
|
|
28
|
+
generated_logout_response = valid_logout_response_document
|
|
29
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(Base64.encode64(generated_logout_response), settings)
|
|
30
|
+
assert_equal generated_logout_response, logoutresponse.response
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe "#validate_structure" do
|
|
35
|
+
it "invalidates when the logout response has an invalid xml" do
|
|
36
|
+
settings.soft = true
|
|
37
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(invalid_xml_logout_response_document, settings)
|
|
38
|
+
assert !logoutresponse.send(:validate_structure)
|
|
39
|
+
assert_includes logoutresponse.errors, "Invalid SAML Logout Response. Not match the saml-schema-protocol-2.0.xsd"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "raise when the logout response has an invalid xml" do
|
|
43
|
+
settings.soft = false
|
|
44
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(invalid_xml_logout_response_document, settings)
|
|
45
|
+
assert_raises OneLogin::RubySaml::ValidationError do
|
|
46
|
+
logoutresponse.send(:validate_structure)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
describe "#validate" do
|
|
52
|
+
describe "when soft=true" do
|
|
53
|
+
before do
|
|
54
|
+
settings.soft = true
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "validate the logout response" do
|
|
58
|
+
in_relation_to_request_id = random_id
|
|
59
|
+
opts = { :matches_request_id => in_relation_to_request_id}
|
|
60
|
+
|
|
61
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document({:uuid => in_relation_to_request_id}), settings, opts)
|
|
62
|
+
|
|
63
|
+
assert logoutresponse.validate
|
|
64
|
+
|
|
65
|
+
assert_equal settings.issuer, logoutresponse.issuer
|
|
66
|
+
assert_equal in_relation_to_request_id, logoutresponse.in_response_to
|
|
67
|
+
|
|
68
|
+
assert logoutresponse.success?
|
|
69
|
+
assert_empty logoutresponse.errors
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "validate the logout response extended" do
|
|
73
|
+
in_relation_to_request_id = random_id
|
|
74
|
+
settings.idp_entity_id = 'http://app.muda.no'
|
|
75
|
+
opts = { :matches_request_id => in_relation_to_request_id}
|
|
76
|
+
|
|
77
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document({:uuid => in_relation_to_request_id}), settings, opts)
|
|
78
|
+
assert logoutresponse.validate
|
|
79
|
+
assert_equal in_relation_to_request_id, logoutresponse.in_response_to
|
|
80
|
+
assert logoutresponse.success?
|
|
81
|
+
assert_empty logoutresponse.errors
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it "invalidate logout response when initiated with blank" do
|
|
85
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new("", settings)
|
|
86
|
+
assert !logoutresponse.validate
|
|
87
|
+
assert_includes logoutresponse.errors, "Blank logout response"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it "invalidate logout response when initiated with no idp cert or fingerprint" do
|
|
91
|
+
settings.idp_cert_fingerprint = nil
|
|
92
|
+
settings.idp_cert = nil
|
|
93
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings)
|
|
94
|
+
assert !logoutresponse.validate
|
|
95
|
+
assert_includes logoutresponse.errors, "No fingerprint or certificate on settings of the logout response"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it "invalidate logout response with wrong id when given option :matches_request_id" do
|
|
99
|
+
expected_request_id = "_some_other_expected_uuid"
|
|
100
|
+
opts = { :matches_request_id => expected_request_id}
|
|
101
|
+
|
|
102
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings, opts)
|
|
103
|
+
|
|
104
|
+
assert !logoutresponse.validate
|
|
105
|
+
refute_equal expected_request_id, logoutresponse.in_response_to
|
|
106
|
+
assert_includes logoutresponse.errors, "Response does not match the request ID, expected: <#{expected_request_id}>, but was: <#{logoutresponse.in_response_to}>"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "invalidate logout response with wrong request status" do
|
|
110
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
|
|
111
|
+
|
|
112
|
+
assert !logoutresponse.success?
|
|
113
|
+
assert !logoutresponse.validate
|
|
114
|
+
assert_includes logoutresponse.errors, "Bad status code. Expected <urn:oasis:names:tc:SAML:2.0:status:Success>, but was: <urn:oasis:names:tc:SAML:2.0:status:Requester>"
|
|
115
|
+
assert_includes logoutresponse.errors, "The status code of the Logout Response was not Success, was Requester"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it "invalidate logout response when in lack of issuer setting" do
|
|
119
|
+
bad_settings = settings
|
|
120
|
+
bad_settings.issuer = nil
|
|
121
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, bad_settings)
|
|
122
|
+
assert !logoutresponse.validate
|
|
123
|
+
assert_includes logoutresponse.errors, "No issuer in settings of the logout response"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it "invalidate logout response with wrong issuer" do
|
|
127
|
+
in_relation_to_request_id = random_id
|
|
128
|
+
settings.idp_entity_id = 'http://invalid.issuer.example.com/'
|
|
129
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document({:uuid => in_relation_to_request_id}), settings)
|
|
130
|
+
assert !logoutresponse.validate
|
|
131
|
+
assert_includes logoutresponse.errors, "Doesn't match the issuer, expected: <#{logoutresponse.settings.idp_entity_id}>, but was: <http://app.muda.no>"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
describe "when soft=false" do
|
|
137
|
+
before do
|
|
138
|
+
settings.soft = false
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it "validates good logout response" do
|
|
142
|
+
in_relation_to_request_id = random_id
|
|
143
|
+
|
|
144
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document({:uuid => in_relation_to_request_id}), settings)
|
|
145
|
+
assert logoutresponse.validate
|
|
146
|
+
assert_empty logoutresponse.errors
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it "raises validation error when response initiated with blank" do
|
|
150
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new("", settings)
|
|
151
|
+
|
|
152
|
+
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
|
|
153
|
+
assert_includes logoutresponse.errors, "Blank logout response"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it "raises validation error when initiated with no idp cert or fingerprint" do
|
|
157
|
+
settings.idp_cert_fingerprint = nil
|
|
158
|
+
settings.idp_cert = nil
|
|
159
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings)
|
|
160
|
+
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
|
|
161
|
+
assert_includes logoutresponse.errors, "No fingerprint or certificate on settings of the logout response"
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
it "raises validation error when matching for wrong request id" do
|
|
165
|
+
|
|
166
|
+
expected_request_id = "_some_other_expected_id"
|
|
167
|
+
opts = { :matches_request_id => expected_request_id}
|
|
168
|
+
|
|
169
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings, opts)
|
|
170
|
+
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
|
|
171
|
+
assert_includes logoutresponse.errors, "Response does not match the request ID, expected: <#{expected_request_id}>, but was: <#{logoutresponse.in_response_to}>"
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
it "raise validation error for wrong request status" do
|
|
175
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
|
|
176
|
+
|
|
177
|
+
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
|
|
178
|
+
assert_includes logoutresponse.errors, "Bad status code. Expected <urn:oasis:names:tc:SAML:2.0:status:Success>, but was: <urn:oasis:names:tc:SAML:2.0:status:Requester>"
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
it "raise validation error when in bad state" do
|
|
182
|
+
# no settings
|
|
183
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
|
|
184
|
+
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
|
|
185
|
+
assert_includes logoutresponse.errors, "Bad status code. Expected <urn:oasis:names:tc:SAML:2.0:status:Success>, but was: <urn:oasis:names:tc:SAML:2.0:status:Requester>"
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
it "raise validation error when in lack of issuer setting" do
|
|
189
|
+
settings.issuer = nil
|
|
190
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
|
|
191
|
+
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
|
|
192
|
+
assert_includes logoutresponse.errors, "No issuer in settings of the logout response"
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
it "raise validation error when logout response with wrong issuer" do
|
|
196
|
+
in_relation_to_request_id = random_id
|
|
197
|
+
settings.idp_entity_id = 'http://invalid.issuer.example.com/'
|
|
198
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document({:uuid => in_relation_to_request_id}), settings)
|
|
199
|
+
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
|
|
200
|
+
assert_includes logoutresponse.errors, "Doesn't match the issuer, expected: <#{logoutresponse.settings.idp_entity_id}>, but was: <http://app.muda.no>"
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
describe "#validate_signature" do
|
|
205
|
+
let (:params) { OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, random_id, "Custom Logout Message", :RelayState => 'http://example.com') }
|
|
206
|
+
|
|
207
|
+
before do
|
|
208
|
+
settings.soft = true
|
|
209
|
+
settings.idp_slo_target_url = "http://example.com?field=value"
|
|
210
|
+
settings.security[:logout_responses_signed] = true
|
|
211
|
+
settings.security[:embed_sign] = false
|
|
212
|
+
settings.certificate = ruby_saml_cert_text
|
|
213
|
+
settings.private_key = ruby_saml_key_text
|
|
214
|
+
settings.idp_cert = ruby_saml_cert_text
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
it "return true when valid RSA_SHA1 Signature" do
|
|
218
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
|
|
219
|
+
params['RelayState'] = params[:RelayState]
|
|
220
|
+
options = {}
|
|
221
|
+
options[:get_params] = params
|
|
222
|
+
logoutresponse_sign_test = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
|
|
223
|
+
assert logoutresponse_sign_test.send(:validate_signature)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
it "return true when valid RSA_SHA256 Signature" do
|
|
227
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
|
|
228
|
+
params['RelayState'] = params[:RelayState]
|
|
229
|
+
options = {}
|
|
230
|
+
options[:get_params] = params
|
|
231
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
|
|
232
|
+
assert logoutresponse.send(:validate_signature)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
it "return false when invalid RSA_SHA1 Signature" do
|
|
236
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
|
|
237
|
+
params['RelayState'] = 'http://invalid.example.com'
|
|
238
|
+
options = {}
|
|
239
|
+
options[:get_params] = params
|
|
240
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
|
|
241
|
+
assert !logoutresponse.send(:validate_signature)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
it "raise when invalid RSA_SHA1 Signature" do
|
|
245
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
|
|
246
|
+
settings.soft = false
|
|
247
|
+
params['RelayState'] = 'http://invalid.example.com'
|
|
248
|
+
options = {}
|
|
249
|
+
options[:get_params] = params
|
|
250
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
|
|
251
|
+
|
|
252
|
+
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.send(:validate_signature) }
|
|
253
|
+
assert logoutresponse.errors.include? "Invalid Signature on Logout Response"
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
end
|