ruby-saml 1.10.2 → 1.11.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.
Potentially problematic release.
This version of ruby-saml might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.travis.yml +13 -14
- data/README.md +20 -5
- data/lib/onelogin/ruby-saml/authrequest.rb +2 -2
- data/lib/onelogin/ruby-saml/logging.rb +3 -3
- data/lib/onelogin/ruby-saml/logoutrequest.rb +2 -2
- data/lib/onelogin/ruby-saml/logoutresponse.rb +17 -2
- data/lib/onelogin/ruby-saml/metadata.rb +2 -2
- data/lib/onelogin/ruby-saml/response.rb +26 -7
- data/lib/onelogin/ruby-saml/settings.rb +37 -5
- data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +16 -1
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +2 -2
- data/lib/onelogin/ruby-saml/utils.rb +13 -0
- data/lib/onelogin/ruby-saml/version.rb +1 -1
- data/ruby-saml.gemspec +1 -1
- data/test/logoutresponse_test.rb +25 -7
- data/test/metadata_test.rb +1 -1
- data/test/response_test.rb +26 -17
- data/test/settings_test.rb +8 -0
- data/test/slo_logoutrequest_test.rb +19 -0
- data/test/test_helper.rb +2 -0
- data/test/xml_security_test.rb +1 -1
- metadata +18 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: afcd8a95f66ec94e3bb68b5a5264a651ae623923
|
4
|
+
data.tar.gz: eb05bc60959fde11ac20b0af0d14c07b7c5c1b38
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e85dc90f8f4bd5433f0078a6b5e316109dc7bbbe1ba60d5f8075d1f5047a71c208144d1da51157cf7220f2c56fb8b421e7b70ac5c828bea713146fc6bc774a29
|
7
|
+
data.tar.gz: 7cce3fd10ff5d7753d518b847b7f0ff0976b4ef5a1b7cc39c6e6cd3c8b32df7649e95ab929631b9838df5a4fb3eb3f3c50479f54253094b457a484710381b8f1
|
data/.travis.yml
CHANGED
@@ -1,19 +1,18 @@
|
|
1
|
-
sudo: false
|
2
1
|
language: ruby
|
3
2
|
rvm:
|
4
3
|
- 1.8.7
|
5
4
|
- 1.9.3
|
6
5
|
- 2.0.0
|
7
|
-
- 2.1.
|
8
|
-
- 2.2.
|
9
|
-
- 2.3.
|
10
|
-
- 2.4.
|
11
|
-
- 2.5.
|
12
|
-
- 2.6.
|
6
|
+
- 2.1.10
|
7
|
+
- 2.2.10
|
8
|
+
- 2.3.8
|
9
|
+
- 2.4.6
|
10
|
+
- 2.5.5
|
11
|
+
- 2.6.3
|
13
12
|
- ree
|
14
13
|
- jruby-1.7.27
|
15
14
|
- jruby-9.1.17.0
|
16
|
-
- jruby-9.2.
|
15
|
+
- jruby-9.2.7.0
|
17
16
|
gemfile:
|
18
17
|
- Gemfile
|
19
18
|
- gemfiles/nokogiri-1.5.gemfile
|
@@ -29,19 +28,19 @@ matrix:
|
|
29
28
|
gemfile: gemfiles/nokogiri-1.5.gemfile
|
30
29
|
- rvm: jruby-9.1.17.0
|
31
30
|
gemfile: gemfiles/nokogiri-1.5.gemfile
|
32
|
-
- rvm: jruby-9.2.
|
31
|
+
- rvm: jruby-9.2.7.0
|
33
32
|
gemfile: gemfiles/nokogiri-1.5.gemfile
|
34
33
|
- rvm: 2.1.5
|
35
34
|
gemfile: gemfiles/nokogiri-1.5.gemfile
|
36
|
-
- rvm: 2.2.
|
35
|
+
- rvm: 2.2.10
|
37
36
|
gemfile: gemfiles/nokogiri-1.5.gemfile
|
38
|
-
- rvm: 2.3.
|
37
|
+
- rvm: 2.3.8
|
39
38
|
gemfile: gemfiles/nokogiri-1.5.gemfile
|
40
|
-
- rvm: 2.4.
|
39
|
+
- rvm: 2.4.6
|
41
40
|
gemfile: gemfiles/nokogiri-1.5.gemfile
|
42
|
-
- rvm: 2.5.
|
41
|
+
- rvm: 2.5.5
|
43
42
|
gemfile: gemfiles/nokogiri-1.5.gemfile
|
44
|
-
- rvm: 2.6.
|
43
|
+
- rvm: 2.6.3
|
45
44
|
gemfile: gemfiles/nokogiri-1.5.gemfile
|
46
45
|
env:
|
47
46
|
- JRUBY_OPTS="--debug"
|
data/README.md
CHANGED
@@ -123,6 +123,17 @@ We created a demo project for Rails4 that uses the latest version of this librar
|
|
123
123
|
|
124
124
|
If you believe you have discovered a security vulnerability in this gem, please report it at https://www.onelogin.com/security with a description. We follow responsible disclosure guidelines, and will work with you to quickly find a resolution.
|
125
125
|
|
126
|
+
### Security warning
|
127
|
+
|
128
|
+
Some tools may incorrectly report ruby-saml is a potential security vulnerability.
|
129
|
+
ruby-saml depends on Nokogiri, and it's possible to use Nokogiri in a dangerous way
|
130
|
+
(by enabling its DTDLOAD option and disabling its NONET option).
|
131
|
+
This dangerous Nokogiri configuration, which is sometimes used by other components,
|
132
|
+
can create an XML External Entity (XXE) vulnerability if the XML data is not trusted.
|
133
|
+
However, ruby-saml never enables this dangerous Nokogiri configuration;
|
134
|
+
ruby-saml never enables DTDLOAD, and it never disables NONET.
|
135
|
+
|
136
|
+
|
126
137
|
## Getting Started
|
127
138
|
In order to use the toolkit you will need to install the gem (either manually or using Bundler), and require the library in your Ruby application:
|
128
139
|
|
@@ -238,7 +249,7 @@ def saml_settings
|
|
238
249
|
settings = OneLogin::RubySaml::Settings.new
|
239
250
|
|
240
251
|
settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
|
241
|
-
settings.
|
252
|
+
settings.sp_entity_id = "http://#{request.host}/saml/metadata"
|
242
253
|
settings.idp_entity_id = "https://app.onelogin.com/saml/metadata/#{OneLoginAppId}"
|
243
254
|
settings.idp_sso_target_url = "https://app.onelogin.com/trust/saml2/http-post/sso/#{OneLoginAppId}"
|
244
255
|
settings.idp_slo_target_url = "https://app.onelogin.com/trust/saml2/http-redirect/slo/#{OneLoginAppId}"
|
@@ -262,6 +273,8 @@ def saml_settings
|
|
262
273
|
end
|
263
274
|
```
|
264
275
|
|
276
|
+
The use of settings.issuer is deprecated in favour of settings.sp_entity_id
|
277
|
+
|
265
278
|
Some assertion validations can be skipped by passing parameters to `OneLogin::RubySaml::Response.new()`. For example, you can skip the `AuthnStatement`, `Conditions`, `Recipient`, or the `SubjectConfirmation` validations by initializing the response with different options:
|
266
279
|
|
267
280
|
```ruby
|
@@ -301,7 +314,7 @@ class SamlController < ApplicationController
|
|
301
314
|
settings = OneLogin::RubySaml::Settings.new
|
302
315
|
|
303
316
|
settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
|
304
|
-
settings.
|
317
|
+
settings.sp_entity_id = "http://#{request.host}/saml/metadata"
|
305
318
|
settings.idp_sso_target_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}"
|
306
319
|
settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
|
307
320
|
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
@@ -364,7 +377,7 @@ def saml_settings
|
|
364
377
|
settings = idp_metadata_parser.parse_remote("https://example.com/auth/saml2/idp/metadata")
|
365
378
|
|
366
379
|
settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
|
367
|
-
settings.
|
380
|
+
settings.sp_entity_id = "http://#{request.host}/saml/metadata"
|
368
381
|
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
369
382
|
# Optional for most SAML IdPs
|
370
383
|
settings.authn_context = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
|
@@ -378,8 +391,8 @@ The following attributes are set:
|
|
378
391
|
* idp_sso_target_url
|
379
392
|
* idp_slo_target_url
|
380
393
|
* idp_attribute_names
|
381
|
-
* idp_cert
|
382
|
-
* idp_cert_fingerprint
|
394
|
+
* idp_cert
|
395
|
+
* idp_cert_fingerprint
|
383
396
|
* idp_cert_multi
|
384
397
|
|
385
398
|
### Retrieve one Entity Descriptor when many exist in Metadata
|
@@ -548,6 +561,8 @@ The settings related to sign are stored in the `security` attribute of the setti
|
|
548
561
|
# Embeded signature or HTTP GET parameter signature
|
549
562
|
# Note that metadata signature is always embedded regardless of this value.
|
550
563
|
settings.security[:embed_sign] = false
|
564
|
+
settings.security[:check_idp_cert_expiration] = false # Enable or not IdP x509 cert expiration check
|
565
|
+
settings.security[:check_sp_cert_expiration] = false # Enable or not SP x509 cert expiration check
|
551
566
|
```
|
552
567
|
|
553
568
|
Notice that the RelayState parameter is used when creating the Signature on the HTTP-Redirect Binding.
|
@@ -117,9 +117,9 @@ module OneLogin
|
|
117
117
|
if settings.assertion_consumer_service_url != nil
|
118
118
|
root.attributes["AssertionConsumerServiceURL"] = settings.assertion_consumer_service_url
|
119
119
|
end
|
120
|
-
if settings.
|
120
|
+
if settings.sp_entity_id != nil
|
121
121
|
issuer = root.add_element "saml:Issuer"
|
122
|
-
issuer.text = settings.
|
122
|
+
issuer.text = settings.sp_entity_id
|
123
123
|
end
|
124
124
|
|
125
125
|
if settings.name_identifier_value_requested != nil
|
@@ -8,9 +8,9 @@ module OneLogin
|
|
8
8
|
|
9
9
|
def self.logger
|
10
10
|
@logger ||= begin
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
(defined?(::Rails) && Rails.respond_to?(:logger) && Rails.logger) ||
|
12
|
+
DEFAULT_LOGGER
|
13
|
+
end
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.logger=(logger)
|
@@ -105,9 +105,9 @@ module OneLogin
|
|
105
105
|
root.attributes['Version'] = "2.0"
|
106
106
|
root.attributes['Destination'] = settings.idp_slo_target_url unless settings.idp_slo_target_url.nil?
|
107
107
|
|
108
|
-
if settings.
|
108
|
+
if settings.sp_entity_id
|
109
109
|
issuer = root.add_element "saml:Issuer"
|
110
|
-
issuer.text = settings.
|
110
|
+
issuer.text = settings.sp_entity_id
|
111
111
|
end
|
112
112
|
|
113
113
|
nameid = root.add_element "saml:NameID"
|
@@ -163,7 +163,7 @@ module OneLogin
|
|
163
163
|
|
164
164
|
return append_error("No settings on logout response") if settings.nil?
|
165
165
|
|
166
|
-
return append_error("No
|
166
|
+
return append_error("No sp_entity_id in settings of the logout response") if settings.sp_entity_id.nil?
|
167
167
|
|
168
168
|
if settings.idp_cert_fingerprint.nil? && settings.idp_cert.nil? && settings.idp_cert_multi.nil?
|
169
169
|
return append_error("No fingerprint or certificate on settings of the logout response")
|
@@ -228,13 +228,19 @@ module OneLogin
|
|
228
228
|
:raw_sig_alg => options[:raw_get_params]['SigAlg']
|
229
229
|
)
|
230
230
|
|
231
|
+
expired = false
|
231
232
|
if idp_certs.nil? || idp_certs[:signing].empty?
|
232
233
|
valid = OneLogin::RubySaml::Utils.verify_signature(
|
233
|
-
:cert =>
|
234
|
+
:cert => idp_cert,
|
234
235
|
:sig_alg => options[:get_params]['SigAlg'],
|
235
236
|
:signature => options[:get_params]['Signature'],
|
236
237
|
:query_string => query_string
|
237
238
|
)
|
239
|
+
if valid && settings.security[:check_idp_cert_expiration]
|
240
|
+
if OneLogin::RubySaml::Utils.is_cert_expired(idp_cert)
|
241
|
+
expired = true
|
242
|
+
end
|
243
|
+
end
|
238
244
|
else
|
239
245
|
valid = false
|
240
246
|
idp_certs[:signing].each do |signing_idp_cert|
|
@@ -245,11 +251,20 @@ module OneLogin
|
|
245
251
|
:query_string => query_string
|
246
252
|
)
|
247
253
|
if valid
|
254
|
+
if settings.security[:check_idp_cert_expiration]
|
255
|
+
if OneLogin::RubySaml::Utils.is_cert_expired(signing_idp_cert)
|
256
|
+
expired = true
|
257
|
+
end
|
258
|
+
end
|
248
259
|
break
|
249
260
|
end
|
250
261
|
end
|
251
262
|
end
|
252
263
|
|
264
|
+
if expired
|
265
|
+
error_msg = "IdP x509 certificate expired"
|
266
|
+
return append_error(error_msg)
|
267
|
+
end
|
253
268
|
unless valid
|
254
269
|
error_msg = "Invalid Signature on Logout Response"
|
255
270
|
return append_error(error_msg)
|
@@ -57,8 +57,8 @@ module OneLogin
|
|
57
57
|
end
|
58
58
|
|
59
59
|
root.attributes["ID"] = OneLogin::RubySaml::Utils.uuid
|
60
|
-
if settings.
|
61
|
-
root.attributes["entityID"] = settings.
|
60
|
+
if settings.sp_entity_id
|
61
|
+
root.attributes["entityID"] = settings.sp_entity_id
|
62
62
|
end
|
63
63
|
if settings.single_logout_service_url
|
64
64
|
sp_sso.add_element "md:SingleLogoutService", {
|
@@ -589,11 +589,11 @@ module OneLogin
|
|
589
589
|
# @raise [ValidationError] if soft == false and validation fails
|
590
590
|
#
|
591
591
|
def validate_audience
|
592
|
-
return true if audiences.empty? || settings.
|
592
|
+
return true if audiences.empty? || settings.sp_entity_id.nil? || settings.sp_entity_id.empty?
|
593
593
|
|
594
|
-
unless audiences.include? settings.
|
594
|
+
unless audiences.include? settings.sp_entity_id
|
595
595
|
s = audiences.count > 1 ? 's' : '';
|
596
|
-
error_msg = "Invalid Audience#{s}. The audience#{s} #{audiences.join(',')}, did not match the expected audience #{settings.
|
596
|
+
error_msg = "Invalid Audience#{s}. The audience#{s} #{audiences.join(',')}, did not match the expected audience #{settings.sp_entity_id}"
|
597
597
|
return append_error(error_msg)
|
598
598
|
end
|
599
599
|
|
@@ -781,8 +781,8 @@ module OneLogin
|
|
781
781
|
return append_error("An empty NameID value found")
|
782
782
|
end
|
783
783
|
|
784
|
-
unless settings.
|
785
|
-
if name_id_spnamequalifier != settings.
|
784
|
+
unless settings.sp_entity_id.nil? || settings.sp_entity_id.empty? || name_id_spnamequalifier.nil? || name_id_spnamequalifier.empty?
|
785
|
+
if name_id_spnamequalifier != settings.sp_entity_id
|
786
786
|
return append_error("The SPNameQualifier value mistmatch the SP entityID value.")
|
787
787
|
end
|
788
788
|
end
|
@@ -829,24 +829,43 @@ module OneLogin
|
|
829
829
|
return append_error(error_msg)
|
830
830
|
end
|
831
831
|
|
832
|
+
|
832
833
|
idp_certs = settings.get_idp_cert_multi
|
833
834
|
if idp_certs.nil? || idp_certs[:signing].empty?
|
834
835
|
opts = {}
|
835
836
|
opts[:fingerprint_alg] = settings.idp_cert_fingerprint_algorithm
|
836
|
-
|
837
|
+
idp_cert = settings.get_idp_cert
|
837
838
|
fingerprint = settings.get_fingerprint
|
839
|
+
opts[:cert] = idp_cert
|
838
840
|
|
839
|
-
|
841
|
+
if fingerprint && doc.validate_document(fingerprint, @soft, opts)
|
842
|
+
if settings.security[:check_idp_cert_expiration]
|
843
|
+
if OneLogin::RubySaml::Utils.is_cert_expired(idp_cert)
|
844
|
+
error_msg = "IdP x509 certificate expired"
|
845
|
+
return append_error(error_msg)
|
846
|
+
end
|
847
|
+
end
|
848
|
+
else
|
840
849
|
return append_error(error_msg)
|
841
850
|
end
|
842
851
|
else
|
843
852
|
valid = false
|
853
|
+
expired = false
|
844
854
|
idp_certs[:signing].each do |idp_cert|
|
845
855
|
valid = doc.validate_document_with_cert(idp_cert)
|
846
856
|
if valid
|
857
|
+
if settings.security[:check_idp_cert_expiration]
|
858
|
+
if OneLogin::RubySaml::Utils.is_cert_expired(idp_cert)
|
859
|
+
expired = true
|
860
|
+
end
|
861
|
+
end
|
847
862
|
break
|
848
863
|
end
|
849
864
|
end
|
865
|
+
if expired
|
866
|
+
error_msg = "IdP x509 certificate expired"
|
867
|
+
return append_error(error_msg)
|
868
|
+
end
|
850
869
|
unless valid
|
851
870
|
return append_error(error_msg)
|
852
871
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require "xml_security"
|
2
2
|
require "onelogin/ruby-saml/attribute_service"
|
3
3
|
require "onelogin/ruby-saml/utils"
|
4
|
+
require "onelogin/ruby-saml/validation_error"
|
4
5
|
|
5
6
|
# Only supports SAML 2.0
|
6
7
|
module OneLogin
|
@@ -40,7 +41,6 @@ module OneLogin
|
|
40
41
|
attr_accessor :idp_name_qualifier
|
41
42
|
attr_accessor :valid_until
|
42
43
|
# SP Data
|
43
|
-
attr_accessor :issuer
|
44
44
|
attr_accessor :assertion_consumer_service_url
|
45
45
|
attr_accessor :assertion_consumer_service_binding
|
46
46
|
attr_accessor :sp_name_qualifier
|
@@ -68,6 +68,28 @@ module OneLogin
|
|
68
68
|
# Compability
|
69
69
|
attr_accessor :assertion_consumer_logout_service_url
|
70
70
|
attr_accessor :assertion_consumer_logout_service_binding
|
71
|
+
attr_accessor :issuer
|
72
|
+
|
73
|
+
# @return [String] SP Entity ID
|
74
|
+
#
|
75
|
+
def sp_entity_id
|
76
|
+
val = nil
|
77
|
+
if @sp_entity_id.nil?
|
78
|
+
if @issuer
|
79
|
+
val = @issuer
|
80
|
+
end
|
81
|
+
else
|
82
|
+
val = @sp_entity_id
|
83
|
+
end
|
84
|
+
val
|
85
|
+
end
|
86
|
+
|
87
|
+
# Setter for SP Entity ID.
|
88
|
+
# @param val [String].
|
89
|
+
#
|
90
|
+
def sp_entity_id=(val)
|
91
|
+
@sp_entity_id = val
|
92
|
+
end
|
71
93
|
|
72
94
|
# @return [String] Single Logout Service URL.
|
73
95
|
#
|
@@ -167,7 +189,15 @@ module OneLogin
|
|
167
189
|
return nil if certificate.nil? || certificate.empty?
|
168
190
|
|
169
191
|
formatted_cert = OneLogin::RubySaml::Utils.format_cert(certificate)
|
170
|
-
OpenSSL::X509::Certificate.new(formatted_cert)
|
192
|
+
cert = OpenSSL::X509::Certificate.new(formatted_cert)
|
193
|
+
|
194
|
+
if security[:check_sp_cert_expiration]
|
195
|
+
if OneLogin::RubySaml::Utils.is_cert_expired(cert)
|
196
|
+
raise OneLogin::RubySaml::ValidationError.new("The SP certificate expired.")
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
cert
|
171
201
|
end
|
172
202
|
|
173
203
|
# @return [OpenSSL::X509::Certificate|nil] Build the New SP certificate from the settings (previously format it)
|
@@ -197,6 +227,7 @@ module OneLogin
|
|
197
227
|
:compress_request => true,
|
198
228
|
:compress_response => true,
|
199
229
|
:soft => true,
|
230
|
+
:double_quote_xml_attribute_values => false,
|
200
231
|
:security => {
|
201
232
|
:authn_requests_signed => false,
|
202
233
|
:logout_requests_signed => false,
|
@@ -207,9 +238,10 @@ module OneLogin
|
|
207
238
|
:metadata_signed => false,
|
208
239
|
:embed_sign => false,
|
209
240
|
:digest_method => XMLSecurity::Document::SHA1,
|
210
|
-
:signature_method => XMLSecurity::Document::RSA_SHA1
|
211
|
-
|
212
|
-
|
241
|
+
:signature_method => XMLSecurity::Document::RSA_SHA1,
|
242
|
+
:check_idp_cert_expiration => false,
|
243
|
+
:check_sp_cert_expiration => false
|
244
|
+
}.freeze
|
213
245
|
}.freeze
|
214
246
|
end
|
215
247
|
end
|
@@ -280,13 +280,19 @@ module OneLogin
|
|
280
280
|
:raw_sig_alg => options[:raw_get_params]['SigAlg']
|
281
281
|
)
|
282
282
|
|
283
|
+
expired = false
|
283
284
|
if idp_certs.nil? || idp_certs[:signing].empty?
|
284
285
|
valid = OneLogin::RubySaml::Utils.verify_signature(
|
285
|
-
:cert =>
|
286
|
+
:cert => idp_cert,
|
286
287
|
:sig_alg => options[:get_params]['SigAlg'],
|
287
288
|
:signature => options[:get_params]['Signature'],
|
288
289
|
:query_string => query_string
|
289
290
|
)
|
291
|
+
if valid && settings.security[:check_idp_cert_expiration]
|
292
|
+
if OneLogin::RubySaml::Utils.is_cert_expired(idp_cert)
|
293
|
+
expired = true
|
294
|
+
end
|
295
|
+
end
|
290
296
|
else
|
291
297
|
valid = false
|
292
298
|
idp_certs[:signing].each do |signing_idp_cert|
|
@@ -297,11 +303,20 @@ module OneLogin
|
|
297
303
|
:query_string => query_string
|
298
304
|
)
|
299
305
|
if valid
|
306
|
+
if settings.security[:check_idp_cert_expiration]
|
307
|
+
if OneLogin::RubySaml::Utils.is_cert_expired(signing_idp_cert)
|
308
|
+
expired = true
|
309
|
+
end
|
310
|
+
end
|
300
311
|
break
|
301
312
|
end
|
302
313
|
end
|
303
314
|
end
|
304
315
|
|
316
|
+
if expired
|
317
|
+
error_msg = "IdP x509 certificate expired"
|
318
|
+
return append_error(error_msg)
|
319
|
+
end
|
305
320
|
unless valid
|
306
321
|
return append_error("Invalid Signature on Logout Request")
|
307
322
|
end
|
@@ -114,9 +114,9 @@ module OneLogin
|
|
114
114
|
root.attributes['InResponseTo'] = request_id unless request_id.nil?
|
115
115
|
root.attributes['Destination'] = settings.idp_slo_target_url unless settings.idp_slo_target_url.nil?
|
116
116
|
|
117
|
-
if settings.
|
117
|
+
if settings.sp_entity_id != nil
|
118
118
|
issuer = root.add_element "saml:Issuer"
|
119
|
-
issuer.text = settings.
|
119
|
+
issuer.text = settings.sp_entity_id
|
120
120
|
end
|
121
121
|
|
122
122
|
# add success message
|
@@ -3,6 +3,7 @@ if RUBY_VERSION < '1.9'
|
|
3
3
|
else
|
4
4
|
require 'securerandom'
|
5
5
|
end
|
6
|
+
require "openssl"
|
6
7
|
|
7
8
|
module OneLogin
|
8
9
|
module RubySaml
|
@@ -15,6 +16,18 @@ module OneLogin
|
|
15
16
|
DSIG = "http://www.w3.org/2000/09/xmldsig#"
|
16
17
|
XENC = "http://www.w3.org/2001/04/xmlenc#"
|
17
18
|
|
19
|
+
# Checks if the x509 cert provided is expired
|
20
|
+
#
|
21
|
+
# @param cert [Certificate] The x509 certificate
|
22
|
+
#
|
23
|
+
def self.is_cert_expired(cert)
|
24
|
+
if cert.is_a?(String)
|
25
|
+
cert = OpenSSL::X509::Certificate.new(cert)
|
26
|
+
end
|
27
|
+
|
28
|
+
return cert.not_after < Time.now
|
29
|
+
end
|
30
|
+
|
18
31
|
# Return a properly formatted x509 certificate
|
19
32
|
#
|
20
33
|
# @param cert [String] The original certificate
|
data/ruby-saml.gemspec
CHANGED
@@ -17,7 +17,6 @@ Gem::Specification.new do |s|
|
|
17
17
|
]
|
18
18
|
s.files = `git ls-files`.split("\n")
|
19
19
|
s.homepage = %q{http://github.com/onelogin/ruby-saml}
|
20
|
-
s.rubyforge_project = %q{http://www.rubygems.org/gems/ruby-saml}
|
21
20
|
s.rdoc_options = ["--charset=UTF-8"]
|
22
21
|
s.require_paths = ["lib"]
|
23
22
|
s.rubygems_version = %q{1.3.7}
|
@@ -44,6 +43,7 @@ Gem::Specification.new do |s|
|
|
44
43
|
s.add_runtime_dependency('nokogiri', '>= 1.5.10')
|
45
44
|
end
|
46
45
|
|
46
|
+
s.add_development_dependency('coveralls')
|
47
47
|
s.add_development_dependency('minitest', '~> 5.5')
|
48
48
|
s.add_development_dependency('mocha', '~> 0.14')
|
49
49
|
s.add_development_dependency('rake', '~> 10')
|
data/test/logoutresponse_test.rb
CHANGED
@@ -62,7 +62,7 @@ class RubySamlTest < Minitest::Test
|
|
62
62
|
|
63
63
|
assert logoutresponse.validate
|
64
64
|
|
65
|
-
assert_equal settings.
|
65
|
+
assert_equal settings.sp_entity_id, logoutresponse.issuer
|
66
66
|
assert_equal in_relation_to_request_id, logoutresponse.in_response_to
|
67
67
|
|
68
68
|
assert logoutresponse.success?
|
@@ -123,12 +123,13 @@ class RubySamlTest < Minitest::Test
|
|
123
123
|
assert_includes logoutresponse.errors, "The status code of the Logout Response was not Success, was Requester -> Logoutrequest expired"
|
124
124
|
end
|
125
125
|
|
126
|
-
it "invalidate logout response when in lack of
|
126
|
+
it "invalidate logout response when in lack of sp_entity_id setting" do
|
127
127
|
bad_settings = settings
|
128
128
|
bad_settings.issuer = nil
|
129
|
+
bad_settings.sp_entity_id = nil
|
129
130
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, bad_settings)
|
130
131
|
assert !logoutresponse.validate
|
131
|
-
assert_includes logoutresponse.errors, "No
|
132
|
+
assert_includes logoutresponse.errors, "No sp_entity_id in settings of the logout response"
|
132
133
|
end
|
133
134
|
|
134
135
|
it "invalidate logout response with wrong issuer" do
|
@@ -139,7 +140,7 @@ class RubySamlTest < Minitest::Test
|
|
139
140
|
assert_includes logoutresponse.errors, "Doesn't match the issuer, expected: <#{logoutresponse.settings.idp_entity_id}>, but was: <http://app.muda.no>"
|
140
141
|
end
|
141
142
|
|
142
|
-
it "collect errors when collect_errors=true" do
|
143
|
+
it "collect errors when collect_errors=true" do
|
143
144
|
settings.idp_entity_id = 'http://invalid.issuer.example.com/'
|
144
145
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
|
145
146
|
collect_errors = true
|
@@ -184,7 +185,7 @@ class RubySamlTest < Minitest::Test
|
|
184
185
|
opts = { :matches_request_id => expected_request_id}
|
185
186
|
|
186
187
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings, opts)
|
187
|
-
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
|
188
|
+
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
|
188
189
|
assert_includes logoutresponse.errors, "The InResponseTo of the Logout Response: #{logoutresponse.in_response_to}, does not match the ID of the Logout Request sent by the SP: #{expected_request_id}"
|
189
190
|
end
|
190
191
|
|
@@ -202,11 +203,12 @@ class RubySamlTest < Minitest::Test
|
|
202
203
|
assert_includes logoutresponse.errors, "The status code of the Logout Response was not Success, was Requester"
|
203
204
|
end
|
204
205
|
|
205
|
-
it "raise validation error when in lack of
|
206
|
+
it "raise validation error when in lack of sp_entity_id setting" do
|
206
207
|
settings.issuer = nil
|
208
|
+
settings.sp_entity_id = nil
|
207
209
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
|
208
210
|
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
|
209
|
-
assert_includes logoutresponse.errors, "No
|
211
|
+
assert_includes logoutresponse.errors, "No sp_entity_id in settings of the logout response"
|
210
212
|
end
|
211
213
|
|
212
214
|
it "raise validation error when logout response with wrong issuer" do
|
@@ -392,6 +394,21 @@ class RubySamlTest < Minitest::Test
|
|
392
394
|
assert logoutresponse_sign_test.send(:validate_signature)
|
393
395
|
end
|
394
396
|
|
397
|
+
it "return false when cert expired and check_idp_cert_expiration expired" do
|
398
|
+
params['RelayState'] = params[:RelayState]
|
399
|
+
options = {}
|
400
|
+
options[:get_params] = params
|
401
|
+
settings.security[:check_idp_cert_expiration] = true
|
402
|
+
settings.idp_cert = nil
|
403
|
+
settings.idp_cert_multi = {
|
404
|
+
:signing => [ruby_saml_cert_text],
|
405
|
+
:encryption => []
|
406
|
+
}
|
407
|
+
logoutresponse_sign_test = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
|
408
|
+
assert !logoutresponse_sign_test.send(:validate_signature)
|
409
|
+
assert_includes logoutresponse_sign_test.errors, "IdP x509 certificate expired"
|
410
|
+
end
|
411
|
+
|
395
412
|
it "return false when none cert on idp_cert_multi is valid" do
|
396
413
|
params['RelayState'] = params[:RelayState]
|
397
414
|
options = {}
|
@@ -402,6 +419,7 @@ class RubySamlTest < Minitest::Test
|
|
402
419
|
}
|
403
420
|
logoutresponse_sign_test = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
|
404
421
|
assert !logoutresponse_sign_test.send(:validate_signature)
|
422
|
+
assert_includes logoutresponse_sign_test.errors, "Invalid Signature on Logout Response"
|
405
423
|
end
|
406
424
|
end
|
407
425
|
end
|
data/test/metadata_test.rb
CHANGED
@@ -12,7 +12,7 @@ class MetadataTest < Minitest::Test
|
|
12
12
|
let(:acs) { REXML::XPath.first(xml_doc, "//md:AssertionConsumerService") }
|
13
13
|
|
14
14
|
before do
|
15
|
-
settings.
|
15
|
+
settings.sp_entity_id = "https://example.com"
|
16
16
|
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
17
17
|
settings.assertion_consumer_service_url = "https://foo.example/saml/consume"
|
18
18
|
end
|
data/test/response_test.rb
CHANGED
@@ -259,10 +259,10 @@ class RubySamlTest < Minitest::Test
|
|
259
259
|
|
260
260
|
it "raise when there is no valid audience" do
|
261
261
|
settings.idp_cert_fingerprint = signature_fingerprint_1
|
262
|
-
settings.
|
262
|
+
settings.sp_entity_id = 'invalid'
|
263
263
|
response_valid_signed.settings = settings
|
264
264
|
response_valid_signed.soft = false
|
265
|
-
error_msg = generate_audience_error(response_valid_signed.settings.
|
265
|
+
error_msg = generate_audience_error(response_valid_signed.settings.sp_entity_id, ['https://someone.example.com/audience'])
|
266
266
|
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
267
267
|
response_valid_signed.is_valid?
|
268
268
|
end
|
@@ -415,11 +415,11 @@ class RubySamlTest < Minitest::Test
|
|
415
415
|
|
416
416
|
it "return false when there is no valid audience" do
|
417
417
|
settings.idp_cert_fingerprint = signature_fingerprint_1
|
418
|
-
settings.
|
418
|
+
settings.sp_entity_id = 'invalid'
|
419
419
|
response_valid_signed.settings = settings
|
420
420
|
response_valid_signed.is_valid?
|
421
421
|
|
422
|
-
assert_includes response_valid_signed.errors, generate_audience_error(response_valid_signed.settings.
|
422
|
+
assert_includes response_valid_signed.errors, generate_audience_error(response_valid_signed.settings.sp_entity_id, ['https://someone.example.com/audience'])
|
423
423
|
end
|
424
424
|
|
425
425
|
it "return false when no ID present in the SAML Response" do
|
@@ -451,7 +451,7 @@ class RubySamlTest < Minitest::Test
|
|
451
451
|
|
452
452
|
it "collect errors when collect_errors=true" do
|
453
453
|
settings.idp_cert = ruby_saml_cert_text
|
454
|
-
settings.
|
454
|
+
settings.sp_entity_id = 'invalid'
|
455
455
|
response_invalid_subjectconfirmation_recipient.settings = settings
|
456
456
|
collect_errors = true
|
457
457
|
response_invalid_subjectconfirmation_recipient.is_valid?(collect_errors)
|
@@ -464,23 +464,23 @@ class RubySamlTest < Minitest::Test
|
|
464
464
|
describe "#validate_audience" do
|
465
465
|
it "return true when the audience is valid" do
|
466
466
|
response.settings = settings
|
467
|
-
response.settings.
|
467
|
+
response.settings.sp_entity_id = '{audience}'
|
468
468
|
assert response.send(:validate_audience)
|
469
469
|
assert_empty response.errors
|
470
470
|
end
|
471
471
|
|
472
472
|
it "return true when the audience is self closing" do
|
473
473
|
response_audience_self_closed.settings = settings
|
474
|
-
response_audience_self_closed.settings.
|
474
|
+
response_audience_self_closed.settings.sp_entity_id = '{audience}'
|
475
475
|
assert response_audience_self_closed.send(:validate_audience)
|
476
476
|
assert_empty response_audience_self_closed.errors
|
477
477
|
end
|
478
478
|
|
479
479
|
it "return false when the audience is valid" do
|
480
480
|
response.settings = settings
|
481
|
-
response.settings.
|
481
|
+
response.settings.sp_entity_id = 'invalid_audience'
|
482
482
|
assert !response.send(:validate_audience)
|
483
|
-
assert_includes response.errors, generate_audience_error(response.settings.
|
483
|
+
assert_includes response.errors, generate_audience_error(response.settings.sp_entity_id, ['{audience}'])
|
484
484
|
end
|
485
485
|
end
|
486
486
|
|
@@ -665,23 +665,23 @@ class RubySamlTest < Minitest::Test
|
|
665
665
|
describe "#validate_audience" do
|
666
666
|
it "return true when the audience is valid" do
|
667
667
|
response_valid_signed.settings = settings
|
668
|
-
response_valid_signed.settings.
|
668
|
+
response_valid_signed.settings.sp_entity_id = "https://someone.example.com/audience"
|
669
669
|
assert response_valid_signed.send(:validate_audience)
|
670
670
|
assert_empty response_valid_signed.errors
|
671
671
|
end
|
672
672
|
|
673
|
-
it "return true when there is not
|
673
|
+
it "return true when there is not sp_entity_id defined" do
|
674
674
|
response_valid_signed.settings = settings
|
675
|
-
response_valid_signed.settings.
|
675
|
+
response_valid_signed.settings.sp_entity_id = nil
|
676
676
|
assert response_valid_signed.send(:validate_audience)
|
677
677
|
assert_empty response_valid_signed.errors
|
678
678
|
end
|
679
679
|
|
680
680
|
it "return false when there is no valid audience" do
|
681
681
|
response_invalid_audience.settings = settings
|
682
|
-
response_invalid_audience.settings.
|
682
|
+
response_invalid_audience.settings.sp_entity_id = "https://invalid.example.com/audience"
|
683
683
|
assert !response_invalid_audience.send(:validate_audience)
|
684
|
-
assert_includes response_invalid_audience.errors, generate_audience_error(response_invalid_audience.settings.
|
684
|
+
assert_includes response_invalid_audience.errors, generate_audience_error(response_invalid_audience.settings.sp_entity_id, ['http://invalid.audience.com'])
|
685
685
|
end
|
686
686
|
end
|
687
687
|
|
@@ -880,6 +880,15 @@ class RubySamlTest < Minitest::Test
|
|
880
880
|
assert_includes response_valid_signed_without_x509certificate.errors, "Invalid Signature on SAML Response"
|
881
881
|
end
|
882
882
|
|
883
|
+
it "return false when cert expired and check_idp_cert_expiration enabled" do
|
884
|
+
settings.idp_cert_fingerprint = nil
|
885
|
+
settings.idp_cert = ruby_saml_cert_text
|
886
|
+
settings.security[:check_idp_cert_expiration] = true
|
887
|
+
response_valid_signed.settings = settings
|
888
|
+
assert !response_valid_signed.send(:validate_signature)
|
889
|
+
assert_includes response_valid_signed.errors, "IdP x509 certificate expired"
|
890
|
+
end
|
891
|
+
|
883
892
|
it "return false when no X509Certificate and the cert provided at settings mismatches" do
|
884
893
|
settings.idp_cert_fingerprint = nil
|
885
894
|
settings.idp_cert = signature_1
|
@@ -953,7 +962,7 @@ class RubySamlTest < Minitest::Test
|
|
953
962
|
end
|
954
963
|
|
955
964
|
it "return false when wrong_spnamequalifier" do
|
956
|
-
settings.
|
965
|
+
settings.sp_entity_id = 'sp_entity_id'
|
957
966
|
response_wrong_spnamequalifier.settings = settings
|
958
967
|
assert !response_wrong_spnamequalifier.send(:validate_name_id)
|
959
968
|
assert_includes response_wrong_spnamequalifier.errors, "The SPNameQualifier value mistmatch the SP entityID value."
|
@@ -966,7 +975,7 @@ class RubySamlTest < Minitest::Test
|
|
966
975
|
end
|
967
976
|
|
968
977
|
it "return true when nameid is valid and response_wrong_spnamequalifier matches the SP issuer" do
|
969
|
-
settings.
|
978
|
+
settings.sp_entity_id = 'wrong-sp-entityid'
|
970
979
|
response_wrong_spnamequalifier.settings = settings
|
971
980
|
assert response_wrong_spnamequalifier.send(:validate_name_id)
|
972
981
|
end
|
@@ -1398,7 +1407,7 @@ class RubySamlTest < Minitest::Test
|
|
1398
1407
|
|
1399
1408
|
before do
|
1400
1409
|
settings.idp_cert_fingerprint = 'EE:17:4E:FB:A8:81:71:12:0D:2A:78:43:BC:E7:0C:07:58:79:F4:F4'
|
1401
|
-
settings.
|
1410
|
+
settings.sp_entity_id = 'http://rubysaml.com:3000/saml/metadata'
|
1402
1411
|
settings.assertion_consumer_service_url = 'http://rubysaml.com:3000/saml/acs'
|
1403
1412
|
settings.certificate = ruby_saml_cert_text
|
1404
1413
|
settings.private_key = ruby_saml_key_text
|
data/test/settings_test.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
2
2
|
|
3
3
|
require 'onelogin/ruby-saml/settings'
|
4
|
+
require 'onelogin/ruby-saml/validation_error'
|
4
5
|
|
5
6
|
class SettingsTest < Minitest::Test
|
6
7
|
|
@@ -243,6 +244,13 @@ class SettingsTest < Minitest::Test
|
|
243
244
|
}
|
244
245
|
end
|
245
246
|
|
247
|
+
it "raises an error if SP certificate expired and check_sp_cert_expiration enabled" do
|
248
|
+
@settings.certificate = ruby_saml_cert_text
|
249
|
+
@settings.security[:check_sp_cert_expiration] = true
|
250
|
+
assert_raises(OneLogin::RubySaml::ValidationError) {
|
251
|
+
settings.get_sp_cert
|
252
|
+
}
|
253
|
+
end
|
246
254
|
end
|
247
255
|
|
248
256
|
describe "#get_sp_cert_new" do
|
@@ -429,6 +429,23 @@ class RubySamlTest < Minitest::Test
|
|
429
429
|
assert logout_request_sign_test.send(:validate_signature)
|
430
430
|
end
|
431
431
|
|
432
|
+
it "return false when cert expired and check_idp_cert_expiration expired" do
|
433
|
+
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
|
434
|
+
params['RelayState'] = params[:RelayState]
|
435
|
+
options = {}
|
436
|
+
options[:get_params] = params
|
437
|
+
settings.security[:check_idp_cert_expiration] = true
|
438
|
+
logout_request_sign_test = OneLogin::RubySaml::SloLogoutrequest.new(params['SAMLRequest'], options)
|
439
|
+
settings.idp_cert = nil
|
440
|
+
settings.idp_cert_multi = {
|
441
|
+
:signing => [ruby_saml_cert_text],
|
442
|
+
:encryption => []
|
443
|
+
}
|
444
|
+
logout_request_sign_test.settings = settings
|
445
|
+
assert !logout_request_sign_test.send(:validate_signature)
|
446
|
+
assert_includes logout_request_sign_test.errors, "IdP x509 certificate expired"
|
447
|
+
end
|
448
|
+
|
432
449
|
it "return false when none cert on idp_cert_multi is valid" do
|
433
450
|
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
|
434
451
|
params['RelayState'] = params[:RelayState]
|
@@ -442,6 +459,8 @@ class RubySamlTest < Minitest::Test
|
|
442
459
|
}
|
443
460
|
logout_request_sign_test.settings = settings
|
444
461
|
assert !logout_request_sign_test.send(:validate_signature)
|
462
|
+
assert_includes logout_request_sign_test.errors, "Invalid Signature on Logout Request"
|
463
|
+
|
445
464
|
end
|
446
465
|
end
|
447
466
|
end
|
data/test/test_helper.rb
CHANGED
data/test/xml_security_test.rb
CHANGED
@@ -239,7 +239,7 @@ class XmlSecurityTest < Minitest::Test
|
|
239
239
|
settings.idp_sso_target_url = "https://idp.example.com/sso"
|
240
240
|
settings.protocol_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
|
241
241
|
settings.idp_slo_target_url = "https://idp.example.com/slo",
|
242
|
-
settings.
|
242
|
+
settings.sp_entity_id = "https://sp.example.com/saml2"
|
243
243
|
settings.assertion_consumer_service_url = "https://sp.example.com/acs"
|
244
244
|
settings.single_logout_service_url = "https://sp.example.com/sls"
|
245
245
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-saml
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- OneLogin LLC
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-07-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 1.5.10
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: coveralls
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: minitest
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -344,8 +358,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
344
358
|
- !ruby/object:Gem::Version
|
345
359
|
version: '0'
|
346
360
|
requirements: []
|
347
|
-
rubyforge_project:
|
348
|
-
rubygems_version: 2.
|
361
|
+
rubyforge_project:
|
362
|
+
rubygems_version: 2.5.2.1
|
349
363
|
signing_key:
|
350
364
|
specification_version: 4
|
351
365
|
summary: SAML Ruby Tookit
|