ruby-saml 0.8.11 → 0.8.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ruby-saml might be problematic. Click here for more details.
- checksums.yaml +7 -7
- data/Gemfile +3 -1
- data/Rakefile +0 -14
- data/lib/onelogin/ruby-saml/response.rb +24 -13
- data/lib/onelogin/ruby-saml/version.rb +1 -1
- data/lib/xml_security.rb +1 -2
- data/test/logoutrequest_test.rb +124 -126
- data/test/logoutresponse_test.rb +22 -28
- data/test/response_test.rb +171 -122
- data/test/responses/encrypted_new_attack.xml.base64 +1 -0
- data/test/responses/response_with_concealed_signed_assertion.xml +51 -0
- data/test/responses/response_with_doubled_signed_assertion.xml +49 -0
- data/test/responses/response_wrapped.xml.base64 +150 -0
- data/test/responses/valid_response.xml.base64 +1 -0
- data/test/settings_test.rb +5 -5
- data/test/test_helper.rb +48 -11
- data/test/utils_test.rb +10 -10
- data/test/xml_security_test.rb +34 -36
- metadata +55 -48
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
5
|
-
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
1
|
+
---
|
2
|
+
SHA512:
|
3
|
+
metadata.gz: 8a2479b6725a5a9e7fdc76a4bec612e2f0c66cf53cbb79ff7c1dc0343d1cc56c09e9fd3b2d3490bdacbb09b16b473702d42fa42fdb6fedff6e7fa5a44fa421a2
|
4
|
+
data.tar.gz: 43b1cfb12dc3fc14a2cbc139430e3ad15b975dca0d95d0d8c14363f362833de181a52ca10b8b607215c4b5aa23ffa62f2d8f3a3b2c9b5541e9387c635ca77150
|
5
|
+
SHA256:
|
6
|
+
metadata.gz: 694ade703ed05cc38aa2ca98cbfee57cc16223991ae6539422c136164cf29608
|
7
|
+
data.tar.gz: ee07b69a9391b26c9af95d0cfdbaa57c8991fa187b869660e15c549fcbbe47e3
|
data/Gemfile
CHANGED
@@ -7,10 +7,13 @@ gemspec
|
|
7
7
|
|
8
8
|
if RUBY_VERSION < '1.9'
|
9
9
|
gem 'nokogiri', '~> 1.5.0'
|
10
|
+
gem 'minitest', '~> 5.5', '<= 5.11.3'
|
10
11
|
elsif RUBY_VERSION < '2.1'
|
11
12
|
gem 'nokogiri', '>= 1.5.0', '<= 1.6.8.1'
|
13
|
+
gem 'minitest', '~> 5.5'
|
12
14
|
else
|
13
15
|
gem 'nokogiri', '>= 1.5.0'
|
16
|
+
gem 'minitest', '~> 5.5'
|
14
17
|
end
|
15
18
|
|
16
19
|
group :test do
|
@@ -30,6 +33,5 @@ group :test do
|
|
30
33
|
gem 'shoulda', '~> 2.11'
|
31
34
|
gem 'systemu', '~> 2'
|
32
35
|
gem 'test-unit', '~> 3.0.9'
|
33
|
-
gem 'minitest', '~> 5.5'
|
34
36
|
gem 'timecop', '<= 0.6.0'
|
35
37
|
end
|
data/Rakefile
CHANGED
@@ -25,17 +25,3 @@ end
|
|
25
25
|
task :test
|
26
26
|
|
27
27
|
task :default => :test
|
28
|
-
|
29
|
-
# require 'rake/rdoctask'
|
30
|
-
# Rake::RDocTask.new do |rdoc|
|
31
|
-
# if File.exist?('VERSION')
|
32
|
-
# version = File.read('VERSION')
|
33
|
-
# else
|
34
|
-
# version = ""
|
35
|
-
# end
|
36
|
-
|
37
|
-
# rdoc.rdoc_dir = 'rdoc'
|
38
|
-
# rdoc.title = "ruby-saml #{version}"
|
39
|
-
# rdoc.rdoc_files.include('README*')
|
40
|
-
# rdoc.rdoc_files.include('lib/**/*.rb')
|
41
|
-
#end
|
@@ -149,13 +149,13 @@ module OneLogin
|
|
149
149
|
end
|
150
150
|
|
151
151
|
def validate(soft = true)
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
validate_response_state(soft)
|
157
|
-
validate_conditions(soft)
|
158
|
-
validate_audience(soft)
|
152
|
+
validate_structure(soft) &&
|
153
|
+
validate_success_status(soft) &&
|
154
|
+
validate_num_assertion &&
|
155
|
+
validate_signed_elements(soft) &&
|
156
|
+
validate_response_state(soft) &&
|
157
|
+
validate_conditions(soft) &&
|
158
|
+
validate_audience(soft) &&
|
159
159
|
document.validate_document(get_fingerprint, soft) &&
|
160
160
|
success?
|
161
161
|
end
|
@@ -175,10 +175,6 @@ module OneLogin
|
|
175
175
|
{ "a" => ASSERTION }
|
176
176
|
)
|
177
177
|
|
178
|
-
unless assertions.size != 0
|
179
|
-
return soft ? false : validation_error("Encrypted assertion is not supported")
|
180
|
-
end
|
181
|
-
|
182
178
|
unless assertions.size + encrypted_assertions.size == 1
|
183
179
|
return soft ? false : validation_error("SAML Response must contain 1 assertion")
|
184
180
|
end
|
@@ -190,7 +186,7 @@ module OneLogin
|
|
190
186
|
# @return [Boolean] True if there is 1 or 2 Elements signed in the SAML Response
|
191
187
|
# an are a Response or an Assertion Element, otherwise False if soft=True
|
192
188
|
#
|
193
|
-
def validate_signed_elements
|
189
|
+
def validate_signed_elements(soft)
|
194
190
|
signature_nodes = REXML::XPath.match(
|
195
191
|
document,
|
196
192
|
"//ds:Signature",
|
@@ -249,7 +245,7 @@ module OneLogin
|
|
249
245
|
# @return [Boolean] True if the SAML Response contains a Success code, otherwise False if soft == false
|
250
246
|
# @raise [ValidationError] if soft == false and validation fails
|
251
247
|
#
|
252
|
-
def validate_success_status
|
248
|
+
def validate_success_status(soft = true)
|
253
249
|
return true if success?
|
254
250
|
|
255
251
|
return false unless soft
|
@@ -298,6 +294,21 @@ module OneLogin
|
|
298
294
|
end
|
299
295
|
end
|
300
296
|
|
297
|
+
# @return [String] the StatusMessage value from a SAML Response.
|
298
|
+
#
|
299
|
+
def status_message
|
300
|
+
@status_message ||= begin
|
301
|
+
nodes = REXML::XPath.match(
|
302
|
+
document,
|
303
|
+
"/p:Response/p:Status/p:StatusMessage",
|
304
|
+
{ "p" => PROTOCOL }
|
305
|
+
)
|
306
|
+
if nodes.size == 1
|
307
|
+
Utils.element_text(nodes.first)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
301
312
|
def validate_structure(soft = true)
|
302
313
|
Dir.chdir(File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'schemas'))) do
|
303
314
|
@schema = Nokogiri::XML::Schema(IO.read('saml20protocol_schema.xsd'))
|
data/lib/xml_security.rb
CHANGED
@@ -212,10 +212,9 @@ module XMLSecurity
|
|
212
212
|
# create a working copy so we don't modify the original
|
213
213
|
@working_copy ||= REXML::Document.new(self.to_s).root
|
214
214
|
|
215
|
-
# store
|
215
|
+
# store signature node
|
216
216
|
@sig_element ||= begin
|
217
217
|
element = REXML::XPath.first(@working_copy, "//ds:Signature", {"ds"=>DSIG})
|
218
|
-
element.remove
|
219
218
|
end
|
220
219
|
|
221
220
|
# verify signature
|
data/test/logoutrequest_test.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
2
|
-
require 'uuid'
|
3
2
|
|
4
3
|
class LogoutRequestTest < Minitest::Test
|
5
4
|
|
@@ -29,7 +28,7 @@ class LogoutRequestTest < Minitest::Test
|
|
29
28
|
end
|
30
29
|
|
31
30
|
it "set sessionindex" do
|
32
|
-
sessionidx =
|
31
|
+
sessionidx = random_id
|
33
32
|
settings.sessionindex = sessionidx
|
34
33
|
|
35
34
|
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :name_id => "there" })
|
@@ -78,169 +77,168 @@ class LogoutRequestTest < Minitest::Test
|
|
78
77
|
assert_match %r[ID='#{unauth_req.uuid}'], inflated
|
79
78
|
end
|
80
79
|
end
|
81
|
-
end
|
82
80
|
|
83
|
-
describe "when the settings indicate to sign (embedded) logout request" do
|
84
81
|
|
85
|
-
|
86
|
-
# sign the logout request
|
87
|
-
settings.security[:logout_requests_signed] = true
|
88
|
-
settings.security[:embed_sign] = true
|
89
|
-
settings.certificate = ruby_saml_cert_text
|
90
|
-
settings.private_key = ruby_saml_key_text
|
91
|
-
end
|
82
|
+
describe "when the settings indicate to sign (embedded) logout request" do
|
92
83
|
|
93
|
-
|
94
|
-
|
95
|
-
|
84
|
+
before do
|
85
|
+
# sign the logout request
|
86
|
+
settings.security[:logout_requests_signed] = true
|
87
|
+
settings.security[:embed_sign] = true
|
88
|
+
settings.certificate = ruby_saml_cert_text
|
89
|
+
settings.private_key = ruby_saml_key_text
|
90
|
+
end
|
96
91
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
end
|
92
|
+
it "doesn't sign through create_xml_document" do
|
93
|
+
unauth_req = OneLogin::RubySaml::Logoutrequest.new
|
94
|
+
inflated = unauth_req.create_xml_document(settings).to_s
|
101
95
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
96
|
+
refute_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
|
97
|
+
refute_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
|
98
|
+
refute_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
|
99
|
+
end
|
106
100
|
|
107
|
-
|
108
|
-
|
109
|
-
|
101
|
+
it "sign unsigned request" do
|
102
|
+
unauth_req = OneLogin::RubySaml::Logoutrequest.new
|
103
|
+
unauth_req_doc = unauth_req.create_xml_document(settings)
|
104
|
+
inflated = unauth_req_doc.to_s
|
110
105
|
|
111
|
-
|
106
|
+
refute_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
|
107
|
+
refute_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
|
108
|
+
refute_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
|
112
109
|
|
113
|
-
|
114
|
-
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
|
115
|
-
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
|
116
|
-
end
|
110
|
+
inflated = unauth_req.sign_document(unauth_req_doc, settings).to_s
|
117
111
|
|
118
|
-
|
119
|
-
|
120
|
-
|
112
|
+
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
|
113
|
+
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
|
114
|
+
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
|
115
|
+
end
|
121
116
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
end
|
117
|
+
it "signs through create_logout_request_xml_doc" do
|
118
|
+
unauth_req = OneLogin::RubySaml::Logoutrequest.new
|
119
|
+
inflated = unauth_req.create_logout_request_xml_doc(settings).to_s
|
126
120
|
|
127
|
-
|
128
|
-
|
121
|
+
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
|
122
|
+
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
|
123
|
+
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
|
124
|
+
end
|
129
125
|
|
130
|
-
|
131
|
-
|
126
|
+
it "created a signed logout request" do
|
127
|
+
settings.compress_request = true
|
132
128
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
129
|
+
unauth_req = OneLogin::RubySaml::Logoutrequest.new
|
130
|
+
unauth_url = unauth_req.create(settings)
|
131
|
+
|
132
|
+
inflated = decode_saml_request_payload(unauth_url)
|
133
|
+
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
|
134
|
+
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
|
135
|
+
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
|
136
|
+
end
|
138
137
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
138
|
+
it "create a signed logout request with 256 digest and signature method" do
|
139
|
+
settings.compress_request = false
|
140
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
|
141
|
+
settings.security[:digest_method] = XMLSecurity::Document::SHA256
|
143
142
|
|
144
|
-
|
145
|
-
|
143
|
+
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings)
|
144
|
+
request_xml = Base64.decode64(params["SAMLRequest"])
|
146
145
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
146
|
+
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], request_xml
|
147
|
+
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'/>], request_xml
|
148
|
+
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2001/04/xmlenc#sha256'/>], request_xml
|
149
|
+
end
|
151
150
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
151
|
+
it "create a signed logout request with 512 digest and signature method RSA_SHA384" do
|
152
|
+
settings.compress_request = false
|
153
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA384
|
154
|
+
settings.security[:digest_method] = XMLSecurity::Document::SHA512
|
156
155
|
|
157
|
-
|
158
|
-
|
156
|
+
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings)
|
157
|
+
request_xml = Base64.decode64(params["SAMLRequest"])
|
159
158
|
|
160
|
-
|
161
|
-
|
162
|
-
|
159
|
+
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], request_xml
|
160
|
+
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#rsa-sha384'/>], request_xml
|
161
|
+
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2001/04/xmlenc#sha512'/>], request_xml
|
162
|
+
end
|
163
163
|
end
|
164
|
-
end
|
165
164
|
|
166
|
-
|
165
|
+
describe "#create_params when the settings indicate to sign the logout request" do
|
167
166
|
|
168
|
-
|
167
|
+
let(:cert) { OpenSSL::X509::Certificate.new(ruby_saml_cert_text) }
|
169
168
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
169
|
+
before do
|
170
|
+
# sign the logout request
|
171
|
+
settings.security[:logout_requests_signed] = true
|
172
|
+
settings.security[:embed_sign] = false
|
173
|
+
settings.certificate = ruby_saml_cert_text
|
174
|
+
settings.private_key = ruby_saml_key_text
|
175
|
+
end
|
177
176
|
|
178
|
-
|
179
|
-
|
177
|
+
it "create a signature parameter with RSA_SHA1 / SHA1 and validate it" do
|
178
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
|
180
179
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
180
|
+
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
|
181
|
+
assert params['SAMLRequest']
|
182
|
+
assert params[:RelayState]
|
183
|
+
assert params['Signature']
|
184
|
+
assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA1
|
186
185
|
|
187
|
-
|
188
|
-
|
189
|
-
|
186
|
+
query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
|
187
|
+
query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
|
188
|
+
query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
|
190
189
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
190
|
+
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
191
|
+
assert_equal signature_algorithm, OpenSSL::Digest::SHA1
|
192
|
+
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
193
|
+
end
|
195
194
|
|
196
|
-
|
197
|
-
|
195
|
+
it "create a signature parameter with RSA_SHA256 / SHA256 and validate it" do
|
196
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
|
198
197
|
|
199
|
-
|
200
|
-
|
201
|
-
|
198
|
+
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
|
199
|
+
assert params['Signature']
|
200
|
+
assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA256
|
202
201
|
|
203
|
-
|
204
|
-
|
205
|
-
|
202
|
+
query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
|
203
|
+
query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
|
204
|
+
query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
|
206
205
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
206
|
+
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
207
|
+
assert_equal signature_algorithm, OpenSSL::Digest::SHA256
|
208
|
+
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
209
|
+
end
|
211
210
|
|
212
|
-
|
213
|
-
|
211
|
+
it "create a signature parameter with RSA_SHA384 / SHA384 and validate it" do
|
212
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA384
|
214
213
|
|
215
|
-
|
216
|
-
|
217
|
-
|
214
|
+
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
|
215
|
+
assert params['Signature']
|
216
|
+
assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA384
|
218
217
|
|
219
|
-
|
220
|
-
|
221
|
-
|
218
|
+
query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
|
219
|
+
query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
|
220
|
+
query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
|
222
221
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
222
|
+
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
223
|
+
assert_equal signature_algorithm, OpenSSL::Digest::SHA384
|
224
|
+
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
225
|
+
end
|
227
226
|
|
228
|
-
|
229
|
-
|
227
|
+
it "create a signature parameter with RSA_SHA512 / SHA512 and validate it" do
|
228
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA512
|
230
229
|
|
231
|
-
|
232
|
-
|
233
|
-
|
230
|
+
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
|
231
|
+
assert params['Signature']
|
232
|
+
assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA512
|
234
233
|
|
235
|
-
|
236
|
-
|
237
|
-
|
234
|
+
query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
|
235
|
+
query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
|
236
|
+
query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
|
238
237
|
|
239
|
-
|
240
|
-
|
241
|
-
|
238
|
+
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
239
|
+
assert_equal signature_algorithm, OpenSSL::Digest::SHA512
|
240
|
+
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
241
|
+
end
|
242
242
|
end
|
243
|
-
|
244
243
|
end
|
245
|
-
|
246
244
|
end
|