ruby-saml 0.8.18 → 0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +1 -6
- data/Gemfile +2 -12
- data/README.md +363 -35
- data/Rakefile +14 -0
- data/changelog.md +22 -9
- data/lib/onelogin/ruby-saml/attribute_service.rb +34 -0
- data/lib/onelogin/ruby-saml/attributes.rb +26 -64
- data/lib/onelogin/ruby-saml/authrequest.rb +47 -93
- data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +87 -0
- data/lib/onelogin/ruby-saml/logoutrequest.rb +36 -100
- data/lib/onelogin/ruby-saml/logoutresponse.rb +25 -35
- data/lib/onelogin/ruby-saml/metadata.rb +46 -16
- data/lib/onelogin/ruby-saml/response.rb +63 -373
- data/lib/onelogin/ruby-saml/saml_message.rb +78 -0
- data/lib/onelogin/ruby-saml/settings.rb +54 -122
- data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +25 -71
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +37 -102
- data/lib/onelogin/ruby-saml/utils.rb +32 -199
- data/lib/onelogin/ruby-saml/version.rb +1 -1
- data/lib/ruby-saml.rb +5 -2
- data/lib/schemas/{saml20assertion_schema.xsd → saml-schema-assertion-2.0.xsd} +283 -283
- data/lib/schemas/saml-schema-authn-context-2.0.xsd +23 -0
- data/lib/schemas/saml-schema-authn-context-types-2.0.xsd +821 -0
- data/lib/schemas/saml-schema-metadata-2.0.xsd +339 -0
- data/lib/schemas/{saml20protocol_schema.xsd → saml-schema-protocol-2.0.xsd} +302 -302
- data/lib/schemas/sstc-metadata-attr.xsd +35 -0
- data/lib/schemas/sstc-saml-attribute-ext.xsd +25 -0
- data/lib/schemas/sstc-saml-metadata-algsupport-v1.0.xsd +41 -0
- data/lib/schemas/sstc-saml-metadata-ui-v1.0.xsd +89 -0
- data/lib/schemas/{xenc_schema.xsd → xenc-schema.xsd} +1 -11
- data/lib/schemas/xml.xsd +287 -0
- data/lib/schemas/{xmldsig_schema.xsd → xmldsig-core-schema.xsd} +0 -9
- data/lib/xml_security.rb +83 -235
- data/ruby-saml.gemspec +1 -0
- data/test/idp_metadata_parser_test.rb +54 -0
- data/test/logoutrequest_test.rb +68 -155
- data/test/logoutresponse_test.rb +43 -32
- data/test/metadata_test.rb +87 -0
- data/test/request_test.rb +102 -99
- data/test/response_test.rb +181 -495
- data/test/responses/idp_descriptor.xml +3 -0
- data/test/responses/logoutresponse_fixtures.rb +7 -8
- data/test/responses/response_no_cert_and_encrypted_attrs.xml +29 -0
- data/test/responses/response_with_multiple_attribute_values.xml +1 -1
- data/test/responses/slo_request.xml +4 -0
- data/test/settings_test.rb +25 -112
- data/test/slo_logoutrequest_test.rb +40 -50
- data/test/slo_logoutresponse_test.rb +86 -185
- data/test/test_helper.rb +27 -102
- data/test/xml_security_test.rb +114 -337
- metadata +30 -81
- data/lib/onelogin/ruby-saml/setting_error.rb +0 -6
- data/test/certificates/certificate.der +0 -0
- data/test/certificates/formatted_certificate +0 -14
- data/test/certificates/formatted_chained_certificate +0 -42
- data/test/certificates/formatted_private_key +0 -12
- data/test/certificates/formatted_rsa_private_key +0 -12
- data/test/certificates/invalid_certificate1 +0 -1
- data/test/certificates/invalid_certificate2 +0 -1
- data/test/certificates/invalid_certificate3 +0 -12
- data/test/certificates/invalid_chained_certificate1 +0 -1
- data/test/certificates/invalid_private_key1 +0 -1
- data/test/certificates/invalid_private_key2 +0 -1
- data/test/certificates/invalid_private_key3 +0 -10
- data/test/certificates/invalid_rsa_private_key1 +0 -1
- data/test/certificates/invalid_rsa_private_key2 +0 -1
- data/test/certificates/invalid_rsa_private_key3 +0 -10
- data/test/certificates/ruby-saml-2.crt +0 -15
- data/test/requests/logoutrequest_fixtures.rb +0 -47
- data/test/responses/encrypted_new_attack.xml.base64 +0 -1
- data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +0 -1
- data/test/responses/invalids/invalid_issuer_message.xml.base64 +0 -1
- data/test/responses/invalids/multiple_signed.xml.base64 +0 -1
- data/test/responses/invalids/no_signature.xml.base64 +0 -1
- data/test/responses/invalids/response_with_concealed_signed_assertion.xml +0 -51
- data/test/responses/invalids/response_with_doubled_signed_assertion.xml +0 -49
- data/test/responses/invalids/signature_wrapping_attack.xml.base64 +0 -1
- data/test/responses/response_node_text_attack.xml.base64 +0 -1
- data/test/responses/response_with_concealed_signed_assertion.xml +0 -51
- data/test/responses/response_with_doubled_signed_assertion.xml +0 -49
- data/test/responses/response_with_multiple_attribute_statements.xml +0 -72
- data/test/responses/response_with_signed_assertion_3.xml +0 -30
- data/test/responses/response_with_signed_message_and_assertion.xml +0 -34
- data/test/responses/response_with_undefined_recipient.xml.base64 +0 -1
- data/test/responses/response_wrapped.xml.base64 +0 -150
- data/test/responses/valid_response.xml.base64 +0 -1
- data/test/responses/valid_response_without_x509certificate.xml.base64 +0 -1
- data/test/utils_test.rb +0 -231
|
@@ -1,245 +1,146 @@
|
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
|
2
2
|
|
|
3
|
-
class SloLogoutresponseTest <
|
|
3
|
+
class SloLogoutresponseTest < Test::Unit::TestCase
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
context "SloLogoutresponse" do
|
|
6
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
before do
|
|
8
|
+
should "create the deflated SAMLResponse URL parameter" do
|
|
10
9
|
settings.idp_slo_target_url = "http://unauth.com/logout"
|
|
11
10
|
settings.name_identifier_value = "f00f00"
|
|
12
11
|
settings.compress_request = true
|
|
13
|
-
settings.certificate = ruby_saml_cert_text
|
|
14
|
-
settings.private_key = ruby_saml_key_text
|
|
15
|
-
end
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings)
|
|
19
|
-
assert_match /^http:\/\/unauth\.com\/logout\?SAMLResponse=/, unauth_url
|
|
13
|
+
request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document)
|
|
20
14
|
|
|
21
|
-
|
|
22
|
-
assert_match /^<samlp:LogoutResponse/, inflated
|
|
23
|
-
end
|
|
15
|
+
assert request.is_valid?
|
|
24
16
|
|
|
25
|
-
|
|
26
|
-
unauth_url
|
|
27
|
-
assert_match /&hello=$/, unauth_url
|
|
17
|
+
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, request.id)
|
|
18
|
+
assert unauth_url =~ /^http:\/\/unauth\.com\/logout\?SAMLResponse=/
|
|
28
19
|
|
|
29
|
-
|
|
30
|
-
assert_match /&foo=bar$/, unauth_url
|
|
20
|
+
inflated = decode_saml_response_payload(unauth_url)
|
|
31
21
|
|
|
32
|
-
|
|
33
|
-
assert_match /&RelayState=http%3A%2F%2Fidp.example.com$/, unauth_url
|
|
22
|
+
assert_match /^<samlp:LogoutResponse/, inflated
|
|
34
23
|
end
|
|
35
24
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
25
|
+
should "support additional params" do
|
|
26
|
+
settings.idp_slo_target_url = "http://unauth.com/logout"
|
|
27
|
+
settings.name_identifier_value = "f00f00"
|
|
28
|
+
settings.compress_request = true
|
|
39
29
|
|
|
40
|
-
|
|
41
|
-
assert unauth_url.include?('&RelayState=http%3A%2F%2Fexample.com')
|
|
30
|
+
request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document)
|
|
42
31
|
|
|
43
|
-
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings,
|
|
44
|
-
assert
|
|
32
|
+
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, request.id, nil, { :hello => nil })
|
|
33
|
+
assert unauth_url =~ /&hello=$/
|
|
45
34
|
|
|
46
|
-
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings,
|
|
47
|
-
assert unauth_url
|
|
35
|
+
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, request.id, nil, { :foo => "bar" })
|
|
36
|
+
assert unauth_url =~ /&foo=bar$/
|
|
37
|
+
|
|
38
|
+
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, request.id, nil, { :RelayState => "http://idp.example.com" })
|
|
39
|
+
assert unauth_url =~ /&RelayState=http%3A%2F%2Fidp.example.com$/
|
|
48
40
|
end
|
|
49
41
|
|
|
50
|
-
|
|
51
|
-
|
|
42
|
+
should "set InResponseTo to the ID from the logout request" do
|
|
43
|
+
settings.idp_slo_target_url = "http://unauth.com/logout"
|
|
44
|
+
settings.name_identifier_value = "f00f00"
|
|
45
|
+
settings.compress_request = true
|
|
46
|
+
|
|
47
|
+
request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document)
|
|
48
|
+
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, request.id)
|
|
52
49
|
|
|
53
50
|
inflated = decode_saml_response_payload(unauth_url)
|
|
51
|
+
|
|
54
52
|
assert_match /InResponseTo='_c0348950-935b-0131-1060-782bcb56fcaa'/, inflated
|
|
55
53
|
end
|
|
56
54
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
assert_match /<samlp:StatusMessage>Custom Logout Message<\/samlp:StatusMessage>/, inflated
|
|
62
|
-
end
|
|
55
|
+
should "set a custom successful logout message on the response" do
|
|
56
|
+
settings.idp_slo_target_url = "http://unauth.com/logout"
|
|
57
|
+
settings.name_identifier_value = "f00f00"
|
|
58
|
+
settings.compress_request = true
|
|
63
59
|
|
|
64
|
-
|
|
65
|
-
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings,
|
|
60
|
+
request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document)
|
|
61
|
+
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, request.id, "Custom Logout Message")
|
|
66
62
|
|
|
67
63
|
inflated = decode_saml_response_payload(unauth_url)
|
|
64
|
+
|
|
68
65
|
assert_match /<samlp:StatusMessage>Custom Logout Message<\/samlp:StatusMessage>/, inflated
|
|
69
|
-
assert_match /<samlp:StatusCode Value='urn:oasis:names:tc:SAML:2.0:status:PartialLogout/, inflated
|
|
70
66
|
end
|
|
71
67
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
68
|
+
context "when the settings indicate to sign (embebed) the logout response" do
|
|
69
|
+
should "create a signed logout response" do
|
|
70
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
75
71
|
settings.compress_response = false
|
|
72
|
+
settings.idp_slo_target_url = "http://example.com?field=value"
|
|
76
73
|
settings.security[:logout_responses_signed] = true
|
|
77
74
|
settings.security[:embed_sign] = true
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
it "doesn't sign through create_xml_document" do
|
|
81
|
-
unauth_res = OneLogin::RubySaml::SloLogoutresponse.new
|
|
82
|
-
inflated = unauth_res.create_xml_document(settings).to_s
|
|
83
|
-
|
|
84
|
-
refute_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
|
|
85
|
-
refute_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
|
|
86
|
-
refute_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
|
|
87
|
-
end
|
|
75
|
+
settings.certificate = ruby_saml_cert_text
|
|
76
|
+
settings.private_key = ruby_saml_key_text
|
|
88
77
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
unauth_res_doc = unauth_res.create_xml_document(settings)
|
|
92
|
-
inflated = unauth_res_doc.to_s
|
|
93
|
-
|
|
94
|
-
refute_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
|
|
95
|
-
refute_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
|
|
96
|
-
refute_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
|
|
97
|
-
|
|
98
|
-
inflated = unauth_res.sign_document(unauth_res_doc, settings).to_s
|
|
99
|
-
|
|
100
|
-
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
|
|
101
|
-
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
|
|
102
|
-
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
it "signs through create_logout_response_xml_doc" do
|
|
106
|
-
unauth_res = OneLogin::RubySaml::SloLogoutresponse.new
|
|
107
|
-
inflated = unauth_res.create_logout_response_xml_doc(settings).to_s
|
|
108
|
-
|
|
109
|
-
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
|
|
110
|
-
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
|
|
111
|
-
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
it "create a signed logout response" do
|
|
115
|
-
params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, nil, "Custom Logout Message")
|
|
78
|
+
request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document)
|
|
79
|
+
params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, request.id, "Custom Logout Message")
|
|
116
80
|
|
|
117
81
|
response_xml = Base64.decode64(params["SAMLResponse"])
|
|
118
82
|
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], response_xml
|
|
119
|
-
|
|
120
|
-
|
|
83
|
+
response_xml =~ /<ds:SignatureMethod Algorithm='http:\/\/www.w3.org\/2000\/09\/xmldsig#rsa-sha1'\/>/
|
|
84
|
+
response_xml =~ /<ds:DigestMethod Algorithm='http:\/\/www.w3.org\/2000\/09\/xmldsig#rsa-sha1'\/>/
|
|
121
85
|
end
|
|
122
86
|
|
|
123
|
-
|
|
124
|
-
settings
|
|
125
|
-
settings.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], response_xml
|
|
131
|
-
assert_match /<ds:SignatureMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmldsig-more#rsa-sha256'\/>/, response_xml
|
|
132
|
-
assert_match /<ds:DigestMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmlenc#sha256'\/>/, response_xml
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
it "create a signed logout response with 512 digest and signature method RSA_SHA384" do
|
|
136
|
-
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA384
|
|
87
|
+
should "create a signed logout response with 256 digest and signature methods" do
|
|
88
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
89
|
+
settings.compress_response = false
|
|
90
|
+
settings.idp_slo_target_url = "http://example.com?field=value"
|
|
91
|
+
settings.security[:logout_responses_signed] = true
|
|
92
|
+
settings.security[:embed_sign] = true
|
|
93
|
+
settings.security[:signature_method] = XMLSecurity::Document::SHA256
|
|
137
94
|
settings.security[:digest_method] = XMLSecurity::Document::SHA512
|
|
95
|
+
settings.certificate = ruby_saml_cert_text
|
|
96
|
+
settings.private_key = ruby_saml_key_text
|
|
138
97
|
|
|
139
|
-
|
|
98
|
+
request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document)
|
|
99
|
+
params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, request.id, "Custom Logout Message")
|
|
140
100
|
|
|
141
101
|
response_xml = Base64.decode64(params["SAMLResponse"])
|
|
142
102
|
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], response_xml
|
|
143
|
-
|
|
144
|
-
|
|
103
|
+
response_xml =~ /<ds:SignatureMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmldsig-more#rsa-sha256'\/>/
|
|
104
|
+
response_xml =~ /<ds:DigestMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmldsig-more#rsa-sha512'\/>/
|
|
145
105
|
end
|
|
146
106
|
end
|
|
147
107
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
before do
|
|
108
|
+
context "when the settings indicate to sign the logout response" do
|
|
109
|
+
should "create a signature parameter" do
|
|
110
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
153
111
|
settings.compress_response = false
|
|
112
|
+
settings.idp_slo_target_url = "http://example.com?field=value"
|
|
113
|
+
settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign"
|
|
154
114
|
settings.security[:logout_responses_signed] = true
|
|
155
115
|
settings.security[:embed_sign] = false
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
|
|
116
|
+
settings.security[:signature_method] = XMLSecurity::Document::SHA1
|
|
117
|
+
settings.certificate = ruby_saml_cert_text
|
|
118
|
+
settings.private_key = ruby_saml_key_text
|
|
160
119
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
assert params[:RelayState]
|
|
120
|
+
request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document)
|
|
121
|
+
params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, request.id, "Custom Logout Message")
|
|
164
122
|
assert params['Signature']
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
query_string = "SAMLResponse=#{CGI.escape(params['SAMLResponse'])}"
|
|
168
|
-
query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
|
|
169
|
-
query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
|
|
170
|
-
|
|
171
|
-
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
|
172
|
-
assert_equal signature_algorithm, OpenSSL::Digest::SHA1
|
|
173
|
-
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
|
174
|
-
end
|
|
123
|
+
assert params['SigAlg'] == XMLSecurity::Document::SHA1
|
|
175
124
|
|
|
176
|
-
|
|
177
|
-
settings.security[:signature_method] = XMLSecurity::Document::
|
|
178
|
-
|
|
179
|
-
params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, nil, "Custom Logout Message", :RelayState => 'http://example.com')
|
|
180
|
-
assert params['SAMLResponse']
|
|
181
|
-
assert params[:RelayState]
|
|
125
|
+
# signature_method only affects the embedeed signature
|
|
126
|
+
settings.security[:signature_method] = XMLSecurity::Document::SHA256
|
|
127
|
+
params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, request.id, "Custom Logout Message")
|
|
182
128
|
assert params['Signature']
|
|
183
|
-
|
|
184
|
-
assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA256
|
|
185
|
-
|
|
186
|
-
query_string = "SAMLResponse=#{CGI.escape(params['SAMLResponse'])}"
|
|
187
|
-
query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
|
|
188
|
-
query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
|
|
189
|
-
|
|
190
|
-
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
|
191
|
-
assert_equal signature_algorithm, OpenSSL::Digest::SHA256
|
|
192
|
-
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
|
129
|
+
assert params['SigAlg'] == XMLSecurity::Document::SHA1
|
|
193
130
|
end
|
|
131
|
+
end
|
|
194
132
|
|
|
195
|
-
|
|
196
|
-
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA384
|
|
197
|
-
|
|
198
|
-
params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, nil, "Custom Logout Message", :RelayState => 'http://example.com')
|
|
199
|
-
assert params['SAMLResponse']
|
|
200
|
-
assert params[:RelayState]
|
|
201
|
-
assert params['Signature']
|
|
202
|
-
|
|
203
|
-
assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA384
|
|
204
|
-
|
|
205
|
-
query_string = "SAMLResponse=#{CGI.escape(params['SAMLResponse'])}"
|
|
206
|
-
query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
|
|
207
|
-
query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
|
|
208
|
-
|
|
209
|
-
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
|
210
|
-
assert_equal signature_algorithm, OpenSSL::Digest::SHA384
|
|
211
|
-
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
it "create a signature parameter with RSA_SHA512 / SHA512 and validate it" do
|
|
215
|
-
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA512
|
|
216
|
-
|
|
217
|
-
params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, nil, "Custom Logout Message", :RelayState => 'http://example.com')
|
|
218
|
-
assert params['SAMLResponse']
|
|
219
|
-
assert params[:RelayState]
|
|
220
|
-
assert params['Signature']
|
|
221
|
-
|
|
222
|
-
assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA512
|
|
133
|
+
end
|
|
223
134
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
135
|
+
def decode_saml_response_payload(unauth_url)
|
|
136
|
+
payload = CGI.unescape(unauth_url.split("SAMLResponse=").last)
|
|
137
|
+
decoded = Base64.decode64(payload)
|
|
227
138
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
describe "#manipulate response_id" do
|
|
235
|
-
it "be able to modify the response id" do
|
|
236
|
-
logoutresponse = OneLogin::RubySaml::SloLogoutresponse.new
|
|
237
|
-
response_id = logoutresponse.response_id
|
|
238
|
-
assert_equal response_id, logoutresponse.uuid
|
|
239
|
-
logoutresponse.uuid = "new_uuid"
|
|
240
|
-
assert_equal logoutresponse.response_id, logoutresponse.uuid
|
|
241
|
-
assert_equal "new_uuid", logoutresponse.response_id
|
|
242
|
-
end
|
|
243
|
-
end
|
|
139
|
+
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
|
140
|
+
inflated = zstream.inflate(decoded)
|
|
141
|
+
zstream.finish
|
|
142
|
+
zstream.close
|
|
143
|
+
inflated
|
|
244
144
|
end
|
|
145
|
+
|
|
245
146
|
end
|
data/test/test_helper.rb
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
require 'rubygems'
|
|
2
|
-
require '
|
|
3
|
-
require '
|
|
2
|
+
require 'bundler'
|
|
3
|
+
require 'test/unit'
|
|
4
4
|
require 'mocha/setup'
|
|
5
|
-
require 'timecop'
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
require 'uuid'
|
|
9
|
-
else
|
|
10
|
-
require 'securerandom'
|
|
11
|
-
end
|
|
6
|
+
Bundler.require :default, :test
|
|
12
7
|
|
|
13
8
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
14
9
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
@@ -16,7 +11,7 @@ require 'ruby-saml'
|
|
|
16
11
|
|
|
17
12
|
ENV["ruby-saml/testing"] = "1"
|
|
18
13
|
|
|
19
|
-
class
|
|
14
|
+
class Test::Unit::TestCase
|
|
20
15
|
def fixture(document, base64 = true)
|
|
21
16
|
response = Dir.glob(File.join(File.dirname(__FILE__), "responses", "#{document}*")).first
|
|
22
17
|
if base64 && response =~ /\.xml$/
|
|
@@ -26,48 +21,32 @@ class Minitest::Test
|
|
|
26
21
|
end
|
|
27
22
|
end
|
|
28
23
|
|
|
29
|
-
def random_id
|
|
30
|
-
RUBY_VERSION < '1.9' ? "_#{UUID.new.generate}" : "_#{SecureRandom.uuid}"
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def read_invalid_response(response)
|
|
34
|
-
File.read(File.join(File.dirname(__FILE__), "responses", "invalids", response))
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def read_response(response)
|
|
38
|
-
File.read(File.join(File.dirname(__FILE__), "responses", response))
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def read_certificate(certificate)
|
|
42
|
-
File.read(File.join(File.dirname(__FILE__), "certificates", certificate))
|
|
43
|
-
end
|
|
44
|
-
|
|
45
24
|
def response_document
|
|
46
|
-
@response_document ||=
|
|
25
|
+
@response_document ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response1.xml.base64'))
|
|
47
26
|
end
|
|
48
27
|
|
|
49
28
|
def response_document_2
|
|
50
|
-
@response_document2 ||=
|
|
29
|
+
@response_document2 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response2.xml.base64'))
|
|
51
30
|
end
|
|
52
31
|
|
|
53
32
|
def response_document_3
|
|
54
|
-
@response_document3 ||=
|
|
33
|
+
@response_document3 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response3.xml.base64'))
|
|
55
34
|
end
|
|
56
35
|
|
|
57
36
|
def response_document_4
|
|
58
|
-
@response_document4 ||=
|
|
37
|
+
@response_document4 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response4.xml.base64'))
|
|
59
38
|
end
|
|
60
39
|
|
|
61
40
|
def response_document_5
|
|
62
|
-
@response_document5 ||=
|
|
41
|
+
@response_document5 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response5.xml.base64'))
|
|
63
42
|
end
|
|
64
43
|
|
|
65
44
|
def r1_response_document_6
|
|
66
|
-
@response_document6 ||=
|
|
45
|
+
@response_document6 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'r1_response6.xml.base64'))
|
|
67
46
|
end
|
|
68
47
|
|
|
69
48
|
def ampersands_response
|
|
70
|
-
@
|
|
49
|
+
@ampersands_response ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response_with_ampersands.xml.base64'))
|
|
71
50
|
end
|
|
72
51
|
|
|
73
52
|
def response_document_6
|
|
@@ -77,20 +56,8 @@ class Minitest::Test
|
|
|
77
56
|
Base64.encode64(doc)
|
|
78
57
|
end
|
|
79
58
|
|
|
80
|
-
def
|
|
81
|
-
@
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def response_document_valid_signed
|
|
85
|
-
response_document_valid_signed ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'valid_response.xml.base64'))
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def response_document_valid_signed_without_x509certificate
|
|
89
|
-
@response_document_valid_signed_without_x509certificate ||= read_response("valid_response_without_x509certificate.xml.base64")
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def response_document_without_recipient
|
|
93
|
-
@response_document_without_recipient ||= read_response("response_with_undefined_recipient.xml.base64")
|
|
59
|
+
def response_document_7
|
|
60
|
+
@response_document7 ||= Base64.encode64(File.read(File.join(File.dirname(__FILE__), 'responses', 'response_no_cert_and_encrypted_attrs.xml')))
|
|
94
61
|
end
|
|
95
62
|
|
|
96
63
|
def wrapped_response_2
|
|
@@ -101,87 +68,45 @@ class Minitest::Test
|
|
|
101
68
|
@signature_fingerprint1 ||= "C5:19:85:D9:47:F1:BE:57:08:20:25:05:08:46:EB:27:F6:CA:B7:83"
|
|
102
69
|
end
|
|
103
70
|
|
|
104
|
-
def signature_fingerprint_valid_res
|
|
105
|
-
@signature_fingerprint1 ||= "4b68c453c7d994aad9025c99d5efcf566287fe8d"
|
|
106
|
-
end
|
|
107
|
-
|
|
108
71
|
def signature_1
|
|
109
|
-
@signature1 ||=
|
|
72
|
+
@signature1 ||= File.read(File.join(File.dirname(__FILE__), 'certificates', 'certificate1'))
|
|
110
73
|
end
|
|
111
74
|
|
|
112
75
|
def r1_signature_2
|
|
113
|
-
@signature2 ||=
|
|
76
|
+
@signature2 ||= File.read(File.join(File.dirname(__FILE__), 'certificates', 'r1_certificate2_base64'))
|
|
114
77
|
end
|
|
115
78
|
|
|
116
|
-
def
|
|
117
|
-
@
|
|
79
|
+
def idp_metadata
|
|
80
|
+
@idp_metadata ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'idp_descriptor.xml'))
|
|
118
81
|
end
|
|
119
82
|
|
|
120
|
-
def
|
|
121
|
-
@
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def response_multiple_attr_values
|
|
129
|
-
@response_multiple_attr_values = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
83
|
+
def logout_request_document
|
|
84
|
+
unless @logout_request_document
|
|
85
|
+
xml = File.read(File.join(File.dirname(__FILE__), 'responses', 'slo_request.xml'))
|
|
86
|
+
deflated = Zlib::Deflate.deflate(xml, 9)[2..-5]
|
|
87
|
+
@logout_request_document = Base64.encode64(deflated)
|
|
88
|
+
end
|
|
89
|
+
@logout_request_document
|
|
130
90
|
end
|
|
131
91
|
|
|
132
92
|
def ruby_saml_cert
|
|
133
93
|
@ruby_saml_cert ||= OpenSSL::X509::Certificate.new(ruby_saml_cert_text)
|
|
134
94
|
end
|
|
135
95
|
|
|
136
|
-
def ruby_saml_cert2
|
|
137
|
-
@ruby_saml_cert2 ||= OpenSSL::X509::Certificate.new(ruby_saml_cert_text2)
|
|
138
|
-
end
|
|
139
|
-
|
|
140
96
|
def ruby_saml_cert_fingerprint
|
|
141
97
|
@ruby_saml_cert_fingerprint ||= Digest::SHA1.hexdigest(ruby_saml_cert.to_der).scan(/../).join(":")
|
|
142
98
|
end
|
|
143
99
|
|
|
144
100
|
def ruby_saml_cert_text
|
|
145
|
-
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
def ruby_saml_cert_text2
|
|
149
|
-
read_certificate("ruby-saml-2.crt")
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
def ruby_saml_key_text
|
|
153
|
-
read_certificate("ruby-saml.key")
|
|
101
|
+
File.read(File.join(File.dirname(__FILE__), 'certificates', 'ruby-saml.crt'))
|
|
154
102
|
end
|
|
155
103
|
|
|
156
104
|
def ruby_saml_key
|
|
157
105
|
@ruby_saml_key ||= OpenSSL::PKey::RSA.new(ruby_saml_key_text)
|
|
158
106
|
end
|
|
159
107
|
|
|
160
|
-
def
|
|
161
|
-
File.read(File.join(File.dirname(__FILE__),
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
def decode_saml_request_payload(unauth_url)
|
|
165
|
-
payload = CGI.unescape(unauth_url.split("SAMLRequest=").last)
|
|
166
|
-
decoded = Base64.decode64(payload)
|
|
167
|
-
|
|
168
|
-
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
|
169
|
-
inflated = zstream.inflate(decoded)
|
|
170
|
-
zstream.finish
|
|
171
|
-
zstream.close
|
|
172
|
-
inflated
|
|
108
|
+
def ruby_saml_key_text
|
|
109
|
+
File.read(File.join(File.dirname(__FILE__), 'certificates', 'ruby-saml.key'))
|
|
173
110
|
end
|
|
174
111
|
|
|
175
|
-
# decodes a base64 encoded SAML response for use in SloLogoutresponse tests
|
|
176
|
-
#
|
|
177
|
-
def decode_saml_response_payload(unauth_url)
|
|
178
|
-
payload = CGI.unescape(unauth_url.split("SAMLResponse=").last)
|
|
179
|
-
decoded = Base64.decode64(payload)
|
|
180
|
-
|
|
181
|
-
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
|
182
|
-
inflated = zstream.inflate(decoded)
|
|
183
|
-
zstream.finish
|
|
184
|
-
zstream.close
|
|
185
|
-
inflated
|
|
186
|
-
end
|
|
187
112
|
end
|