ruby-saml 0.9.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/LICENSE +1 -1
- data/README.md +71 -15
- data/changelog.md +15 -6
- data/lib/onelogin/ruby-saml.rb +1 -0
- data/lib/onelogin/ruby-saml/attribute_service.rb +25 -2
- data/lib/onelogin/ruby-saml/attributes.rb +42 -23
- data/lib/onelogin/ruby-saml/authrequest.rb +33 -8
- data/lib/onelogin/ruby-saml/http_error.rb +7 -0
- data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +65 -10
- data/lib/onelogin/ruby-saml/logging.rb +14 -10
- data/lib/onelogin/ruby-saml/logoutrequest.rb +39 -14
- data/lib/onelogin/ruby-saml/logoutresponse.rb +166 -39
- data/lib/onelogin/ruby-saml/metadata.rb +40 -23
- data/lib/onelogin/ruby-saml/response.rb +562 -88
- data/lib/onelogin/ruby-saml/saml_message.rb +80 -14
- data/lib/onelogin/ruby-saml/settings.rb +62 -23
- data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +210 -20
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +44 -13
- data/lib/onelogin/ruby-saml/utils.rb +163 -40
- data/lib/onelogin/ruby-saml/version.rb +1 -1
- data/lib/schemas/saml-schema-metadata-2.0.xsd +0 -2
- data/lib/xml_security.rb +87 -29
- data/ruby-saml.gemspec +1 -0
- data/test/certificates/{r1_certificate2_base64 → certificate_without_head_foot} +0 -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/idp_metadata_parser_test.rb +41 -4
- data/test/logging_test.rb +62 -0
- data/test/logout_requests/invalid_slo_request.xml +6 -0
- data/test/{responses → logout_requests}/slo_request.xml +0 -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/{responses → logout_responses}/logoutresponse_fixtures.rb +6 -6
- data/test/logoutrequest_test.rb +79 -52
- data/test/logoutresponse_test.rb +206 -59
- data/test/metadata_test.rb +77 -7
- data/test/request_test.rb +80 -65
- data/test/response_test.rb +862 -189
- data/test/responses/attackxee.xml +13 -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/{response4.xml.base64 → response_assertion_wrapped.xml.base64} +0 -0
- data/test/responses/response_encrypted_nameid.xml.base64 +1 -0
- data/test/responses/response_unsigned_xml_base64 +1 -0
- data/test/responses/{response5.xml.base64 → response_with_saml2_namespace.xml.base64} +0 -0
- data/test/responses/{response3.xml.base64 → response_with_signed_assertion.xml.base64} +0 -0
- data/test/responses/{r1_response6.xml.base64 → response_with_signed_assertion_2.xml.base64} +0 -0
- data/test/responses/{response1.xml.base64 → response_with_undefined_recipient.xml.base64} +0 -0
- data/test/responses/{response2.xml.base64 → response_without_attributes.xml.base64} +0 -0
- data/test/responses/{wrapped_response_2.xml.base64 → response_wrapped.xml.base64} +0 -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/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 +138 -1
- data/test/slo_logoutrequest_test.rb +239 -28
- data/test/slo_logoutresponse_test.rb +93 -71
- data/test/test_helper.rb +138 -31
- data/test/utils_test.rb +129 -25
- data/test/xml_security_test.rb +140 -71
- metadata +142 -25
- data/test/responses/response_node_text_attack.xml.base64 +0 -1
data/test/request_test.rb
CHANGED
|
@@ -5,11 +5,15 @@ require 'onelogin/ruby-saml/authrequest'
|
|
|
5
5
|
class RequestTest < Minitest::Test
|
|
6
6
|
|
|
7
7
|
describe "Authrequest" do
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
let(:settings) { OneLogin::RubySaml::Settings.new }
|
|
9
|
+
|
|
10
|
+
before do
|
|
10
11
|
settings.idp_sso_target_url = "http://example.com"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "create the deflated SAMLRequest URL parameter" do
|
|
11
15
|
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
|
12
|
-
|
|
16
|
+
assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url
|
|
13
17
|
payload = CGI.unescape(auth_url.split("=").last)
|
|
14
18
|
decoded = Base64.decode64(payload)
|
|
15
19
|
|
|
@@ -22,8 +26,6 @@ class RequestTest < Minitest::Test
|
|
|
22
26
|
end
|
|
23
27
|
|
|
24
28
|
it "create the deflated SAMLRequest URL parameter including the Destination" do
|
|
25
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
26
|
-
settings.idp_sso_target_url = "http://example.com"
|
|
27
29
|
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
|
28
30
|
payload = CGI.unescape(auth_url.split("=").last)
|
|
29
31
|
decoded = Base64.decode64(payload)
|
|
@@ -37,11 +39,9 @@ class RequestTest < Minitest::Test
|
|
|
37
39
|
end
|
|
38
40
|
|
|
39
41
|
it "create the SAMLRequest URL parameter without deflating" do
|
|
40
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
41
42
|
settings.compress_request = false
|
|
42
|
-
settings.idp_sso_target_url = "http://example.com"
|
|
43
43
|
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
|
44
|
-
|
|
44
|
+
assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url
|
|
45
45
|
payload = CGI.unescape(auth_url.split("=").last)
|
|
46
46
|
decoded = Base64.decode64(payload)
|
|
47
47
|
|
|
@@ -49,11 +49,9 @@ class RequestTest < Minitest::Test
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
it "create the SAMLRequest URL parameter with IsPassive" do
|
|
52
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
53
|
-
settings.idp_sso_target_url = "http://example.com"
|
|
54
52
|
settings.passive = true
|
|
55
53
|
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
|
56
|
-
|
|
54
|
+
assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url
|
|
57
55
|
payload = CGI.unescape(auth_url.split("=").last)
|
|
58
56
|
decoded = Base64.decode64(payload)
|
|
59
57
|
|
|
@@ -66,11 +64,9 @@ class RequestTest < Minitest::Test
|
|
|
66
64
|
end
|
|
67
65
|
|
|
68
66
|
it "create the SAMLRequest URL parameter with ProtocolBinding" do
|
|
69
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
70
|
-
settings.idp_sso_target_url = "http://example.com"
|
|
71
67
|
settings.protocol_binding = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
|
|
72
68
|
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
|
73
|
-
|
|
69
|
+
assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url
|
|
74
70
|
payload = CGI.unescape(auth_url.split("=").last)
|
|
75
71
|
decoded = Base64.decode64(payload)
|
|
76
72
|
|
|
@@ -83,11 +79,9 @@ class RequestTest < Minitest::Test
|
|
|
83
79
|
end
|
|
84
80
|
|
|
85
81
|
it "create the SAMLRequest URL parameter with AttributeConsumingServiceIndex" do
|
|
86
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
87
|
-
settings.idp_sso_target_url = "http://example.com"
|
|
88
82
|
settings.attributes_index = 30
|
|
89
83
|
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
|
90
|
-
|
|
84
|
+
assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url
|
|
91
85
|
payload = CGI.unescape(auth_url.split("=").last)
|
|
92
86
|
decoded = Base64.decode64(payload)
|
|
93
87
|
|
|
@@ -99,11 +93,9 @@ class RequestTest < Minitest::Test
|
|
|
99
93
|
end
|
|
100
94
|
|
|
101
95
|
it "create the SAMLRequest URL parameter with ForceAuthn" do
|
|
102
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
103
|
-
settings.idp_sso_target_url = "http://example.com"
|
|
104
96
|
settings.force_authn = true
|
|
105
97
|
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
|
106
|
-
|
|
98
|
+
assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url
|
|
107
99
|
payload = CGI.unescape(auth_url.split("=").last)
|
|
108
100
|
decoded = Base64.decode64(payload)
|
|
109
101
|
|
|
@@ -114,66 +106,96 @@ class RequestTest < Minitest::Test
|
|
|
114
106
|
assert_match /<samlp:AuthnRequest[^<]* ForceAuthn='true'/, inflated
|
|
115
107
|
end
|
|
116
108
|
|
|
117
|
-
it "
|
|
118
|
-
settings =
|
|
119
|
-
|
|
109
|
+
it "create the SAMLRequest URL parameter with NameID Format" do
|
|
110
|
+
settings.name_identifier_format = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
|
|
111
|
+
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
|
112
|
+
assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url
|
|
113
|
+
payload = CGI.unescape(auth_url.split("=").last)
|
|
114
|
+
decoded = Base64.decode64(payload)
|
|
115
|
+
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
|
116
|
+
inflated = zstream.inflate(decoded)
|
|
117
|
+
zstream.finish
|
|
118
|
+
zstream.close
|
|
119
|
+
|
|
120
|
+
assert_match /<samlp:NameIDPolicy[^<]* AllowCreate='true'/, inflated
|
|
121
|
+
assert_match /<samlp:NameIDPolicy[^<]* Format='urn:oasis:names:tc:SAML:2.0:nameid-format:transient'/, inflated
|
|
122
|
+
end
|
|
120
123
|
|
|
124
|
+
it "accept extra parameters" do
|
|
121
125
|
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings, { :hello => "there" })
|
|
122
|
-
|
|
126
|
+
assert_match /&hello=there$/, auth_url
|
|
123
127
|
|
|
124
128
|
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings, { :hello => nil })
|
|
125
|
-
|
|
129
|
+
assert_match /&hello=$/, auth_url
|
|
126
130
|
end
|
|
127
131
|
|
|
128
132
|
describe "when the target url doesn't contain a query string" do
|
|
129
133
|
it "create the SAMLRequest parameter correctly" do
|
|
130
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
131
|
-
settings.idp_sso_target_url = "http://example.com"
|
|
132
134
|
|
|
133
135
|
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
|
134
|
-
|
|
136
|
+
assert_match /^http:\/\/example.com\?SAMLRequest/, auth_url
|
|
135
137
|
end
|
|
136
138
|
end
|
|
137
139
|
|
|
138
140
|
describe "when the target url contains a query string" do
|
|
139
141
|
it "create the SAMLRequest parameter correctly" do
|
|
140
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
141
142
|
settings.idp_sso_target_url = "http://example.com?field=value"
|
|
142
143
|
|
|
143
144
|
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
|
144
|
-
|
|
145
|
+
assert_match /^http:\/\/example.com\?field=value&SAMLRequest/, auth_url
|
|
145
146
|
end
|
|
146
147
|
end
|
|
147
148
|
|
|
149
|
+
it "create the saml:AuthnContextClassRef element correctly" do
|
|
150
|
+
settings.authn_context = 'secure/name/password/uri'
|
|
151
|
+
auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
|
|
152
|
+
assert_match /<saml:AuthnContextClassRef>secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
it "create the saml:AuthnContextClassRef with comparison exact" do
|
|
156
|
+
settings.authn_context = 'secure/name/password/uri'
|
|
157
|
+
auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
|
|
158
|
+
assert_match /<samlp:RequestedAuthnContext[\S ]+Comparison='exact'/, auth_doc.to_s
|
|
159
|
+
assert_match /<saml:AuthnContextClassRef>secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it "create the saml:AuthnContextClassRef with comparison minimun" do
|
|
163
|
+
settings.authn_context = 'secure/name/password/uri'
|
|
164
|
+
settings.authn_context_comparison = 'minimun'
|
|
165
|
+
auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
|
|
166
|
+
assert_match /<samlp:RequestedAuthnContext[\S ]+Comparison='minimun'/, auth_doc.to_s
|
|
167
|
+
assert_match /<saml:AuthnContextClassRef>secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it "create the saml:AuthnContextDeclRef element correctly" do
|
|
171
|
+
settings.authn_context_decl_ref = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'
|
|
172
|
+
auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
|
|
173
|
+
assert_match /<saml:AuthnContextDeclRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport<\/saml:AuthnContextDeclRef>/, auth_doc.to_s
|
|
174
|
+
end
|
|
175
|
+
|
|
148
176
|
describe "#create_params when the settings indicate to sign (embebed) the request" do
|
|
149
|
-
|
|
150
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
177
|
+
before do
|
|
151
178
|
settings.compress_request = false
|
|
152
179
|
settings.idp_sso_target_url = "http://example.com?field=value"
|
|
153
180
|
settings.security[:authn_requests_signed] = true
|
|
154
181
|
settings.security[:embed_sign] = true
|
|
155
|
-
settings.certificate
|
|
182
|
+
settings.certificate = ruby_saml_cert_text
|
|
156
183
|
settings.private_key = ruby_saml_key_text
|
|
184
|
+
end
|
|
157
185
|
|
|
186
|
+
it "create a signed request" do
|
|
158
187
|
params = OneLogin::RubySaml::Authrequest.new.create_params(settings)
|
|
159
188
|
request_xml = Base64.decode64(params["SAMLRequest"])
|
|
160
189
|
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], request_xml
|
|
161
190
|
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], request_xml
|
|
162
|
-
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], request_xml
|
|
163
191
|
end
|
|
164
192
|
|
|
165
193
|
it "create a signed request with 256 digest and signature methods" do
|
|
166
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
167
|
-
settings.compress_request = false
|
|
168
|
-
settings.idp_sso_target_url = "http://example.com?field=value"
|
|
169
|
-
settings.security[:authn_requests_signed] = true
|
|
170
|
-
settings.security[:embed_sign] = true
|
|
171
194
|
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
|
|
172
195
|
settings.security[:digest_method] = XMLSecurity::Document::SHA512
|
|
173
|
-
settings.certificate = ruby_saml_cert_text
|
|
174
|
-
settings.private_key = ruby_saml_key_text
|
|
175
196
|
|
|
176
197
|
params = OneLogin::RubySaml::Authrequest.new.create_params(settings)
|
|
198
|
+
|
|
177
199
|
request_xml = Base64.decode64(params["SAMLRequest"])
|
|
178
200
|
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], request_xml
|
|
179
201
|
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'/>], request_xml
|
|
@@ -182,22 +204,22 @@ class RequestTest < Minitest::Test
|
|
|
182
204
|
end
|
|
183
205
|
|
|
184
206
|
describe "#create_params when the settings indicate to sign the request" do
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
207
|
+
let(:cert) { OpenSSL::X509::Certificate.new(ruby_saml_cert_text) }
|
|
208
|
+
|
|
209
|
+
before do
|
|
210
|
+
settings.compress_request = false
|
|
211
|
+
settings.idp_sso_target_url = "http://example.com?field=value"
|
|
212
|
+
settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign"
|
|
213
|
+
settings.security[:authn_requests_signed] = true
|
|
214
|
+
settings.security[:embed_sign] = false
|
|
215
|
+
settings.certificate = ruby_saml_cert_text
|
|
216
|
+
settings.private_key = ruby_saml_key_text
|
|
195
217
|
end
|
|
196
218
|
|
|
197
219
|
it "create a signature parameter with RSA_SHA1 and validate it" do
|
|
198
|
-
|
|
220
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
|
|
199
221
|
|
|
200
|
-
params = OneLogin::RubySaml::Authrequest.new.create_params(
|
|
222
|
+
params = OneLogin::RubySaml::Authrequest.new.create_params(settings, :RelayState => 'http://example.com')
|
|
201
223
|
assert params['SAMLRequest']
|
|
202
224
|
assert params[:RelayState]
|
|
203
225
|
assert params['Signature']
|
|
@@ -209,13 +231,14 @@ class RequestTest < Minitest::Test
|
|
|
209
231
|
|
|
210
232
|
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
|
211
233
|
assert_equal signature_algorithm, OpenSSL::Digest::SHA1
|
|
212
|
-
|
|
234
|
+
|
|
235
|
+
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
|
213
236
|
end
|
|
214
237
|
|
|
215
238
|
it "create a signature parameter with RSA_SHA256 and validate it" do
|
|
216
|
-
|
|
239
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
|
|
217
240
|
|
|
218
|
-
params = OneLogin::RubySaml::Authrequest.new.create_params(
|
|
241
|
+
params = OneLogin::RubySaml::Authrequest.new.create_params(settings, :RelayState => 'http://example.com')
|
|
219
242
|
assert params['Signature']
|
|
220
243
|
assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA256
|
|
221
244
|
|
|
@@ -225,21 +248,17 @@ class RequestTest < Minitest::Test
|
|
|
225
248
|
|
|
226
249
|
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
|
227
250
|
assert_equal signature_algorithm, OpenSSL::Digest::SHA256
|
|
228
|
-
assert
|
|
251
|
+
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
|
229
252
|
end
|
|
230
253
|
end
|
|
231
254
|
|
|
232
255
|
it "create the saml:AuthnContextClassRef element correctly" do
|
|
233
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
234
|
-
settings.idp_sso_target_url = "http://example.com"
|
|
235
256
|
settings.authn_context = 'secure/name/password/uri'
|
|
236
257
|
auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
|
|
237
258
|
assert auth_doc.to_s =~ /<saml:AuthnContextClassRef>secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/
|
|
238
259
|
end
|
|
239
260
|
|
|
240
261
|
it "create the saml:AuthnContextClassRef with comparison exact" do
|
|
241
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
242
|
-
settings.idp_sso_target_url = "http://example.com"
|
|
243
262
|
settings.authn_context = 'secure/name/password/uri'
|
|
244
263
|
auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
|
|
245
264
|
assert auth_doc.to_s =~ /<samlp:RequestedAuthnContext[\S ]+Comparison='exact'/
|
|
@@ -247,8 +266,6 @@ class RequestTest < Minitest::Test
|
|
|
247
266
|
end
|
|
248
267
|
|
|
249
268
|
it "create the saml:AuthnContextClassRef with comparison minimun" do
|
|
250
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
251
|
-
settings.idp_sso_target_url = "http://example.com"
|
|
252
269
|
settings.authn_context = 'secure/name/password/uri'
|
|
253
270
|
settings.authn_context_comparison = 'minimun'
|
|
254
271
|
auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
|
|
@@ -257,8 +274,6 @@ class RequestTest < Minitest::Test
|
|
|
257
274
|
end
|
|
258
275
|
|
|
259
276
|
it "create the saml:AuthnContextDeclRef element correctly" do
|
|
260
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
261
|
-
settings.idp_sso_target_url = "http://example.com"
|
|
262
277
|
settings.authn_context_decl_ref = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'
|
|
263
278
|
auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
|
|
264
279
|
assert auth_doc.to_s =~ /<saml:AuthnContextDeclRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport<\/saml:AuthnContextDeclRef>/
|
data/test/response_test.rb
CHANGED
|
@@ -5,6 +5,38 @@ require 'onelogin/ruby-saml/response'
|
|
|
5
5
|
class RubySamlTest < Minitest::Test
|
|
6
6
|
|
|
7
7
|
describe "Response" do
|
|
8
|
+
|
|
9
|
+
let(:settings) { OneLogin::RubySaml::Settings.new }
|
|
10
|
+
let(:response) { OneLogin::RubySaml::Response.new(response_document_without_recipient) }
|
|
11
|
+
let(:response_without_attributes) { OneLogin::RubySaml::Response.new(response_document_without_attributes) }
|
|
12
|
+
let(:response_with_signed_assertion) { OneLogin::RubySaml::Response.new(response_document_with_signed_assertion) }
|
|
13
|
+
let(:response_unsigned) { OneLogin::RubySaml::Response.new(response_document_unsigned) }
|
|
14
|
+
let(:response_wrapped) { OneLogin::RubySaml::Response.new(response_document_wrapped) }
|
|
15
|
+
let(:response_multiple_attr_values) { OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values)) }
|
|
16
|
+
let(:response_valid_signed) { OneLogin::RubySaml::Response.new(response_document_valid_signed) }
|
|
17
|
+
let(:response_no_id) { OneLogin::RubySaml::Response.new(read_invalid_response("no_id.xml.base64")) }
|
|
18
|
+
let(:response_no_version) { OneLogin::RubySaml::Response.new(read_invalid_response("no_saml2.xml.base64")) }
|
|
19
|
+
let(:response_multi_assertion) { OneLogin::RubySaml::Response.new(read_invalid_response("multiple_assertions.xml.base64")) }
|
|
20
|
+
let(:response_no_status) { OneLogin::RubySaml::Response.new(read_invalid_response("no_status.xml.base64")) }
|
|
21
|
+
let(:response_no_statuscode) { OneLogin::RubySaml::Response.new(read_invalid_response("no_status_code.xml.base64")) }
|
|
22
|
+
let(:response_statuscode_responder) { OneLogin::RubySaml::Response.new(read_invalid_response("status_code_responder.xml.base64")) }
|
|
23
|
+
let(:response_statuscode_responder_and_msg) { OneLogin::RubySaml::Response.new(read_invalid_response("status_code_responer_and_msg.xml.base64")) }
|
|
24
|
+
let(:response_encrypted_attrs) { OneLogin::RubySaml::Response.new(read_invalid_response("response_encrypted_attrs.xml.base64")) }
|
|
25
|
+
let(:response_no_signed_elements) { OneLogin::RubySaml::Response.new(read_invalid_response("no_signature.xml.base64")) }
|
|
26
|
+
let(:response_multiple_signed) { OneLogin::RubySaml::Response.new(read_invalid_response("multiple_signed.xml.base64")) }
|
|
27
|
+
let(:response_invalid_audience) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_audience.xml.base64")) }
|
|
28
|
+
let(:response_invalid_signed_element) { OneLogin::RubySaml::Response.new(read_invalid_response("response_invalid_signed_element.xml.base64")) }
|
|
29
|
+
let(:response_invalid_issuer_assertion) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_issuer_assertion.xml.base64")) }
|
|
30
|
+
let(:response_invalid_issuer_message) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_issuer_message.xml.base64")) }
|
|
31
|
+
let(:response_no_subjectconfirmation_data) { OneLogin::RubySaml::Response.new(read_invalid_response("no_subjectconfirmation_data.xml.base64")) }
|
|
32
|
+
let(:response_no_subjectconfirmation_method) { OneLogin::RubySaml::Response.new(read_invalid_response("no_subjectconfirmation_method.xml.base64")) }
|
|
33
|
+
let(:response_invalid_subjectconfirmation_inresponse) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_inresponse.xml.base64")) }
|
|
34
|
+
let(:response_invalid_subjectconfirmation_recipient) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_recipient.xml.base64")) }
|
|
35
|
+
let(:response_invalid_subjectconfirmation_nb) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_nb.xml.base64")) }
|
|
36
|
+
let(:response_invalid_subjectconfirmation_noa) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_noa.xml.base64")) }
|
|
37
|
+
let(:response_invalid_signature_position) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_signature_position.xml.base64")) }
|
|
38
|
+
let(:response_encrypted_nameid) { OneLogin::RubySaml::Response.new(response_document_encrypted_nameid) }
|
|
39
|
+
|
|
8
40
|
it "raise an exception when response is initialized with nil" do
|
|
9
41
|
assert_raises(ArgumentError) { OneLogin::RubySaml::Response.new(nil) }
|
|
10
42
|
end
|
|
@@ -13,319 +45,753 @@ class RubySamlTest < Minitest::Test
|
|
|
13
45
|
XMLSecurity::SignedDocument.any_instance.stubs(:digests_match?).returns(true)
|
|
14
46
|
OneLogin::RubySaml::Response.any_instance.stubs(:validate_conditions).returns(true)
|
|
15
47
|
|
|
16
|
-
|
|
17
|
-
settings =
|
|
18
|
-
settings.idp_cert_fingerprint = 'c51985d947f1be57082025050846eb27f6cab783'
|
|
19
|
-
|
|
20
|
-
|
|
48
|
+
ampersands_response = OneLogin::RubySaml::Response.new(ampersands_document)
|
|
49
|
+
ampersands_response.settings = settings
|
|
50
|
+
ampersands_response.settings.idp_cert_fingerprint = 'c51985d947f1be57082025050846eb27f6cab783'
|
|
51
|
+
|
|
52
|
+
assert !ampersands_response.is_valid?
|
|
53
|
+
assert_includes ampersands_response.errors, "SAML Response must contain 1 assertion"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
describe "Prevent XEE attack" do
|
|
57
|
+
before do
|
|
58
|
+
@response = OneLogin::RubySaml::Response.new(fixture(:attackxee))
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "false when evil attack vector is present, soft = true" do
|
|
62
|
+
@response.soft = true
|
|
63
|
+
assert !@response.send(:validate_structure)
|
|
64
|
+
assert_includes @response.errors, "Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "raise when evil attack vector is present, soft = false " do
|
|
68
|
+
@response.soft = false
|
|
69
|
+
|
|
70
|
+
assert_raises(OneLogin::RubySaml::ValidationError) do
|
|
71
|
+
@response.send(:validate_structure)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
21
74
|
end
|
|
22
75
|
|
|
23
76
|
it "adapt namespace" do
|
|
24
|
-
response
|
|
25
|
-
refute_nil
|
|
26
|
-
|
|
27
|
-
refute_nil response.name_id
|
|
28
|
-
response = OneLogin::RubySaml::Response.new(response_document_3)
|
|
29
|
-
refute_nil response.name_id
|
|
77
|
+
refute_nil response.nameid
|
|
78
|
+
refute_nil response_without_attributes.nameid
|
|
79
|
+
refute_nil response_with_signed_assertion.nameid
|
|
30
80
|
end
|
|
31
81
|
|
|
32
82
|
it "default to raw input when a response is not Base64 encoded" do
|
|
33
|
-
decoded = Base64.decode64(
|
|
34
|
-
|
|
35
|
-
assert
|
|
83
|
+
decoded = Base64.decode64(response_document_without_attributes)
|
|
84
|
+
response_from_raw = OneLogin::RubySaml::Response.new(decoded)
|
|
85
|
+
assert response_from_raw.document
|
|
36
86
|
end
|
|
37
87
|
|
|
38
88
|
describe "Assertion" do
|
|
39
89
|
it "only retreive an assertion with an ID that matches the signature's reference URI" do
|
|
40
|
-
|
|
41
|
-
response.stubs(:conditions).returns(nil)
|
|
42
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
90
|
+
response_wrapped.stubs(:conditions).returns(nil)
|
|
43
91
|
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
44
|
-
|
|
45
|
-
assert_nil
|
|
92
|
+
response_wrapped.settings = settings
|
|
93
|
+
assert_nil response_wrapped.nameid
|
|
46
94
|
end
|
|
47
95
|
end
|
|
48
96
|
|
|
49
|
-
describe "#
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
response.
|
|
97
|
+
describe "#is_valid?" do
|
|
98
|
+
describe "soft = false" do
|
|
99
|
+
|
|
100
|
+
before do
|
|
101
|
+
response.soft = false
|
|
102
|
+
response_valid_signed.soft = false
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "raise when response is initialized with blank data" do
|
|
106
|
+
blank_response = OneLogin::RubySaml::Response.new('')
|
|
107
|
+
blank_response.soft = false
|
|
108
|
+
error_msg = "Blank response"
|
|
109
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
110
|
+
blank_response.is_valid?
|
|
111
|
+
end
|
|
112
|
+
assert_includes blank_response.errors, error_msg
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "raise when settings have not been set" do
|
|
116
|
+
error_msg = "No settings on response"
|
|
117
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
118
|
+
response.is_valid?
|
|
119
|
+
end
|
|
120
|
+
assert_includes response.errors, error_msg
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "raise when No fingerprint or certificate on settings" do
|
|
124
|
+
settings.idp_cert_fingerprint = nil
|
|
125
|
+
settings.idp_cert = nil
|
|
126
|
+
response.settings = settings
|
|
127
|
+
error_msg = "No fingerprint or certificate on settings"
|
|
128
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
129
|
+
response.is_valid?
|
|
130
|
+
end
|
|
131
|
+
assert_includes response.errors, error_msg
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it "raise when signature wrapping attack" do
|
|
135
|
+
response_wrapped.stubs(:conditions).returns(nil)
|
|
136
|
+
response_wrapped.stubs(:validate_subject_confirmation).returns(true)
|
|
137
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
138
|
+
response_wrapped.settings = settings
|
|
139
|
+
assert !response_wrapped.is_valid?
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it "validate SAML 2.0 XML structure" do
|
|
143
|
+
resp_xml = Base64.decode64(response_document_unsigned).gsub(/emailAddress/,'test')
|
|
144
|
+
response_unsigned_mod = OneLogin::RubySaml::Response.new(Base64.encode64(resp_xml))
|
|
145
|
+
response_unsigned_mod.stubs(:conditions).returns(nil)
|
|
146
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
147
|
+
response_unsigned_mod.settings = settings
|
|
148
|
+
response_unsigned_mod.soft = false
|
|
149
|
+
assert_raises(OneLogin::RubySaml::ValidationError, 'Digest mismatch') do
|
|
150
|
+
response_unsigned_mod.is_valid?
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it "raise when encountering a condition that prevents the document from being valid" do
|
|
155
|
+
settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
|
|
156
|
+
response.settings = settings
|
|
157
|
+
response.soft = false
|
|
158
|
+
error_msg = "Current time is on or after NotOnOrAfter condition"
|
|
159
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
160
|
+
response.is_valid?
|
|
161
|
+
end
|
|
162
|
+
assert_includes response.errors[0], error_msg
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it "raise when encountering a SAML Response with bad formatted" do
|
|
166
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
167
|
+
response_without_attributes.settings = settings
|
|
168
|
+
response_without_attributes.soft = false
|
|
169
|
+
assert_raises(OneLogin::RubySaml::ValidationError) do
|
|
170
|
+
response_without_attributes.is_valid?
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
it "raise when the inResponseTo value does not match the Request ID" do
|
|
175
|
+
settings.soft = false
|
|
176
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
177
|
+
opts = {}
|
|
178
|
+
opts[:settings] = settings
|
|
179
|
+
opts[:matches_request_id] = "invalid_request_id"
|
|
180
|
+
response_valid_signed = OneLogin::RubySaml::Response.new(response_document_valid_signed, opts)
|
|
181
|
+
error_msg = "The InResponseTo of the Response: _fc4a34b0-7efb-012e-caae-782bcb13bb38, does not match the ID of the AuthNRequest sent by the SP: invalid_request_id"
|
|
182
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
183
|
+
response_valid_signed.is_valid?
|
|
184
|
+
end
|
|
185
|
+
assert_includes response_valid_signed.errors, error_msg
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
it "raise when the assertion contains encrypted attributes" do
|
|
189
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
190
|
+
response_encrypted_attrs.settings = settings
|
|
191
|
+
response_encrypted_attrs.soft = false
|
|
192
|
+
error_msg = "There is an EncryptedAttribute in the Response and this SP not support them"
|
|
193
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
194
|
+
response_encrypted_attrs.is_valid?
|
|
195
|
+
end
|
|
196
|
+
assert_includes response_encrypted_attrs.errors, error_msg
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
it "raise when there is no valid audience" do
|
|
200
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
201
|
+
settings.issuer = 'invalid'
|
|
202
|
+
response_valid_signed.settings = settings
|
|
203
|
+
response_valid_signed.soft = false
|
|
204
|
+
error_msg = "#{response_valid_signed.settings.issuer} is not a valid audience for this Response"
|
|
205
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
206
|
+
response_valid_signed.is_valid?
|
|
207
|
+
end
|
|
208
|
+
assert_includes response_valid_signed.errors, error_msg
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
it "raise when no ID present in the SAML Response" do
|
|
212
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
213
|
+
response_no_id.settings = settings
|
|
214
|
+
response_no_id.soft = false
|
|
215
|
+
error_msg = "Missing ID attribute on SAML Response"
|
|
216
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
217
|
+
response_no_id.is_valid?
|
|
218
|
+
end
|
|
219
|
+
assert_includes response_no_id.errors, error_msg
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
it "raise when no 2.0 Version present in the SAML Response" do
|
|
223
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
224
|
+
response_no_version.settings = settings
|
|
225
|
+
response_no_version.soft = false
|
|
226
|
+
error_msg = "Unsupported SAML version"
|
|
227
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
228
|
+
response_no_version.is_valid?
|
|
229
|
+
end
|
|
230
|
+
assert_includes response_no_version.errors, error_msg
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
describe "soft = true" do
|
|
235
|
+
before do
|
|
236
|
+
response.soft = true
|
|
237
|
+
response_valid_signed.soft = true
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
it "return true when the response is initialized with valid data" do
|
|
241
|
+
response_valid_signed.stubs(:conditions).returns(nil)
|
|
242
|
+
response_valid_signed.settings = settings
|
|
243
|
+
response_valid_signed.settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
|
|
244
|
+
assert response_valid_signed.is_valid?
|
|
245
|
+
assert_empty response_valid_signed.errors
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
it "return true when the response is initialized with valid data and using certificate instead of fingerprint" do
|
|
249
|
+
response_valid_signed.stubs(:conditions).returns(nil)
|
|
250
|
+
response_valid_signed.settings = settings
|
|
251
|
+
response_valid_signed.settings.idp_cert = ruby_saml_cert_text
|
|
252
|
+
assert response_valid_signed.is_valid?
|
|
253
|
+
assert_empty response_valid_signed.errors
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
it "return false when response is initialized with blank data" do
|
|
257
|
+
blank_response = OneLogin::RubySaml::Response.new('')
|
|
258
|
+
blank_response.soft = true
|
|
259
|
+
assert !blank_response.is_valid?
|
|
260
|
+
assert_includes blank_response.errors, "Blank response"
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
it "return false if settings have not been set" do
|
|
264
|
+
assert !response.is_valid?
|
|
265
|
+
assert_includes response.errors, "No settings on response"
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
it "return false if fingerprint or certificate not been set on settings" do
|
|
269
|
+
response.settings = settings
|
|
270
|
+
assert !response.is_valid?
|
|
271
|
+
assert_includes response.errors, "No fingerprint or certificate on settings"
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
it "should be idempotent when the response is initialized with invalid data" do
|
|
275
|
+
response_unsigned.stubs(:conditions).returns(nil)
|
|
276
|
+
response_unsigned.settings = settings
|
|
277
|
+
assert !response_unsigned.is_valid?
|
|
278
|
+
assert !response_unsigned.is_valid?
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
it "should be idempotent when the response is initialized with valid data" do
|
|
282
|
+
response_valid_signed.stubs(:conditions).returns(nil)
|
|
283
|
+
response_valid_signed.settings = settings
|
|
284
|
+
response_valid_signed.settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
|
|
285
|
+
assert response_valid_signed.is_valid?
|
|
286
|
+
assert response_valid_signed.is_valid?
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
it "not allow signature wrapping attack" do
|
|
290
|
+
response_wrapped.stubs(:conditions).returns(nil)
|
|
291
|
+
response_wrapped.stubs(:validate_subject_confirmation).returns(true)
|
|
292
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
293
|
+
response_wrapped.settings = settings
|
|
294
|
+
assert !response_wrapped.is_valid?
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
it "support dynamic namespace resolution on signature elements" do
|
|
298
|
+
no_signature_response = OneLogin::RubySaml::Response.new(fixture("no_signature_ns.xml"))
|
|
299
|
+
no_signature_response.stubs(:conditions).returns(nil)
|
|
300
|
+
no_signature_response.stubs(:validate_subject_confirmation).returns(true)
|
|
301
|
+
no_signature_response.settings = settings
|
|
302
|
+
no_signature_response.settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA"
|
|
303
|
+
XMLSecurity::SignedDocument.any_instance.expects(:validate_signature).returns(true)
|
|
304
|
+
assert no_signature_response.is_valid?
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
it "validate ADFS assertions" do
|
|
308
|
+
adfs_response = OneLogin::RubySaml::Response.new(fixture(:adfs_response_sha256))
|
|
309
|
+
adfs_response.stubs(:conditions).returns(nil)
|
|
310
|
+
adfs_response.stubs(:validate_subject_confirmation).returns(true)
|
|
311
|
+
settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA"
|
|
312
|
+
adfs_response.settings = settings
|
|
313
|
+
adfs_response.soft = true
|
|
314
|
+
assert adfs_response.is_valid?
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
it "validate SAML 2.0 XML structure" do
|
|
318
|
+
resp_xml = Base64.decode64(response_document_unsigned).gsub(/emailAddress/,'test')
|
|
319
|
+
response_unsigned_mod = OneLogin::RubySaml::Response.new(Base64.encode64(resp_xml))
|
|
320
|
+
response_unsigned_mod.stubs(:conditions).returns(nil)
|
|
321
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
322
|
+
response_unsigned_mod.settings = settings
|
|
323
|
+
response_unsigned_mod.soft = true
|
|
324
|
+
assert !response_unsigned_mod.is_valid?
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
it "return false when encountering a condition that prevents the document from being valid" do
|
|
328
|
+
settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
|
|
329
|
+
response.settings = settings
|
|
330
|
+
error_msg = "Current time is on or after NotOnOrAfter condition"
|
|
331
|
+
assert !response.is_valid?
|
|
332
|
+
assert_includes response.errors[0], "Current time is on or after NotOnOrAfter condition"
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
it "return false when encountering a SAML Response with bad formatted" do
|
|
336
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
337
|
+
response_without_attributes.settings = settings
|
|
338
|
+
response_without_attributes.soft = true
|
|
339
|
+
error_msg = "Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd"
|
|
340
|
+
response_without_attributes.is_valid?
|
|
341
|
+
assert_includes response_without_attributes.errors, error_msg
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
it "return false when the inResponseTo value does not match the Request ID" do
|
|
345
|
+
settings.soft = true
|
|
346
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
347
|
+
opts = {}
|
|
348
|
+
opts[:settings] = settings
|
|
349
|
+
opts[:matches_request_id] = "invalid_request_id"
|
|
350
|
+
response_valid_signed = OneLogin::RubySaml::Response.new(response_document_valid_signed, opts)
|
|
351
|
+
response_valid_signed.is_valid?
|
|
352
|
+
assert_includes response_valid_signed.errors, "The InResponseTo of the Response: _fc4a34b0-7efb-012e-caae-782bcb13bb38, does not match the ID of the AuthNRequest sent by the SP: invalid_request_id"
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
it "return false when the assertion contains encrypted attributes" do
|
|
356
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
357
|
+
response_encrypted_attrs.settings = settings
|
|
358
|
+
response_encrypted_attrs.soft = true
|
|
359
|
+
response_encrypted_attrs.is_valid?
|
|
360
|
+
assert_includes response_encrypted_attrs.errors, "There is an EncryptedAttribute in the Response and this SP not support them"
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
it "return false when there is no valid audience" do
|
|
364
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
365
|
+
settings.issuer = 'invalid'
|
|
366
|
+
response_valid_signed.settings = settings
|
|
367
|
+
response_valid_signed.is_valid?
|
|
368
|
+
assert_includes response_valid_signed.errors, "#{response_valid_signed.settings.issuer} is not a valid audience for this Response"
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
it "return false when no ID present in the SAML Response" do
|
|
372
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
373
|
+
response_no_id.settings = settings
|
|
374
|
+
response_no_id.soft = true
|
|
375
|
+
response_no_id.is_valid?
|
|
376
|
+
assert_includes response_no_id.errors, "Missing ID attribute on SAML Response"
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
it "return false when no 2.0 Version present in the SAML Response" do
|
|
380
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
381
|
+
response_no_version.settings = settings
|
|
382
|
+
response_no_version.soft = true
|
|
383
|
+
error_msg = "Unsupported SAML version"
|
|
384
|
+
response_no_version.is_valid?
|
|
385
|
+
assert_includes response_no_version.errors, "Unsupported SAML version"
|
|
54
386
|
end
|
|
55
387
|
end
|
|
56
388
|
end
|
|
57
389
|
|
|
390
|
+
describe "#validate_audience" do
|
|
391
|
+
it "return true when the audience is valid" do
|
|
392
|
+
response.settings = settings
|
|
393
|
+
response.settings.issuer = '{audience}'
|
|
394
|
+
assert response.send(:validate_audience)
|
|
395
|
+
assert_empty response.errors
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
it "return false when the audience is valid" do
|
|
399
|
+
response.settings = settings
|
|
400
|
+
response.settings.issuer = 'invalid_audience'
|
|
401
|
+
assert !response.send(:validate_audience)
|
|
402
|
+
assert_includes response.errors, "#{response.settings.issuer} is not a valid audience for this Response"
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
describe "#validate_issuer" do
|
|
407
|
+
it "return true when the issuer of the Message/Assertion matches the IdP entityId" do
|
|
408
|
+
response_valid_signed.settings = settings
|
|
409
|
+
assert response_valid_signed.send(:validate_issuer)
|
|
410
|
+
|
|
411
|
+
response_valid_signed.settings.idp_entity_id = 'https://app.onelogin.com/saml2'
|
|
412
|
+
assert response_valid_signed.send(:validate_issuer)
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
it "return false when the issuer of the Message does not match the IdP entityId" do
|
|
416
|
+
response_invalid_issuer_message.settings = settings
|
|
417
|
+
response_invalid_issuer_message.settings.idp_entity_id = 'http://idp.example.com/'
|
|
418
|
+
assert !response_invalid_issuer_message.send(:validate_issuer)
|
|
419
|
+
assert_includes response_invalid_issuer_message.errors, "Doesn't match the issuer, expected: <#{response_invalid_issuer_message.settings.idp_entity_id}>, but was: <http://invalid.issuer.example.com/>"
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
it "return false when the issuer of the Assertion does not match the IdP entityId" do
|
|
423
|
+
response_invalid_issuer_assertion.settings = settings
|
|
424
|
+
response_invalid_issuer_assertion.settings.idp_entity_id = 'http://idp.example.com/'
|
|
425
|
+
assert !response_invalid_issuer_assertion.send(:validate_issuer)
|
|
426
|
+
assert_includes response_invalid_issuer_assertion.errors, "Doesn't match the issuer, expected: <#{response_invalid_issuer_assertion.settings.idp_entity_id}>, but was: <http://invalid.issuer.example.com/>"
|
|
427
|
+
end
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
describe "#validate_num_assertion" do
|
|
431
|
+
it "return true when SAML Response contains 1 assertion" do
|
|
432
|
+
assert response.send(:validate_num_assertion)
|
|
433
|
+
assert_empty response.errors
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
it "return false when no 2.0 Version present in the SAML Response" do
|
|
437
|
+
assert !response_multi_assertion.send(:validate_num_assertion)
|
|
438
|
+
assert_includes response_multi_assertion.errors, "SAML Response must contain 1 assertion"
|
|
439
|
+
end
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
describe "validate_success_status" do
|
|
443
|
+
it "return true when the status is 'Success'" do
|
|
444
|
+
assert response.send(:validate_success_status)
|
|
445
|
+
assert_empty response.errors
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
it "return false when the status if no Status provided" do
|
|
449
|
+
assert !response_no_status.send(:validate_success_status)
|
|
450
|
+
assert_includes response_no_status.errors, "The status code of the Response was not Success"
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
it "return false when the status if no StatusCode provided" do
|
|
454
|
+
assert !response_no_statuscode.send(:validate_success_status)
|
|
455
|
+
assert_includes response_no_statuscode.errors, "The status code of the Response was not Success"
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
it "return false when the status is not 'Success'" do
|
|
459
|
+
assert !response_statuscode_responder.send(:validate_success_status)
|
|
460
|
+
assert_includes response_statuscode_responder.errors, "The status code of the Response was not Success, was Responder"
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
it "return false when the status is not 'Success', and shows the StatusMessage" do
|
|
464
|
+
assert !response_statuscode_responder_and_msg.send(:validate_success_status)
|
|
465
|
+
assert_includes response_statuscode_responder_and_msg.errors, "The status code of the Response was not Success, was Responder -> something_is_wrong"
|
|
466
|
+
end
|
|
467
|
+
end
|
|
468
|
+
|
|
58
469
|
describe "#validate_structure" do
|
|
59
|
-
it "
|
|
60
|
-
response
|
|
61
|
-
response.
|
|
62
|
-
|
|
470
|
+
it "return true when encountering a wellformed SAML Response" do
|
|
471
|
+
assert response.send(:validate_structure)
|
|
472
|
+
assert_empty response.errors
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
it "return false when encountering a mailformed element that prevents the document from being valid" do
|
|
476
|
+
response_without_attributes.soft = true
|
|
477
|
+
response_without_attributes.send(:validate_structure)
|
|
478
|
+
assert response_without_attributes.errors.include? "Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd"
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
it "raise when encountering a mailformed element that prevents the document from being valid" do
|
|
482
|
+
response_without_attributes.soft = false
|
|
483
|
+
assert_raises(OneLogin::RubySaml::ValidationError) {
|
|
484
|
+
response_without_attributes.send(:validate_structure)
|
|
485
|
+
}
|
|
63
486
|
end
|
|
64
487
|
end
|
|
65
488
|
|
|
66
|
-
describe "#
|
|
67
|
-
it "return
|
|
68
|
-
response = OneLogin::RubySaml::Response.new(
|
|
69
|
-
assert
|
|
489
|
+
describe "#validate_in_response_to" do
|
|
490
|
+
it "return true when the inResponseTo value matches the Request ID" do
|
|
491
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed, :settings => settings, :matches_request_id => "_fc4a34b0-7efb-012e-caae-782bcb13bb38")
|
|
492
|
+
assert response.send(:validate_in_response_to)
|
|
493
|
+
assert_empty response.errors
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
it "return true when no Request ID is provided for checking" do
|
|
497
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed, :settings => settings)
|
|
498
|
+
assert response.send(:validate_in_response_to)
|
|
499
|
+
assert_empty response.errors
|
|
70
500
|
end
|
|
71
501
|
|
|
72
|
-
it "return false
|
|
73
|
-
response = OneLogin::RubySaml::Response.new(
|
|
74
|
-
assert !response.
|
|
502
|
+
it "return false when the inResponseTo value does not match the Request ID" do
|
|
503
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed, :settings => settings, :matches_request_id => "invalid_request_id")
|
|
504
|
+
assert !response.send(:validate_in_response_to)
|
|
505
|
+
assert_includes response.errors, "The InResponseTo of the Response: _fc4a34b0-7efb-012e-caae-782bcb13bb38, does not match the ID of the AuthNRequest sent by the SP: invalid_request_id"
|
|
75
506
|
end
|
|
507
|
+
end
|
|
76
508
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
assert
|
|
81
|
-
|
|
82
|
-
assert !response.is_valid?
|
|
83
|
-
response.settings = settings
|
|
84
|
-
assert !response.is_valid?
|
|
85
|
-
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
86
|
-
assert response.is_valid?
|
|
509
|
+
describe "#validate_no_encrypted_attributes" do
|
|
510
|
+
it "return true when the assertion does not contain encrypted attributes" do
|
|
511
|
+
response_valid_signed.settings = settings
|
|
512
|
+
assert response_valid_signed.send(:validate_no_encrypted_attributes)
|
|
513
|
+
assert_empty response_valid_signed.errors
|
|
87
514
|
end
|
|
88
515
|
|
|
89
|
-
it "
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
response.settings = settings
|
|
94
|
-
assert !response.is_valid?
|
|
95
|
-
assert !response.is_valid?
|
|
516
|
+
it "return false when the assertion contains encrypted attributes" do
|
|
517
|
+
response_encrypted_attrs.settings = settings
|
|
518
|
+
assert !response_encrypted_attrs.send(:validate_no_encrypted_attributes)
|
|
519
|
+
assert_includes response_encrypted_attrs.errors, "There is an EncryptedAttribute in the Response and this SP not support them"
|
|
96
520
|
end
|
|
521
|
+
end
|
|
97
522
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
settings =
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
assert response.is_valid?
|
|
105
|
-
assert response.is_valid?
|
|
523
|
+
describe "#validate_audience" do
|
|
524
|
+
it "return true when the audience is valid" do
|
|
525
|
+
response_valid_signed.settings = settings
|
|
526
|
+
response_valid_signed.settings.issuer = "https://someone.example.com/audience"
|
|
527
|
+
assert response_valid_signed.send(:validate_audience)
|
|
528
|
+
assert_empty response_valid_signed.errors
|
|
106
529
|
end
|
|
107
530
|
|
|
108
|
-
it "return true when
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
settings.idp_cert = signature_1
|
|
114
|
-
assert response.is_valid?
|
|
531
|
+
it "return true when there is not issuer defined" do
|
|
532
|
+
response_valid_signed.settings = settings
|
|
533
|
+
response_valid_signed.settings.issuer = nil
|
|
534
|
+
assert response_valid_signed.send(:validate_audience)
|
|
535
|
+
assert_empty response_valid_signed.errors
|
|
115
536
|
end
|
|
116
537
|
|
|
117
|
-
it "
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
settings.
|
|
122
|
-
response.settings = settings
|
|
123
|
-
assert response.is_valid?
|
|
124
|
-
assert_equal response.name_id, "test@onelogin.com"
|
|
538
|
+
it "return false when there is no valid audience" do
|
|
539
|
+
response_invalid_audience.settings = settings
|
|
540
|
+
response_invalid_audience.settings.issuer = "https://invalid.example.com/audience"
|
|
541
|
+
assert !response_invalid_audience.send(:validate_audience)
|
|
542
|
+
assert_includes response_invalid_audience.errors, "#{response_invalid_audience.settings.issuer} is not a valid audience for this Response"
|
|
125
543
|
end
|
|
544
|
+
end
|
|
126
545
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
546
|
+
describe "#validate_issuer" do
|
|
547
|
+
it "return true when the issuer of the Message/Assertion matches the IdP entityId or it was empty" do
|
|
548
|
+
response_valid_signed.settings = settings
|
|
549
|
+
assert response_valid_signed.send(:validate_issuer)
|
|
550
|
+
assert_empty response_valid_signed.errors
|
|
130
551
|
|
|
131
|
-
|
|
132
|
-
|
|
552
|
+
response_valid_signed.settings.idp_entity_id = 'https://app.onelogin.com/saml2'
|
|
553
|
+
assert response_valid_signed.send(:validate_issuer)
|
|
554
|
+
assert_empty response_valid_signed.errors
|
|
133
555
|
end
|
|
134
556
|
|
|
135
|
-
it "
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
557
|
+
it "return false when the issuer of the Message does not match the IdP entityId" do
|
|
558
|
+
response_invalid_issuer_message.settings = settings
|
|
559
|
+
response_invalid_issuer_message.settings.idp_entity_id = 'http://idp.example.com/'
|
|
560
|
+
assert !response_invalid_issuer_message.send(:validate_issuer)
|
|
561
|
+
assert_includes response_invalid_issuer_message.errors, "Doesn't match the issuer, expected: <#{response_invalid_issuer_message.settings.idp_entity_id}>, but was: <http://invalid.issuer.example.com/>"
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
it "return false when the issuer of the Assertion does not match the IdP entityId" do
|
|
565
|
+
response_invalid_issuer_assertion.settings = settings
|
|
566
|
+
response_invalid_issuer_assertion.settings.idp_entity_id = 'http://idp.example.com/'
|
|
567
|
+
assert !response_invalid_issuer_assertion.send(:validate_issuer)
|
|
568
|
+
assert_includes response_invalid_issuer_assertion.errors, "Doesn't match the issuer, expected: <#{response_invalid_issuer_assertion.settings.idp_entity_id}>, but was: <http://invalid.issuer.example.com/>"
|
|
569
|
+
end
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
describe "#validate_subject_confirmation" do
|
|
573
|
+
it "return true when valid subject confirmation" do
|
|
574
|
+
response_valid_signed.settings = settings
|
|
575
|
+
response_valid_signed.settings.assertion_consumer_service_url = 'recipient'
|
|
576
|
+
assert response_valid_signed.send(:validate_subject_confirmation)
|
|
577
|
+
assert_empty response_valid_signed.errors
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
it "return false when no subject confirmation data" do
|
|
581
|
+
response_no_subjectconfirmation_data.settings = settings
|
|
582
|
+
assert !response_no_subjectconfirmation_data.send(:validate_subject_confirmation)
|
|
583
|
+
assert_includes response_no_subjectconfirmation_data.errors, "A valid SubjectConfirmation was not found on this Response"
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
it "return false when no valid subject confirmation method" do
|
|
587
|
+
response_no_subjectconfirmation_method.settings = settings
|
|
588
|
+
assert !response_no_subjectconfirmation_method.send(:validate_subject_confirmation)
|
|
589
|
+
assert_includes response_no_subjectconfirmation_method.errors, "A valid SubjectConfirmation was not found on this Response"
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
it "return false when invalid inresponse" do
|
|
593
|
+
response_invalid_subjectconfirmation_inresponse.settings = settings
|
|
594
|
+
assert !response_invalid_subjectconfirmation_inresponse.send(:validate_subject_confirmation)
|
|
595
|
+
assert_includes response_invalid_subjectconfirmation_inresponse.errors, "A valid SubjectConfirmation was not found on this Response"
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
it "return false when invalid NotBefore" do
|
|
599
|
+
response_invalid_subjectconfirmation_nb.settings = settings
|
|
600
|
+
assert !response_invalid_subjectconfirmation_nb.send(:validate_subject_confirmation)
|
|
601
|
+
assert_includes response_invalid_subjectconfirmation_nb.errors, "A valid SubjectConfirmation was not found on this Response"
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
it "return false when invalid NotOnOrAfter" do
|
|
605
|
+
response_invalid_subjectconfirmation_noa.settings = settings
|
|
606
|
+
assert !response_invalid_subjectconfirmation_noa.send(:validate_subject_confirmation)
|
|
607
|
+
assert_includes response_invalid_subjectconfirmation_noa.errors, "A valid SubjectConfirmation was not found on this Response"
|
|
608
|
+
end
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
describe "#validate_session_expiration" do
|
|
612
|
+
it "return true when the session has not expired" do
|
|
613
|
+
response_valid_signed.settings = settings
|
|
614
|
+
assert response_valid_signed.send(:validate_session_expiration)
|
|
615
|
+
assert_empty response_valid_signed.errors
|
|
143
616
|
end
|
|
144
617
|
|
|
145
|
-
it "
|
|
146
|
-
response = OneLogin::RubySaml::Response.new(fixture(:adfs_response_sha256))
|
|
147
|
-
response.stubs(:conditions).returns(nil)
|
|
148
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
149
|
-
settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA"
|
|
618
|
+
it "return false when the session has expired" do
|
|
150
619
|
response.settings = settings
|
|
151
|
-
assert response.
|
|
620
|
+
assert !response.send(:validate_session_expiration)
|
|
621
|
+
assert_includes response.errors, "The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response"
|
|
622
|
+
end
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
describe "#validate_signature" do
|
|
626
|
+
it "return true when the signature is valid" do
|
|
627
|
+
settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
|
|
628
|
+
response_valid_signed.settings = settings
|
|
629
|
+
assert response_valid_signed.send(:validate_signature)
|
|
630
|
+
assert_empty response_valid_signed.errors
|
|
152
631
|
end
|
|
153
632
|
|
|
154
|
-
it "
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
158
|
-
settings.idp_cert = Base64.decode64(r1_signature_2)
|
|
633
|
+
it "return false when no fingerprint" do
|
|
634
|
+
settings.idp_cert_fingerprint = nil
|
|
635
|
+
settings.idp_cert = nil
|
|
159
636
|
response.settings = settings
|
|
160
|
-
assert response.
|
|
637
|
+
assert !response.send(:validate_signature)
|
|
638
|
+
assert_includes response.errors, "Invalid Signature on SAML Response"
|
|
161
639
|
end
|
|
162
640
|
|
|
163
|
-
it "
|
|
164
|
-
resp_xml = Base64.decode64(response_document_4).gsub(/emailAddress/,'test')
|
|
165
|
-
response = OneLogin::RubySaml::Response.new(Base64.encode64(resp_xml))
|
|
166
|
-
response.stubs(:conditions).returns(nil)
|
|
167
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
641
|
+
it "return false when the signature is invalid" do
|
|
168
642
|
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
169
643
|
response.settings = settings
|
|
170
|
-
|
|
644
|
+
assert !response.send(:validate_signature)
|
|
645
|
+
assert_includes response.errors, "Invalid Signature on SAML Response"
|
|
171
646
|
end
|
|
172
647
|
end
|
|
173
648
|
|
|
174
|
-
describe "#
|
|
649
|
+
describe "#nameid" do
|
|
175
650
|
it "extract the value of the name id element" do
|
|
176
|
-
|
|
177
|
-
assert_equal "
|
|
178
|
-
|
|
179
|
-
response = OneLogin::RubySaml::Response.new(response_document_3)
|
|
180
|
-
assert_equal "someone@example.com", response.name_id
|
|
651
|
+
assert_equal "support@onelogin.com", response.nameid
|
|
652
|
+
assert_equal "someone@example.com", response_with_signed_assertion.nameid
|
|
181
653
|
end
|
|
182
654
|
|
|
183
655
|
it "be extractable from an OpenSAML response" do
|
|
184
|
-
|
|
185
|
-
assert_equal "someone@example.org",
|
|
656
|
+
response_open_saml = OneLogin::RubySaml::Response.new(fixture(:open_saml))
|
|
657
|
+
assert_equal "someone@example.org", response_open_saml.nameid
|
|
186
658
|
end
|
|
187
659
|
|
|
188
660
|
it "be extractable from a Simple SAML PHP response" do
|
|
661
|
+
response_ssp = OneLogin::RubySaml::Response.new(fixture(:simple_saml_php))
|
|
662
|
+
assert_equal "someone@example.com", response_ssp.nameid
|
|
663
|
+
end
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
describe "#sessionindex" do
|
|
667
|
+
it "extract the value of the sessionindex element" do
|
|
189
668
|
response = OneLogin::RubySaml::Response.new(fixture(:simple_saml_php))
|
|
190
|
-
assert_equal "
|
|
669
|
+
assert_equal "_51be37965feb5579d803141076936dc2e9d1d98ebf", response.sessionindex
|
|
191
670
|
end
|
|
192
671
|
end
|
|
193
672
|
|
|
194
673
|
describe "#check_conditions" do
|
|
195
674
|
it "check time conditions" do
|
|
196
|
-
response =
|
|
197
|
-
assert !response.send(:validate_conditions
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
Time.
|
|
202
|
-
|
|
203
|
-
|
|
675
|
+
response.soft = true
|
|
676
|
+
assert !response.send(:validate_conditions)
|
|
677
|
+
response_time_updated = OneLogin::RubySaml::Response.new(response_document_without_recipient_with_time_updated)
|
|
678
|
+
response_time_updated.soft = true
|
|
679
|
+
assert response_time_updated.send(:validate_conditions)
|
|
680
|
+
Timecop.freeze(Time.parse("2011-06-14T18:25:01.516Z")) do
|
|
681
|
+
response_with_saml2_namespace = OneLogin::RubySaml::Response.new(response_document_with_saml2_namespace)
|
|
682
|
+
response_with_saml2_namespace.soft = true
|
|
683
|
+
assert response_with_saml2_namespace.send(:validate_conditions)
|
|
684
|
+
end
|
|
204
685
|
end
|
|
205
686
|
|
|
206
687
|
it "optionally allows for clock drift" do
|
|
207
688
|
# The NotBefore condition in the document is 2011-06-14T18:21:01.516Z
|
|
208
689
|
Timecop.freeze(Time.parse("2011-06-14T18:21:01Z")) do
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
690
|
+
settings.soft = true
|
|
691
|
+
special_response_with_saml2_namespace = OneLogin::RubySaml::Response.new(
|
|
692
|
+
response_document_with_saml2_namespace,
|
|
693
|
+
:allowed_clock_drift => 0.515,
|
|
694
|
+
:settings => settings
|
|
212
695
|
)
|
|
213
|
-
assert !
|
|
696
|
+
assert !special_response_with_saml2_namespace.send(:validate_conditions)
|
|
214
697
|
end
|
|
215
698
|
|
|
216
699
|
Timecop.freeze(Time.parse("2011-06-14T18:21:01Z")) do
|
|
217
|
-
|
|
218
|
-
|
|
700
|
+
special_response_with_saml2_namespace = OneLogin::RubySaml::Response.new(
|
|
701
|
+
response_document_with_saml2_namespace,
|
|
219
702
|
:allowed_clock_drift => 0.516
|
|
220
703
|
)
|
|
221
|
-
assert
|
|
704
|
+
assert special_response_with_saml2_namespace.send(:validate_conditions)
|
|
222
705
|
end
|
|
223
706
|
end
|
|
224
707
|
end
|
|
225
708
|
|
|
226
709
|
describe "#attributes" do
|
|
227
710
|
it "extract the first attribute in a hash accessed via its symbol" do
|
|
228
|
-
response = OneLogin::RubySaml::Response.new(response_document)
|
|
229
711
|
assert_equal "demo", response.attributes[:uid]
|
|
230
712
|
end
|
|
231
713
|
|
|
232
714
|
it "extract the first attribute in a hash accessed via its name" do
|
|
233
|
-
response = OneLogin::RubySaml::Response.new(response_document)
|
|
234
715
|
assert_equal "demo", response.attributes["uid"]
|
|
235
716
|
end
|
|
236
717
|
|
|
237
718
|
it "extract all attributes" do
|
|
238
|
-
response = OneLogin::RubySaml::Response.new(response_document)
|
|
239
719
|
assert_equal "demo", response.attributes[:uid]
|
|
240
720
|
assert_equal "value", response.attributes[:another_value]
|
|
241
721
|
end
|
|
242
722
|
|
|
243
723
|
it "work for implicit namespaces" do
|
|
244
|
-
|
|
245
|
-
assert_equal "someone@example.com", response.attributes["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]
|
|
724
|
+
assert_equal "someone@example.com", response_with_signed_assertion.attributes["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]
|
|
246
725
|
end
|
|
247
726
|
|
|
248
727
|
it "not raise errors about nil/empty attributes for EncryptedAttributes" do
|
|
249
|
-
|
|
250
|
-
assert_equal 'Demo',
|
|
728
|
+
response_no_cert_and_encrypted_attrs = OneLogin::RubySaml::Response.new(response_document_no_cert_and_encrypted_attrs)
|
|
729
|
+
assert_equal 'Demo', response_no_cert_and_encrypted_attrs.attributes["first_name"]
|
|
251
730
|
end
|
|
252
731
|
|
|
253
732
|
it "not raise on responses without attributes" do
|
|
254
|
-
|
|
255
|
-
assert_equal OneLogin::RubySaml::Attributes.new, response.attributes
|
|
733
|
+
assert_equal OneLogin::RubySaml::Attributes.new, response_unsigned.attributes
|
|
256
734
|
end
|
|
257
735
|
|
|
258
736
|
describe "#multiple values" do
|
|
259
737
|
it "extract single value as string" do
|
|
260
|
-
|
|
261
|
-
assert_equal "demo", response.attributes[:uid]
|
|
738
|
+
assert_equal "demo", response_multiple_attr_values.attributes[:uid]
|
|
262
739
|
end
|
|
263
740
|
|
|
264
741
|
it "extract single value as string in compatibility mode off" do
|
|
265
|
-
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
266
742
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
267
|
-
assert_equal ["demo"],
|
|
743
|
+
assert_equal ["demo"], response_multiple_attr_values.attributes[:uid]
|
|
268
744
|
# classes are not reloaded between tests so restore default
|
|
269
745
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
270
746
|
end
|
|
271
747
|
|
|
272
748
|
it "extract first of multiple values as string for b/w compatibility" do
|
|
273
|
-
|
|
274
|
-
assert_equal 'value1', response.attributes[:another_value]
|
|
749
|
+
assert_equal 'value1', response_multiple_attr_values.attributes[:another_value]
|
|
275
750
|
end
|
|
276
751
|
|
|
277
752
|
it "extract first of multiple values as string for b/w compatibility in compatibility mode off" do
|
|
278
|
-
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
279
753
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
280
|
-
assert_equal ['value1', 'value2'],
|
|
754
|
+
assert_equal ['value1', 'value2'], response_multiple_attr_values.attributes[:another_value]
|
|
281
755
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
282
756
|
end
|
|
283
757
|
|
|
284
758
|
it "return array with all attributes when asked in XML order" do
|
|
285
|
-
|
|
286
|
-
assert_equal ['value1', 'value2'], response.attributes.multi(:another_value)
|
|
759
|
+
assert_equal ['value1', 'value2'], response_multiple_attr_values.attributes.multi(:another_value)
|
|
287
760
|
end
|
|
288
761
|
|
|
289
762
|
it "return array with all attributes when asked in XML order in compatibility mode off" do
|
|
290
|
-
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
291
763
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
292
|
-
assert_equal ['value1', 'value2'],
|
|
764
|
+
assert_equal ['value1', 'value2'], response_multiple_attr_values.attributes.multi(:another_value)
|
|
293
765
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
294
766
|
end
|
|
295
767
|
|
|
296
768
|
it "return first of multiple values when multiple Attribute tags in XML" do
|
|
297
|
-
|
|
298
|
-
assert_equal 'role1', response.attributes[:role]
|
|
769
|
+
assert_equal 'role1', response_multiple_attr_values.attributes[:role]
|
|
299
770
|
end
|
|
300
771
|
|
|
301
772
|
it "return first of multiple values when multiple Attribute tags in XML in compatibility mode off" do
|
|
302
|
-
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
303
773
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
304
|
-
assert_equal ['role1', 'role2', 'role3'],
|
|
774
|
+
assert_equal ['role1', 'role2', 'role3'], response_multiple_attr_values.attributes[:role]
|
|
305
775
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
306
776
|
end
|
|
307
777
|
|
|
308
778
|
it "return all of multiple values in reverse order when multiple Attribute tags in XML" do
|
|
309
|
-
|
|
310
|
-
assert_equal ['role1', 'role2', 'role3'], response.attributes.multi(:role)
|
|
779
|
+
assert_equal ['role1', 'role2', 'role3'], response_multiple_attr_values.attributes.multi(:role)
|
|
311
780
|
end
|
|
312
781
|
|
|
313
782
|
it "return all of multiple values in reverse order when multiple Attribute tags in XML in compatibility mode off" do
|
|
314
|
-
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
315
783
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
316
|
-
assert_equal ['role1', 'role2', 'role3'],
|
|
784
|
+
assert_equal ['role1', 'role2', 'role3'], response_multiple_attr_values.attributes.multi(:role)
|
|
317
785
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
318
786
|
end
|
|
319
787
|
|
|
320
788
|
it "return nil value correctly" do
|
|
321
|
-
|
|
322
|
-
assert_nil response.attributes[:attribute_with_nil_value]
|
|
789
|
+
assert_nil response_multiple_attr_values.attributes[:attribute_with_nil_value]
|
|
323
790
|
end
|
|
324
791
|
|
|
325
792
|
it "return nil value correctly when not in compatibility mode off" do
|
|
326
|
-
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
327
793
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
328
|
-
assert_equal [nil],
|
|
794
|
+
assert_equal [nil], response_multiple_attr_values.attributes[:attribute_with_nil_value]
|
|
329
795
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
330
796
|
end
|
|
331
797
|
|
|
@@ -335,22 +801,20 @@ class RubySamlTest < Minitest::Test
|
|
|
335
801
|
end
|
|
336
802
|
|
|
337
803
|
it "return multiple values from [] when not in compatibility mode off" do
|
|
338
|
-
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
339
804
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
340
|
-
assert_equal ["", "valuePresent", nil, nil],
|
|
805
|
+
assert_equal ["", "valuePresent", nil, nil], response_multiple_attr_values.attributes[:attribute_with_nils_and_empty_strings]
|
|
341
806
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
342
807
|
end
|
|
343
808
|
|
|
344
809
|
it "check what happens when trying retrieve attribute that does not exists" do
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
assert_nil response.attributes.multi(:attribute_not_exists)
|
|
810
|
+
assert_equal nil, response_multiple_attr_values.attributes[:attribute_not_exists]
|
|
811
|
+
assert_equal nil, response_multiple_attr_values.attributes.single(:attribute_not_exists)
|
|
812
|
+
assert_equal nil, response_multiple_attr_values.attributes.multi(:attribute_not_exists)
|
|
349
813
|
|
|
350
814
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
815
|
+
assert_equal nil, response_multiple_attr_values.attributes[:attribute_not_exists]
|
|
816
|
+
assert_equal nil, response_multiple_attr_values.attributes.single(:attribute_not_exists)
|
|
817
|
+
assert_equal nil, response_multiple_attr_values.attributes.multi(:attribute_not_exists)
|
|
354
818
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
355
819
|
end
|
|
356
820
|
|
|
@@ -359,29 +823,26 @@ class RubySamlTest < Minitest::Test
|
|
|
359
823
|
|
|
360
824
|
describe "#session_expires_at" do
|
|
361
825
|
it "extract the value of the SessionNotOnOrAfter attribute" do
|
|
362
|
-
response = OneLogin::RubySaml::Response.new(response_document)
|
|
363
826
|
assert response.session_expires_at.is_a?(Time)
|
|
827
|
+
end
|
|
364
828
|
|
|
365
|
-
|
|
366
|
-
assert_nil
|
|
829
|
+
it "return nil when the value of the SessionNotOnOrAfter is not set" do
|
|
830
|
+
assert_nil response_without_attributes.session_expires_at
|
|
367
831
|
end
|
|
368
832
|
end
|
|
369
833
|
|
|
370
|
-
describe "#
|
|
834
|
+
describe "#issuers" do
|
|
371
835
|
it "return the issuer inside the response assertion" do
|
|
372
|
-
response
|
|
373
|
-
assert_equal "https://app.onelogin.com/saml/metadata/13590", response.issuer
|
|
836
|
+
assert_includes response.issuers, "https://app.onelogin.com/saml/metadata/13590"
|
|
374
837
|
end
|
|
375
838
|
|
|
376
839
|
it "return the issuer inside the response" do
|
|
377
|
-
|
|
378
|
-
assert_equal "wibble", response.issuer
|
|
840
|
+
assert_includes response_without_attributes.issuers, "wibble"
|
|
379
841
|
end
|
|
380
842
|
end
|
|
381
843
|
|
|
382
844
|
describe "#success" do
|
|
383
845
|
it "find a status code that says success" do
|
|
384
|
-
response = OneLogin::RubySaml::Response.new(response_document)
|
|
385
846
|
response.success?
|
|
386
847
|
end
|
|
387
848
|
end
|
|
@@ -389,8 +850,8 @@ class RubySamlTest < Minitest::Test
|
|
|
389
850
|
describe '#xpath_first_from_signed_assertion' do
|
|
390
851
|
it 'not allow arbitrary code execution' do
|
|
391
852
|
malicious_response_document = fixture('response_eval', false)
|
|
392
|
-
|
|
393
|
-
|
|
853
|
+
malicious_response = OneLogin::RubySaml::Response.new(malicious_response_document)
|
|
854
|
+
malicious_response.send(:xpath_first_from_signed_assertion)
|
|
394
855
|
assert_nil $evalled
|
|
395
856
|
end
|
|
396
857
|
end
|
|
@@ -408,13 +869,225 @@ class RubySamlTest < Minitest::Test
|
|
|
408
869
|
private_key = OpenSSL::PKey::RSA.new(formated_private_key)
|
|
409
870
|
document.sign_document(private_key, cert)
|
|
410
871
|
|
|
411
|
-
|
|
412
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
872
|
+
signed_response = OneLogin::RubySaml::Response.new(document.to_s)
|
|
413
873
|
settings.idp_cert = ruby_saml_cert_text
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
874
|
+
signed_response.settings = settings
|
|
875
|
+
Timecop.freeze(Time.parse("2015-03-18T04:50:24Z")) do
|
|
876
|
+
assert signed_response.is_valid?
|
|
877
|
+
end
|
|
878
|
+
assert_empty signed_response.errors
|
|
879
|
+
end
|
|
880
|
+
end
|
|
881
|
+
|
|
882
|
+
describe "retrieve nameID" do
|
|
883
|
+
it 'is possible when nameID inside the assertion' do
|
|
884
|
+
response_valid_signed.settings = settings
|
|
885
|
+
assert_equal "test@onelogin.com", response_valid_signed.nameid
|
|
886
|
+
end
|
|
887
|
+
|
|
888
|
+
it 'is not possible when encryptID inside the assertion but no private key' do
|
|
889
|
+
response_encrypted_nameid.settings = settings
|
|
890
|
+
assert_raises(OneLogin::RubySaml::ValidationError, "An EncryptedID found and no SP private key found on the settings to decrypt it") do
|
|
891
|
+
assert_equal "test@onelogin.com", response_encrypted_nameid.nameid
|
|
892
|
+
end
|
|
893
|
+
end
|
|
894
|
+
|
|
895
|
+
it 'is possible when encryptID inside the assertion and settings has the private key' do
|
|
896
|
+
settings.private_key = ruby_saml_key_text
|
|
897
|
+
response_encrypted_nameid.settings = settings
|
|
898
|
+
assert_equal "test@onelogin.com", response_encrypted_nameid.nameid
|
|
899
|
+
end
|
|
900
|
+
|
|
901
|
+
end
|
|
902
|
+
|
|
903
|
+
end
|
|
904
|
+
|
|
905
|
+
describe 'try to initialize an encrypted response' do
|
|
906
|
+
it 'raise if an encrypted assertion is found and no sp private key to decrypt it' do
|
|
907
|
+
error_msg = "An EncryptedAssertion found and no SP private key found on the settings to decrypt it. Be sure you provided the :settings parameter at the initialize method"
|
|
908
|
+
|
|
909
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
910
|
+
response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion)
|
|
911
|
+
end
|
|
912
|
+
|
|
913
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
914
|
+
response2 = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
|
|
915
|
+
end
|
|
916
|
+
|
|
917
|
+
settings.certificate = ruby_saml_cert_text
|
|
918
|
+
settings.private_key = ruby_saml_key_text
|
|
919
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
920
|
+
response3 = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion)
|
|
921
|
+
response3.settings
|
|
922
|
+
end
|
|
923
|
+
end
|
|
924
|
+
|
|
925
|
+
it 'raise if an encrypted assertion is found and the sp private key is wrong' do
|
|
926
|
+
settings.certificate = ruby_saml_cert_text
|
|
927
|
+
wrong_private_key = ruby_saml_key_text.sub!('A', 'B')
|
|
928
|
+
settings.private_key = wrong_private_key
|
|
929
|
+
|
|
930
|
+
error_msg = "Neither PUB key nor PRIV key: nested asn1 error"
|
|
931
|
+
assert_raises(OpenSSL::PKey::RSAError, error_msg) do
|
|
932
|
+
response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
|
|
933
|
+
end
|
|
934
|
+
end
|
|
935
|
+
|
|
936
|
+
it 'return true if an encrypted assertion is found and settings initialized with private_key' do
|
|
937
|
+
settings.certificate = ruby_saml_cert_text
|
|
938
|
+
settings.private_key = ruby_saml_key_text
|
|
939
|
+
response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
|
|
940
|
+
assert response.decrypted_document
|
|
941
|
+
|
|
942
|
+
response2 = OneLogin::RubySaml::Response.new(signed_message_encrypted_signed_assertion, :settings => settings)
|
|
943
|
+
assert response2.decrypted_document
|
|
944
|
+
|
|
945
|
+
response3 = OneLogin::RubySaml::Response.new(unsigned_message_encrypted_signed_assertion, :settings => settings)
|
|
946
|
+
assert response3.decrypted_document
|
|
947
|
+
|
|
948
|
+
response4 = OneLogin::RubySaml::Response.new(unsigned_message_encrypted_unsigned_assertion, :settings => settings)
|
|
949
|
+
assert response4.decrypted_document
|
|
950
|
+
end
|
|
951
|
+
end
|
|
952
|
+
|
|
953
|
+
describe "retrieve nameID and attributes from encrypted assertion" do
|
|
954
|
+
|
|
955
|
+
before do
|
|
956
|
+
settings.idp_cert_fingerprint = 'EE:17:4E:FB:A8:81:71:12:0D:2A:78:43:BC:E7:0C:07:58:79:F4:F4'
|
|
957
|
+
settings.issuer = 'http://rubysaml.com:3000/saml/metadata'
|
|
958
|
+
settings.assertion_consumer_service_url = 'http://rubysaml.com:3000/saml/acs'
|
|
959
|
+
settings.certificate = ruby_saml_cert_text
|
|
960
|
+
settings.private_key = ruby_saml_key_text
|
|
961
|
+
end
|
|
962
|
+
|
|
963
|
+
it 'is possible when signed_message_encrypted_unsigned_assertion' do
|
|
964
|
+
response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
|
|
965
|
+
Timecop.freeze(Time.parse("2015-03-19T14:30:31Z")) do
|
|
966
|
+
assert response.is_valid?
|
|
967
|
+
assert_empty response.errors
|
|
968
|
+
assert_equal "test", response.attributes[:uid]
|
|
969
|
+
assert_equal "98e2bb61075e951b37d6b3be6954a54b340d86c7", response.nameid
|
|
970
|
+
end
|
|
971
|
+
end
|
|
972
|
+
|
|
973
|
+
it 'is possible when signed_message_encrypted_signed_assertion' do
|
|
974
|
+
response = OneLogin::RubySaml::Response.new(signed_message_encrypted_signed_assertion, :settings => settings)
|
|
975
|
+
Timecop.freeze(Time.parse("2015-03-19T14:30:31Z")) do
|
|
976
|
+
assert response.is_valid?
|
|
977
|
+
assert_empty response.errors
|
|
978
|
+
assert_equal "test", response.attributes[:uid]
|
|
979
|
+
assert_equal "98e2bb61075e951b37d6b3be6954a54b340d86c7", response.nameid
|
|
980
|
+
end
|
|
981
|
+
end
|
|
982
|
+
|
|
983
|
+
it 'is possible when unsigned_message_encrypted_signed_assertion' do
|
|
984
|
+
response = OneLogin::RubySaml::Response.new(unsigned_message_encrypted_signed_assertion, :settings => settings)
|
|
985
|
+
Timecop.freeze(Time.parse("2015-03-19T14:30:31Z")) do
|
|
986
|
+
assert response.is_valid?
|
|
987
|
+
assert_empty response.errors
|
|
988
|
+
assert_equal "test", response.attributes[:uid]
|
|
989
|
+
assert_equal "98e2bb61075e951b37d6b3be6954a54b340d86c7", response.nameid
|
|
990
|
+
end
|
|
991
|
+
end
|
|
992
|
+
|
|
993
|
+
it 'is not possible when unsigned_message_encrypted_unsigned_assertion' do
|
|
994
|
+
response = OneLogin::RubySaml::Response.new(unsigned_message_encrypted_unsigned_assertion, :settings => settings)
|
|
995
|
+
Timecop.freeze(Time.parse("2015-03-19T14:30:31Z")) do
|
|
996
|
+
assert !response.is_valid?
|
|
997
|
+
assert_includes response.errors, "Found an unexpected number of Signature Element. SAML Response rejected"
|
|
998
|
+
end
|
|
999
|
+
end
|
|
1000
|
+
end
|
|
1001
|
+
|
|
1002
|
+
describe "#decrypt_assertion" do
|
|
1003
|
+
before do
|
|
1004
|
+
settings.private_key = ruby_saml_key_text
|
|
1005
|
+
end
|
|
1006
|
+
|
|
1007
|
+
describe "check right settings" do
|
|
1008
|
+
|
|
1009
|
+
it "is not possible to decrypt the assertion if no private key" do
|
|
1010
|
+
response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
|
|
1011
|
+
|
|
1012
|
+
encrypted_assertion_node = REXML::XPath.first(
|
|
1013
|
+
response.document,
|
|
1014
|
+
"(/p:Response/EncryptedAssertion/)|(/p:Response/a:EncryptedAssertion/)",
|
|
1015
|
+
{ "p" => "urn:oasis:names:tc:SAML:2.0:protocol", "a" => "urn:oasis:names:tc:SAML:2.0:assertion" }
|
|
1016
|
+
)
|
|
1017
|
+
response.settings.private_key = nil
|
|
1018
|
+
|
|
1019
|
+
error_msg = "An EncryptedAssertion found and no SP private key found on the settings to decrypt it"
|
|
1020
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
1021
|
+
decrypted = response.send(:decrypt_assertion, encrypted_assertion_node)
|
|
1022
|
+
end
|
|
1023
|
+
end
|
|
1024
|
+
|
|
1025
|
+
it "is possible to decrypt the assertion if private key" do
|
|
1026
|
+
response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
|
|
1027
|
+
|
|
1028
|
+
encrypted_assertion_node = REXML::XPath.first(
|
|
1029
|
+
response.document,
|
|
1030
|
+
"(/p:Response/EncryptedAssertion/)|(/p:Response/a:EncryptedAssertion/)",
|
|
1031
|
+
{ "p" => "urn:oasis:names:tc:SAML:2.0:protocol", "a" => "urn:oasis:names:tc:SAML:2.0:assertion" }
|
|
1032
|
+
)
|
|
1033
|
+
decrypted = response.send(:decrypt_assertion, encrypted_assertion_node)
|
|
1034
|
+
|
|
1035
|
+
encrypted_assertion_node2 = REXML::XPath.first(
|
|
1036
|
+
decrypted,
|
|
1037
|
+
"(/p:Response/EncryptedAssertion/)|(/p:Response/a:EncryptedAssertion/)",
|
|
1038
|
+
{ "p" => "urn:oasis:names:tc:SAML:2.0:protocol", "a" => "urn:oasis:names:tc:SAML:2.0:assertion" }
|
|
1039
|
+
)
|
|
1040
|
+
assert_nil encrypted_assertion_node2
|
|
1041
|
+
assert decrypted.name, "Assertion"
|
|
1042
|
+
end
|
|
1043
|
+
|
|
1044
|
+
it "is possible to decrypt the assertion if private key but no saml namespace on the Assertion Element that is inside the EncryptedAssertion" do
|
|
1045
|
+
unsigned_message_encrypted_assertion_without_saml_namespace = read_response('unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64')
|
|
1046
|
+
response = OneLogin::RubySaml::Response.new(unsigned_message_encrypted_assertion_without_saml_namespace, :settings => settings)
|
|
1047
|
+
encrypted_assertion_node = REXML::XPath.first(
|
|
1048
|
+
response.document,
|
|
1049
|
+
"(/p:Response/EncryptedAssertion/)|(/p:Response/a:EncryptedAssertion/)",
|
|
1050
|
+
{ "p" => "urn:oasis:names:tc:SAML:2.0:protocol", "a" => "urn:oasis:names:tc:SAML:2.0:assertion" }
|
|
1051
|
+
)
|
|
1052
|
+
decrypted = response.send(:decrypt_assertion, encrypted_assertion_node)
|
|
1053
|
+
|
|
1054
|
+
encrypted_assertion_node2 = REXML::XPath.first(
|
|
1055
|
+
decrypted,
|
|
1056
|
+
"(/p:Response/EncryptedAssertion/)|(/p:Response/a:EncryptedAssertion/)",
|
|
1057
|
+
{ "p" => "urn:oasis:names:tc:SAML:2.0:protocol", "a" => "urn:oasis:names:tc:SAML:2.0:assertion" }
|
|
1058
|
+
)
|
|
1059
|
+
assert_nil encrypted_assertion_node2
|
|
1060
|
+
assert decrypted.name, "Assertion"
|
|
1061
|
+
end
|
|
1062
|
+
end
|
|
1063
|
+
|
|
1064
|
+
describe "check different encrypt methods supported" do
|
|
1065
|
+
it "EncryptionMethod DES-192 && Key Encryption Algorithm RSA-1_5" do
|
|
1066
|
+
unsigned_message_des192_encrypted_signed_assertion = read_response('unsigned_message_des192_encrypted_signed_assertion.xml.base64')
|
|
1067
|
+
response = OneLogin::RubySaml::Response.new(unsigned_message_des192_encrypted_signed_assertion, :settings => settings)
|
|
1068
|
+
assert_equal "test", response.attributes[:uid]
|
|
1069
|
+
assert_equal "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7", response.nameid
|
|
1070
|
+
end
|
|
1071
|
+
|
|
1072
|
+
it "EncryptionMethod AES-128 && Key Encryption Algorithm RSA-OAEP-MGF1P" do
|
|
1073
|
+
unsigned_message_aes128_encrypted_signed_assertion = read_response('unsigned_message_aes128_encrypted_signed_assertion.xml.base64')
|
|
1074
|
+
response = OneLogin::RubySaml::Response.new(unsigned_message_aes128_encrypted_signed_assertion, :settings => settings)
|
|
1075
|
+
assert_equal "test", response.attributes[:uid]
|
|
1076
|
+
assert_equal "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7", response.nameid
|
|
1077
|
+
end
|
|
1078
|
+
|
|
1079
|
+
it "EncryptionMethod AES-192 && Key Encryption Algorithm RSA-OAEP-MGF1P" do
|
|
1080
|
+
unsigned_message_aes192_encrypted_signed_assertion = read_response('unsigned_message_aes192_encrypted_signed_assertion.xml.base64')
|
|
1081
|
+
response = OneLogin::RubySaml::Response.new(unsigned_message_aes192_encrypted_signed_assertion, :settings => settings)
|
|
1082
|
+
assert_equal "test", response.attributes[:uid]
|
|
1083
|
+
assert_equal "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7", response.nameid
|
|
1084
|
+
end
|
|
1085
|
+
|
|
1086
|
+
it "EncryptionMethod AES-256 && Key Encryption Algorithm RSA-OAEP-MGF1P" do
|
|
1087
|
+
unsigned_message_aes256_encrypted_signed_assertion = read_response('unsigned_message_aes256_encrypted_signed_assertion.xml.base64')
|
|
1088
|
+
response = OneLogin::RubySaml::Response.new(unsigned_message_aes256_encrypted_signed_assertion, :settings => settings)
|
|
1089
|
+
assert_equal "test", response.attributes[:uid]
|
|
1090
|
+
assert_equal "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7", response.nameid
|
|
418
1091
|
end
|
|
419
1092
|
end
|
|
420
1093
|
end
|