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/logoutresponse_test.rb
CHANGED
|
@@ -1,110 +1,257 @@
|
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
|
2
2
|
|
|
3
3
|
require 'onelogin/ruby-saml/logoutresponse'
|
|
4
|
-
require '
|
|
4
|
+
require 'logout_responses/logoutresponse_fixtures'
|
|
5
5
|
|
|
6
6
|
class RubySamlTest < Minitest::Test
|
|
7
7
|
|
|
8
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
|
+
|
|
9
13
|
describe "#new" do
|
|
10
14
|
it "raise an exception when response is initialized with nil" do
|
|
11
15
|
assert_raises(ArgumentError) { OneLogin::RubySaml::Logoutresponse.new(nil) }
|
|
12
16
|
end
|
|
13
17
|
it "default to empty settings" do
|
|
14
|
-
|
|
15
|
-
assert_nil logoutresponse.settings
|
|
18
|
+
assert_nil valid_logout_response_without_settings.settings
|
|
16
19
|
end
|
|
17
20
|
it "accept constructor-injected settings" do
|
|
18
|
-
|
|
19
|
-
refute_nil logoutresponse.settings
|
|
21
|
+
refute_nil valid_logout_response.settings
|
|
20
22
|
end
|
|
21
23
|
it "accept constructor-injected options" do
|
|
22
|
-
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(
|
|
24
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, nil, { :foo => :bar} )
|
|
23
25
|
assert !logoutresponse.options.empty?
|
|
24
26
|
end
|
|
25
27
|
it "support base64 encoded responses" do
|
|
26
|
-
|
|
27
|
-
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(Base64.encode64(
|
|
28
|
-
|
|
29
|
-
assert_equal expected_response, logoutresponse.response
|
|
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
|
|
30
31
|
end
|
|
31
32
|
end
|
|
32
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
|
+
|
|
33
51
|
describe "#validate" do
|
|
34
|
-
|
|
35
|
-
|
|
52
|
+
describe "when soft=true" do
|
|
53
|
+
before do
|
|
54
|
+
settings.soft = true
|
|
55
|
+
end
|
|
36
56
|
|
|
37
|
-
|
|
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}
|
|
38
60
|
|
|
39
|
-
|
|
61
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document({:uuid => in_relation_to_request_id}), settings, opts)
|
|
40
62
|
|
|
41
|
-
|
|
42
|
-
assert_equal in_relation_to_request_id, logoutresponse.in_response_to
|
|
63
|
+
assert logoutresponse.validate
|
|
43
64
|
|
|
44
|
-
|
|
45
|
-
|
|
65
|
+
assert_equal settings.issuer, logoutresponse.issuer
|
|
66
|
+
assert_equal in_relation_to_request_id, logoutresponse.in_response_to
|
|
46
67
|
|
|
47
|
-
|
|
68
|
+
assert logoutresponse.success?
|
|
69
|
+
assert_empty logoutresponse.errors
|
|
70
|
+
end
|
|
48
71
|
|
|
49
|
-
|
|
50
|
-
|
|
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}
|
|
51
76
|
|
|
52
|
-
|
|
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
|
|
53
83
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
|
57
89
|
|
|
58
|
-
|
|
59
|
-
|
|
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
|
|
60
97
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
end
|
|
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}
|
|
65
101
|
|
|
66
|
-
|
|
67
|
-
it "validates good responses" do
|
|
68
|
-
in_relation_to_request_id = random_id
|
|
102
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings, opts)
|
|
69
103
|
|
|
70
|
-
|
|
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
|
|
71
108
|
|
|
72
|
-
|
|
73
|
-
|
|
109
|
+
it "invalidate logout response with wrong request status" do
|
|
110
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
|
|
74
111
|
|
|
75
|
-
|
|
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
|
|
76
117
|
|
|
77
|
-
|
|
78
|
-
|
|
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
|
|
79
125
|
|
|
80
|
-
|
|
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
|
|
81
133
|
|
|
82
|
-
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
|
|
83
134
|
end
|
|
84
135
|
|
|
85
|
-
|
|
86
|
-
|
|
136
|
+
describe "when soft=false" do
|
|
137
|
+
before do
|
|
138
|
+
settings.soft = false
|
|
139
|
+
end
|
|
87
140
|
|
|
88
|
-
|
|
89
|
-
|
|
141
|
+
it "validates good logout response" do
|
|
142
|
+
in_relation_to_request_id = random_id
|
|
90
143
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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)
|
|
96
151
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
|
102
202
|
end
|
|
103
203
|
|
|
104
|
-
|
|
105
|
-
|
|
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)
|
|
106
251
|
|
|
107
|
-
|
|
252
|
+
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.send(:validate_signature) }
|
|
253
|
+
assert logoutresponse.errors.include? "Invalid Signature on Logout Response"
|
|
254
|
+
end
|
|
108
255
|
end
|
|
109
256
|
end
|
|
110
257
|
end
|
data/test/metadata_test.rb
CHANGED
|
@@ -33,10 +33,31 @@ class MetadataTest < Minitest::Test
|
|
|
33
33
|
|
|
34
34
|
assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", acs.attribute("Binding").value
|
|
35
35
|
assert_equal "https://foo.example/saml/consume", acs.attribute("Location").value
|
|
36
|
+
|
|
37
|
+
assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
|
|
36
38
|
end
|
|
37
39
|
|
|
38
40
|
it "generates Service Provider Metadata" do
|
|
39
|
-
|
|
41
|
+
settings.single_logout_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
|
|
42
|
+
settings.single_logout_service_url = "https://foo.example/saml/sls"
|
|
43
|
+
xml_metadata = OneLogin::RubySaml::Metadata.new.generate(settings, false)
|
|
44
|
+
|
|
45
|
+
start = "<?xml version='1.0' encoding='UTF-8'?><md:EntityDescriptor"
|
|
46
|
+
assert_equal xml_metadata[0..start.length-1],start
|
|
47
|
+
|
|
48
|
+
doc_metadata = REXML::Document.new(xml_metadata)
|
|
49
|
+
sls = REXML::XPath.first(doc_metadata, "//md:SingleLogoutService")
|
|
50
|
+
|
|
51
|
+
assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", sls.attribute("Binding").value
|
|
52
|
+
assert_equal "https://foo.example/saml/sls", sls.attribute("Location").value
|
|
53
|
+
assert_equal "https://foo.example/saml/sls", sls.attribute("ResponseLocation").value
|
|
54
|
+
assert_nil sls.attribute("isDefault")
|
|
55
|
+
assert_nil sls.attribute("index")
|
|
56
|
+
|
|
57
|
+
assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "generates Service Provider Metadata with single logout service" do
|
|
40
61
|
start = "<?xml version='1.0' encoding='UTF-8'?><md:EntityDescriptor"
|
|
41
62
|
assert_equal xml_text[0..start.length-1], start
|
|
42
63
|
|
|
@@ -50,27 +71,50 @@ class MetadataTest < Minitest::Test
|
|
|
50
71
|
|
|
51
72
|
assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", acs.attribute("Binding").value
|
|
52
73
|
assert_equal "https://foo.example/saml/consume", acs.attribute("Location").value
|
|
74
|
+
|
|
75
|
+
assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
|
|
53
76
|
end
|
|
54
77
|
|
|
55
78
|
describe "when auth requests are signed" do
|
|
56
|
-
let(:
|
|
57
|
-
REXML::XPath.
|
|
79
|
+
let(:key_descriptors) do
|
|
80
|
+
REXML::XPath.match(
|
|
81
|
+
xml_doc,
|
|
82
|
+
"//md:KeyDescriptor",
|
|
83
|
+
"md" => "urn:oasis:names:tc:SAML:2.0:metadata"
|
|
84
|
+
)
|
|
85
|
+
end
|
|
86
|
+
let(:cert_nodes) do
|
|
87
|
+
REXML::XPath.match(
|
|
58
88
|
xml_doc,
|
|
59
89
|
"//md:KeyDescriptor/ds:KeyInfo/ds:X509Data/ds:X509Certificate",
|
|
60
90
|
"md" => "urn:oasis:names:tc:SAML:2.0:metadata",
|
|
61
91
|
"ds" => "http://www.w3.org/2000/09/xmldsig#"
|
|
62
92
|
)
|
|
63
93
|
end
|
|
64
|
-
let(:cert) { OpenSSL::X509::Certificate.new(Base64.decode64(
|
|
94
|
+
let(:cert) { OpenSSL::X509::Certificate.new(Base64.decode64(cert_nodes[0].text)) }
|
|
65
95
|
|
|
66
96
|
before do
|
|
67
|
-
settings.security[:authn_requests_signed] = true
|
|
68
97
|
settings.certificate = ruby_saml_cert_text
|
|
69
98
|
end
|
|
70
99
|
|
|
71
|
-
it "generates Service Provider Metadata with
|
|
100
|
+
it "generates Service Provider Metadata with AuthnRequestsSigned" do
|
|
101
|
+
settings.security[:authn_requests_signed] = true
|
|
72
102
|
assert_equal "true", spsso_descriptor.attribute("AuthnRequestsSigned").value
|
|
73
103
|
assert_equal ruby_saml_cert.to_der, cert.to_der
|
|
104
|
+
|
|
105
|
+
assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it "generates Service Provider Metadata with X509Certificate for sign and encrypt" do
|
|
109
|
+
assert_equal 2, key_descriptors.length
|
|
110
|
+
assert_equal "signing", key_descriptors[0].attribute("use").value
|
|
111
|
+
assert_equal "encryption", key_descriptors[1].attribute("use").value
|
|
112
|
+
|
|
113
|
+
assert_equal 2, cert_nodes.length
|
|
114
|
+
assert_equal ruby_saml_cert.to_der, cert.to_der
|
|
115
|
+
assert_equal cert_nodes[0].text, cert_nodes[1].text
|
|
116
|
+
|
|
117
|
+
assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
|
|
74
118
|
end
|
|
75
119
|
end
|
|
76
120
|
|
|
@@ -93,7 +137,29 @@ class MetadataTest < Minitest::Test
|
|
|
93
137
|
assert_equal "Name", req_attr.attribute("Name").value
|
|
94
138
|
assert_equal "Name Format", req_attr.attribute("NameFormat").value
|
|
95
139
|
assert_equal "Friendly Name", req_attr.attribute("FriendlyName").value
|
|
96
|
-
assert_equal "Attribute Value", REXML::XPath.first(xml_doc, "//
|
|
140
|
+
assert_equal "Attribute Value", REXML::XPath.first(xml_doc, "//saml:AttributeValue").text.strip
|
|
141
|
+
|
|
142
|
+
assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
describe "#service_name" do
|
|
146
|
+
before do
|
|
147
|
+
settings.attribute_consuming_service.service_name("Test2 Service")
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it "change service name" do
|
|
151
|
+
assert_equal REXML::XPath.first(xml_doc, "//md:ServiceName").text.strip, "Test2 Service"
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
describe "#service_index" do
|
|
156
|
+
before do
|
|
157
|
+
settings.attribute_consuming_service.service_index(2)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it "change service index" do
|
|
161
|
+
assert_equal "2", attr_svc.attribute("index").value
|
|
162
|
+
end
|
|
97
163
|
end
|
|
98
164
|
end
|
|
99
165
|
|
|
@@ -110,6 +176,8 @@ class MetadataTest < Minitest::Test
|
|
|
110
176
|
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], xml_text
|
|
111
177
|
signed_metadata = XMLSecurity::SignedDocument.new(xml_text)
|
|
112
178
|
assert signed_metadata.validate_document(ruby_saml_cert_fingerprint, false)
|
|
179
|
+
|
|
180
|
+
assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
|
|
113
181
|
end
|
|
114
182
|
|
|
115
183
|
describe "when digest and signature methods are specified" do
|
|
@@ -126,6 +194,8 @@ class MetadataTest < Minitest::Test
|
|
|
126
194
|
signed_metadata_2 = XMLSecurity::SignedDocument.new(xml_text)
|
|
127
195
|
|
|
128
196
|
assert signed_metadata_2.validate_document(ruby_saml_cert_fingerprint, false)
|
|
197
|
+
|
|
198
|
+
assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
|
|
129
199
|
end
|
|
130
200
|
end
|
|
131
201
|
end
|