ruby-saml 1.11.0 → 1.12.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of ruby-saml might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/.travis.yml +14 -12
- data/README.md +67 -19
- data/changelog.md +23 -0
- data/lib/onelogin/ruby-saml/attributes.rb +24 -1
- data/lib/onelogin/ruby-saml/authrequest.rb +9 -4
- data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +62 -24
- data/lib/onelogin/ruby-saml/logoutrequest.rb +7 -1
- data/lib/onelogin/ruby-saml/logoutresponse.rb +4 -0
- data/lib/onelogin/ruby-saml/metadata.rb +9 -1
- data/lib/onelogin/ruby-saml/response.rb +37 -15
- data/lib/onelogin/ruby-saml/saml_message.rb +6 -0
- data/lib/onelogin/ruby-saml/setting_error.rb +6 -0
- data/lib/onelogin/ruby-saml/settings.rb +34 -2
- data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +4 -0
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +27 -14
- data/lib/onelogin/ruby-saml/utils.rb +56 -0
- data/lib/onelogin/ruby-saml/version.rb +1 -1
- data/lib/xml_security.rb +34 -6
- data/ruby-saml.gemspec +8 -4
- metadata +22 -282
- data/test/certificates/certificate.der +0 -0
- data/test/certificates/certificate1 +0 -12
- data/test/certificates/certificate_without_head_foot +0 -1
- 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/certificates/ruby-saml.crt +0 -14
- data/test/certificates/ruby-saml.key +0 -15
- data/test/idp_metadata_parser_test.rb +0 -594
- data/test/logging_test.rb +0 -62
- data/test/logout_requests/invalid_slo_request.xml +0 -6
- data/test/logout_requests/slo_request.xml +0 -4
- data/test/logout_requests/slo_request.xml.base64 +0 -1
- data/test/logout_requests/slo_request_deflated.xml.base64 +0 -1
- data/test/logout_requests/slo_request_with_name_id_format.xml +0 -4
- data/test/logout_requests/slo_request_with_session_index.xml +0 -5
- data/test/logout_responses/logoutresponse_fixtures.rb +0 -86
- data/test/logoutrequest_test.rb +0 -260
- data/test/logoutresponse_test.rb +0 -427
- data/test/metadata/idp_descriptor.xml +0 -26
- data/test/metadata/idp_descriptor_2.xml +0 -56
- data/test/metadata/idp_descriptor_3.xml +0 -14
- data/test/metadata/idp_descriptor_4.xml +0 -72
- data/test/metadata/idp_metadata_different_sign_and_encrypt_cert.xml +0 -72
- data/test/metadata/idp_metadata_multi_certs.xml +0 -75
- data/test/metadata/idp_metadata_multi_signing_certs.xml +0 -52
- data/test/metadata/idp_metadata_same_sign_and_encrypt_cert.xml +0 -71
- data/test/metadata/idp_multiple_descriptors.xml +0 -59
- data/test/metadata/idp_multiple_descriptors_2.xml +0 -59
- data/test/metadata/no_idp_descriptor.xml +0 -21
- data/test/metadata_test.rb +0 -331
- data/test/request_test.rb +0 -340
- data/test/response_test.rb +0 -1629
- data/test/responses/adfs_response_sha1.xml +0 -46
- data/test/responses/adfs_response_sha256.xml +0 -46
- data/test/responses/adfs_response_sha384.xml +0 -46
- data/test/responses/adfs_response_sha512.xml +0 -46
- data/test/responses/adfs_response_xmlns.xml +0 -45
- data/test/responses/attackxee.xml +0 -13
- data/test/responses/invalids/duplicated_attributes.xml.base64 +0 -1
- data/test/responses/invalids/empty_destination.xml.base64 +0 -1
- data/test/responses/invalids/empty_nameid.xml.base64 +0 -1
- data/test/responses/invalids/encrypted_new_attack.xml.base64 +0 -1
- data/test/responses/invalids/invalid_audience.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/invalid_signature_position.xml.base64 +0 -1
- data/test/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 +0 -1
- data/test/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 +0 -1
- data/test/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 +0 -1
- data/test/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 +0 -1
- data/test/responses/invalids/multiple_assertions.xml.base64 +0 -2
- data/test/responses/invalids/multiple_signed.xml.base64 +0 -1
- data/test/responses/invalids/no_authnstatement.xml.base64 +0 -1
- data/test/responses/invalids/no_conditions.xml.base64 +0 -1
- data/test/responses/invalids/no_id.xml.base64 +0 -1
- data/test/responses/invalids/no_issuer_assertion.xml.base64 +0 -1
- data/test/responses/invalids/no_issuer_response.xml.base64 +0 -1
- data/test/responses/invalids/no_nameid.xml.base64 +0 -1
- data/test/responses/invalids/no_saml2.xml.base64 +0 -1
- data/test/responses/invalids/no_signature.xml.base64 +0 -1
- data/test/responses/invalids/no_status.xml.base64 +0 -1
- data/test/responses/invalids/no_status_code.xml.base64 +0 -1
- data/test/responses/invalids/no_subjectconfirmation_data.xml.base64 +0 -1
- data/test/responses/invalids/no_subjectconfirmation_method.xml.base64 +0 -1
- data/test/responses/invalids/response_invalid_signed_element.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/invalids/status_code_responder.xml.base64 +0 -1
- data/test/responses/invalids/status_code_responer_and_msg.xml.base64 +0 -1
- data/test/responses/invalids/wrong_spnamequalifier.xml.base64 +0 -1
- data/test/responses/no_signature_ns.xml +0 -48
- data/test/responses/open_saml_response.xml +0 -56
- data/test/responses/response_assertion_wrapped.xml.base64 +0 -93
- data/test/responses/response_audience_self_closed_tag.xml.base64 +0 -1
- data/test/responses/response_double_status_code.xml.base64 +0 -1
- data/test/responses/response_encrypted_attrs.xml.base64 +0 -1
- data/test/responses/response_encrypted_nameid.xml.base64 +0 -1
- data/test/responses/response_eval.xml +0 -7
- data/test/responses/response_no_cert_and_encrypted_attrs.xml +0 -29
- data/test/responses/response_node_text_attack.xml.base64 +0 -1
- data/test/responses/response_node_text_attack2.xml.base64 +0 -1
- data/test/responses/response_node_text_attack3.xml.base64 +0 -1
- data/test/responses/response_unsigned_xml_base64 +0 -1
- data/test/responses/response_with_ampersands.xml +0 -139
- data/test/responses/response_with_ampersands.xml.base64 +0 -93
- data/test/responses/response_with_ds_namespace_at_the_root.xml.base64 +0 -1
- data/test/responses/response_with_multiple_attribute_statements.xml +0 -72
- data/test/responses/response_with_multiple_attribute_values.xml +0 -67
- data/test/responses/response_with_retrieval_method.xml +0 -26
- data/test/responses/response_with_saml2_namespace.xml.base64 +0 -102
- data/test/responses/response_with_signed_assertion.xml.base64 +0 -66
- data/test/responses/response_with_signed_assertion_2.xml.base64 +0 -1
- 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_without_attributes.xml.base64 +0 -79
- data/test/responses/response_without_reference_uri.xml.base64 +0 -1
- data/test/responses/response_wrapped.xml.base64 +0 -150
- data/test/responses/signed_message_encrypted_signed_assertion.xml.base64 +0 -1
- data/test/responses/signed_message_encrypted_unsigned_assertion.xml.base64 +0 -1
- data/test/responses/signed_nameid_in_atts.xml +0 -47
- data/test/responses/signed_unqual_nameid_in_atts.xml +0 -47
- data/test/responses/simple_saml_php.xml +0 -71
- data/test/responses/starfield_response.xml.base64 +0 -1
- data/test/responses/test_sign.xml +0 -43
- data/test/responses/unsigned_encrypted_adfs.xml +0 -23
- data/test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64 +0 -1
- data/test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64 +0 -1
- data/test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64 +0 -1
- data/test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64 +0 -1
- data/test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64 +0 -1
- data/test/responses/unsigned_message_encrypted_signed_assertion.xml.base64 +0 -1
- data/test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64 +0 -1
- data/test/responses/valid_response.xml.base64 +0 -1
- data/test/responses/valid_response_with_formatted_x509certificate.xml.base64 +0 -1
- data/test/responses/valid_response_without_x509certificate.xml.base64 +0 -1
- data/test/saml_message_test.rb +0 -56
- data/test/settings_test.rb +0 -338
- data/test/slo_logoutrequest_test.rb +0 -467
- data/test/slo_logoutresponse_test.rb +0 -233
- data/test/test_helper.rb +0 -333
- data/test/utils_test.rb +0 -259
- data/test/xml_security_test.rb +0 -421
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 43bc92cf8a14835577d9bb32d1bdcef71fd5ffccb351dd41ac9b56863fb173c7
|
4
|
+
data.tar.gz: e7975fcf413d9c64801f7b5190246685548205034dc74315bc169738697e1006
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a09fcb8777969eb6d54b29d20520c7e17e3f7dc128cfc81475eba8c9b31f5926f2b64d308b18268762b43fb60cf7956f6e266c1f5343d2b9d58e545be0c3392
|
7
|
+
data.tar.gz: 8e9008647a610935764b2f578b76c5eb72d09be482d76b5f4d8ab51fd29d4569a3ba2e2db61f63fbdde27bd1be14bf4afdeffe1c7f699afc0b7d5e1c85d0fe09
|
data/.travis.yml
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
- 1.8.7
|
4
3
|
- 1.9.3
|
5
4
|
- 2.0.0
|
6
5
|
- 2.1.10
|
7
6
|
- 2.2.10
|
8
7
|
- 2.3.8
|
9
8
|
- 2.4.6
|
10
|
-
- 2.5.
|
11
|
-
- 2.6.
|
12
|
-
-
|
9
|
+
- 2.5.8
|
10
|
+
- 2.6.6
|
11
|
+
- 2.7.2
|
12
|
+
- 3.0.0
|
13
13
|
- jruby-1.7.27
|
14
14
|
- jruby-9.1.17.0
|
15
|
-
- jruby-9.2.
|
15
|
+
- jruby-9.2.13.0
|
16
16
|
gemfile:
|
17
17
|
- Gemfile
|
18
18
|
- gemfiles/nokogiri-1.5.gemfile
|
@@ -20,27 +20,29 @@ before_install:
|
|
20
20
|
- gem update bundler
|
21
21
|
matrix:
|
22
22
|
exclude:
|
23
|
-
- rvm: 1.8.7
|
24
|
-
gemfile: Gemfile
|
25
|
-
- rvm: ree
|
26
|
-
gemfile: Gemfile
|
27
23
|
- rvm: jruby-1.7.27
|
28
24
|
gemfile: gemfiles/nokogiri-1.5.gemfile
|
29
25
|
- rvm: jruby-9.1.17.0
|
30
26
|
gemfile: gemfiles/nokogiri-1.5.gemfile
|
31
|
-
- rvm: jruby-9.2.
|
27
|
+
- rvm: jruby-9.2.13.0
|
32
28
|
gemfile: gemfiles/nokogiri-1.5.gemfile
|
33
29
|
- rvm: 2.1.5
|
34
30
|
gemfile: gemfiles/nokogiri-1.5.gemfile
|
31
|
+
- rvm: 2.1.10
|
32
|
+
gemfile: gemfiles/nokogiri-1.5.gemfile
|
35
33
|
- rvm: 2.2.10
|
36
34
|
gemfile: gemfiles/nokogiri-1.5.gemfile
|
37
35
|
- rvm: 2.3.8
|
38
36
|
gemfile: gemfiles/nokogiri-1.5.gemfile
|
39
37
|
- rvm: 2.4.6
|
40
38
|
gemfile: gemfiles/nokogiri-1.5.gemfile
|
41
|
-
- rvm: 2.5.
|
39
|
+
- rvm: 2.5.8
|
40
|
+
gemfile: gemfiles/nokogiri-1.5.gemfile
|
41
|
+
- rvm: 2.6.6
|
42
|
+
gemfile: gemfiles/nokogiri-1.5.gemfile
|
43
|
+
- rvm: 2.7.2
|
42
44
|
gemfile: gemfiles/nokogiri-1.5.gemfile
|
43
|
-
- rvm:
|
45
|
+
- rvm: 3.0.0
|
44
46
|
gemfile: gemfiles/nokogiri-1.5.gemfile
|
45
47
|
env:
|
46
48
|
- JRUBY_OPTS="--debug"
|
data/README.md
CHANGED
@@ -1,6 +1,18 @@
|
|
1
|
-
# Ruby SAML [![Build Status](https://secure.travis-ci.org/onelogin/ruby-saml.svg)](http://travis-ci.org/onelogin/ruby-saml) [![Coverage Status](https://coveralls.io/repos/onelogin/ruby-saml/badge.svg?branch=master
|
1
|
+
# Ruby SAML [![Build Status](https://secure.travis-ci.org/onelogin/ruby-saml.svg)](http://travis-ci.org/onelogin/ruby-saml) [![Coverage Status](https://coveralls.io/repos/onelogin/ruby-saml/badge.svg?branch=master)](https://coveralls.io/r/onelogin/ruby-saml?branch=master)
|
2
2
|
|
3
|
-
|
3
|
+
## Updating from 1.11.x to 1.12.0
|
4
|
+
Version `1.12.0` adds support for gcm algorithm and
|
5
|
+
change/adds specific error messages for signature validations
|
6
|
+
|
7
|
+
## Updating from 1.10.x to 1.11.0
|
8
|
+
Version `1.11.0` deprecates the use of `settings.issuer` in favour of `settings.sp_entity_id`.
|
9
|
+
There are two new security settings: `settings.security[:check_idp_cert_expiration]` and `settings.security[:check_sp_cert_expiration]` (both false by default) that check if the IdP or SP X.509 certificate has expired, respectively.
|
10
|
+
|
11
|
+
Version `1.10.2` includes the `valid_until` attribute in parsed IdP metadata.
|
12
|
+
|
13
|
+
Version `1.10.1` improves Ruby 1.8.7 support.
|
14
|
+
|
15
|
+
## Updating from 1.9.0 to 1.10.0
|
4
16
|
Version `1.10.0` improves IdpMetadataParser to allow parse multiple IDPSSODescriptor, Add Subject support on AuthNRequest to allow SPs provide info to the IdP about the user to be authenticated and updates the format_cert method to accept certs with /\x0d/
|
5
17
|
|
6
18
|
## Updating from 1.8.0 to 1.9.0
|
@@ -107,6 +119,7 @@ We created a demo project for Rails4 that uses the latest version of this librar
|
|
107
119
|
* 2.4.x
|
108
120
|
* 2.5.x
|
109
121
|
* 2.6.x
|
122
|
+
* 2.7.x
|
110
123
|
* JRuby 1.7.19
|
111
124
|
* JRuby 9.0.0.0
|
112
125
|
* JRuby 9.2.0.0
|
@@ -141,7 +154,7 @@ Using `Gemfile`
|
|
141
154
|
|
142
155
|
```ruby
|
143
156
|
# latest stable
|
144
|
-
gem 'ruby-saml', '~> 1.
|
157
|
+
gem 'ruby-saml', '~> 1.11.0'
|
145
158
|
|
146
159
|
# or track master for bleeding-edge
|
147
160
|
gem 'ruby-saml', :github => 'onelogin/ruby-saml'
|
@@ -228,6 +241,7 @@ def consume
|
|
228
241
|
session[:attributes] = response.attributes
|
229
242
|
else
|
230
243
|
authorize_failure # This method shows an error message
|
244
|
+
# List of errors is available in response.errors array
|
231
245
|
end
|
232
246
|
end
|
233
247
|
```
|
@@ -251,8 +265,8 @@ def saml_settings
|
|
251
265
|
settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
|
252
266
|
settings.sp_entity_id = "http://#{request.host}/saml/metadata"
|
253
267
|
settings.idp_entity_id = "https://app.onelogin.com/saml/metadata/#{OneLoginAppId}"
|
254
|
-
settings.
|
255
|
-
settings.
|
268
|
+
settings.idp_sso_service_url = "https://app.onelogin.com/trust/saml2/http-post/sso/#{OneLoginAppId}"
|
269
|
+
settings.idp_slo_service_url = "https://app.onelogin.com/trust/saml2/http-redirect/slo/#{OneLoginAppId}"
|
256
270
|
settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
|
257
271
|
settings.idp_cert_fingerprint_algorithm = "http://www.w3.org/2000/09/xmldsig#sha1"
|
258
272
|
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
@@ -273,7 +287,7 @@ def saml_settings
|
|
273
287
|
end
|
274
288
|
```
|
275
289
|
|
276
|
-
The use of settings.issuer is deprecated in favour of settings.sp_entity_id
|
290
|
+
The use of settings.issuer is deprecated in favour of settings.sp_entity_id since version 1.11.0
|
277
291
|
|
278
292
|
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:
|
279
293
|
|
@@ -282,6 +296,7 @@ response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_authnst
|
|
282
296
|
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_conditions: true}) # skips conditions
|
283
297
|
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_subject_confirmation: true}) # skips subject confirmation
|
284
298
|
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_recipient_check: true}) # doens't skip subject confirmation, but skips the recipient check which is a sub check of the subject_confirmation check
|
299
|
+
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_audience: true}) # skips audience check
|
285
300
|
```
|
286
301
|
|
287
302
|
All that's left is to wrap everything in a controller and reference it in the initialization and consumption URLs in OneLogin. A full controller example could look like this:
|
@@ -305,6 +320,7 @@ class SamlController < ApplicationController
|
|
305
320
|
session[:attributes] = response.attributes
|
306
321
|
else
|
307
322
|
authorize_failure # This method shows an error message
|
323
|
+
# List of errors is available in response.errors array
|
308
324
|
end
|
309
325
|
end
|
310
326
|
|
@@ -315,7 +331,7 @@ class SamlController < ApplicationController
|
|
315
331
|
|
316
332
|
settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
|
317
333
|
settings.sp_entity_id = "http://#{request.host}/saml/metadata"
|
318
|
-
settings.
|
334
|
+
settings.idp_sso_service_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}"
|
319
335
|
settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
|
320
336
|
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
321
337
|
|
@@ -388,8 +404,8 @@ end
|
|
388
404
|
The following attributes are set:
|
389
405
|
* idp_entity_id
|
390
406
|
* name_identifier_format
|
391
|
-
*
|
392
|
-
*
|
407
|
+
* idp_sso_service_url
|
408
|
+
* idp_slo_service_url
|
393
409
|
* idp_attribute_names
|
394
410
|
* idp_cert
|
395
411
|
* idp_cert_fingerprint
|
@@ -455,6 +471,9 @@ Imagine this `saml:AttributeStatement`
|
|
455
471
|
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
|
456
472
|
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="1"/>
|
457
473
|
</saml:Attribute>
|
474
|
+
<saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname">
|
475
|
+
<saml:AttributeValue>usersName</saml:AttributeValue>
|
476
|
+
</saml:Attribute>
|
458
477
|
</saml:AttributeStatement>
|
459
478
|
```
|
460
479
|
|
@@ -465,7 +484,8 @@ pp(response.attributes) # is an OneLogin::RubySaml::Attributes object
|
|
465
484
|
"another_value"=>["value1", "value2"],
|
466
485
|
"role"=>["role1", "role2", "role3"],
|
467
486
|
"attribute_with_nil_value"=>[nil],
|
468
|
-
"attribute_with_nils_and_empty_strings"=>["", "valuePresent", nil, nil]
|
487
|
+
"attribute_with_nils_and_empty_strings"=>["", "valuePresent", nil, nil]
|
488
|
+
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"=>["usersName"]}>
|
469
489
|
|
470
490
|
# Active single_value_compatibility
|
471
491
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
@@ -482,6 +502,9 @@ pp(response.attributes.single(:role))
|
|
482
502
|
pp(response.attributes.multi(:role))
|
483
503
|
# => ["role1", "role2", "role3"]
|
484
504
|
|
505
|
+
pp(response.attributes.fetch(:role))
|
506
|
+
# => "role1"
|
507
|
+
|
485
508
|
pp(response.attributes[:attribute_with_nil_value])
|
486
509
|
# => nil
|
487
510
|
|
@@ -497,6 +520,9 @@ pp(response.attributes.single(:not_exists))
|
|
497
520
|
pp(response.attributes.multi(:not_exists))
|
498
521
|
# => nil
|
499
522
|
|
523
|
+
pp(response.attributes.fetch(/givenname/))
|
524
|
+
# => "usersName"
|
525
|
+
|
500
526
|
# Deactive single_value_compatibility
|
501
527
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
502
528
|
|
@@ -512,6 +538,9 @@ pp(response.attributes.single(:role))
|
|
512
538
|
pp(response.attributes.multi(:role))
|
513
539
|
# => ["role1", "role2", "role3"]
|
514
540
|
|
541
|
+
pp(response.attributes.fetch(:role))
|
542
|
+
# => ["role1", "role2", "role3"]
|
543
|
+
|
515
544
|
pp(response.attributes[:attribute_with_nil_value])
|
516
545
|
# => [nil]
|
517
546
|
|
@@ -526,12 +555,15 @@ pp(response.attributes.single(:not_exists))
|
|
526
555
|
|
527
556
|
pp(response.attributes.multi(:not_exists))
|
528
557
|
# => nil
|
558
|
+
|
559
|
+
pp(response.attributes.fetch(/givenname/))
|
560
|
+
# => ["usersName"]
|
529
561
|
```
|
530
562
|
|
531
563
|
The `saml:AuthnContextClassRef` of the AuthNRequest can be provided by `settings.authn_context`; possible values are described at [SAMLAuthnCxt]. The comparison method can be set using `settings.authn_context_comparison` parameter. Possible values include: 'exact', 'better', 'maximum' and 'minimum' (default value is 'exact').
|
532
564
|
To add a `saml:AuthnContextDeclRef`, define `settings.authn_context_decl_ref`.
|
533
565
|
|
534
|
-
In a SP-
|
566
|
+
In a SP-initiated flow, the SP can indicate to the IdP the subject that should be authenticated. This is done by defining the `settings.name_identifier_value_requested` before
|
535
567
|
building the authrequest object.
|
536
568
|
|
537
569
|
|
@@ -570,7 +602,7 @@ Remember to provide it to the Signature builder if you are sending a `GET RelayS
|
|
570
602
|
signature validation process will fail at the Identity Provider.
|
571
603
|
|
572
604
|
The Service Provider will sign the request/responses with its private key.
|
573
|
-
The Identity Provider will validate the sign of the received request/responses with the public
|
605
|
+
The Identity Provider will validate the sign of the received request/responses with the public x509 cert of the
|
574
606
|
Service Provider.
|
575
607
|
|
576
608
|
Notice that this toolkit uses 'settings.certificate' and 'settings.private_key' for the sign and decrypt processes.
|
@@ -611,21 +643,27 @@ def sp_logout_request
|
|
611
643
|
# LogoutRequest accepts plain browser requests w/o paramters
|
612
644
|
settings = saml_settings
|
613
645
|
|
614
|
-
if settings.
|
646
|
+
if settings.idp_slo_service_url.nil?
|
615
647
|
logger.info "SLO IdP Endpoint not found in settings, executing then a normal logout'"
|
616
648
|
delete_session
|
617
649
|
else
|
618
650
|
|
619
|
-
# Since we created a new SAML request, save the transaction_id
|
620
|
-
# to compare it with the response we get back
|
621
651
|
logout_request = OneLogin::RubySaml::Logoutrequest.new()
|
622
|
-
session[:
|
623
|
-
logger.info "New SP SLO for userid '#{session[:userid]}' transactionid '#{session[:transaction_id]}'"
|
652
|
+
logger.info "New SP SLO for userid '#{session[:userid]}' transactionid '#{logout_request.uuid}'"
|
624
653
|
|
625
654
|
if settings.name_identifier_value.nil?
|
626
655
|
settings.name_identifier_value = session[:userid]
|
627
656
|
end
|
628
657
|
|
658
|
+
# Ensure user is logged out before redirect to IdP, in case anything goes wrong during single logout process (as recommended by saml2int [SDP-SP34])
|
659
|
+
logged_user = session[:userid]
|
660
|
+
logger.info "Delete session for '#{session[:userid]}'"
|
661
|
+
delete_session
|
662
|
+
|
663
|
+
# Save the transaction_id to compare it with the response we get back
|
664
|
+
session[:transaction_id] = logout_request.uuid
|
665
|
+
session[:logged_out_user] = logged_user
|
666
|
+
|
629
667
|
relayState = url_for controller: 'saml', action: 'index'
|
630
668
|
redirect_to(logout_request.create(settings, :RelayState => relayState))
|
631
669
|
end
|
@@ -653,7 +691,7 @@ def process_logout_response
|
|
653
691
|
logger.error "The SAML Logout Response is invalid"
|
654
692
|
else
|
655
693
|
# Actually log out this session
|
656
|
-
logger.info "
|
694
|
+
logger.info "SLO completed for '#{session[:logged_out_user]}'"
|
657
695
|
delete_session
|
658
696
|
end
|
659
697
|
end
|
@@ -662,6 +700,8 @@ end
|
|
662
700
|
def delete_session
|
663
701
|
session[:userid] = nil
|
664
702
|
session[:attributes] = nil
|
703
|
+
session[:transaction_id] = nil
|
704
|
+
session[:logged_out_user] = nil
|
665
705
|
end
|
666
706
|
```
|
667
707
|
|
@@ -674,7 +714,7 @@ def idp_logout_request
|
|
674
714
|
logout_request = OneLogin::RubySaml::SloLogoutrequest.new(params[:SAMLRequest])
|
675
715
|
if !logout_request.is_valid?
|
676
716
|
logger.error "IdP initiated LogoutRequest was not valid!"
|
677
|
-
render :inline => logger.error
|
717
|
+
return render :inline => logger.error
|
678
718
|
end
|
679
719
|
logger.info "IdP initiated Logout for #{logout_request.name_id}"
|
680
720
|
|
@@ -729,6 +769,14 @@ class SamlController < ApplicationController
|
|
729
769
|
end
|
730
770
|
```
|
731
771
|
|
772
|
+
You can add ValidUntil and CacheDuration to the XML Metadata using instead
|
773
|
+
```ruby
|
774
|
+
# Valid until => 2 days from now
|
775
|
+
# Cache duration = 604800s = 1 week
|
776
|
+
valid_until = Time.now + 172800
|
777
|
+
cache_duration = 604800
|
778
|
+
meta.generate(settings, false, valid_until, cache_duration)
|
779
|
+
```
|
732
780
|
|
733
781
|
## Clock Drift
|
734
782
|
|
data/changelog.md
CHANGED
@@ -1,5 +1,28 @@
|
|
1
1
|
# RubySaml Changelog
|
2
2
|
|
3
|
+
### 1.12.0 (Feb 18, 2021)
|
4
|
+
* Support AES-128-GCM, AES-192-GCM, and AES-256-GCM encryptions
|
5
|
+
* Parse & return SLO ResponseLocation in IDPMetadataParser & Settings
|
6
|
+
* Adding idp_sso_service_url and idp_slo_service_url settings
|
7
|
+
* [#536](https://github.com/onelogin/ruby-saml/pull/536) Adding feth method to be able retrieve attributes based on regex
|
8
|
+
* Reduce size of built gem by excluding the test folder
|
9
|
+
* Improve protection on Zlib deflate decompression bomb attack.
|
10
|
+
* Add ValidUntil and cacheDuration support on Metadata generator
|
11
|
+
* Add support for cacheDuration at the IdpMetadataParser
|
12
|
+
* Support customizable statusCode on generated LogoutResponse
|
13
|
+
* [#545](https://github.com/onelogin/ruby-saml/pull/545) More specific error messages for signature validation
|
14
|
+
* Support Process Transform
|
15
|
+
* Raise SettingError if invoking an action with no endpoint defined on the settings
|
16
|
+
* Made IdpMetadataParser more extensible for subclasses
|
17
|
+
*[#548](https://github.com/onelogin/ruby-saml/pull/548) Add :skip_audience option
|
18
|
+
* [#555](https://github.com/onelogin/ruby-saml/pull/555) Define 'soft' variable to prevent exception when doc cert is invalid
|
19
|
+
* Improve documentation
|
20
|
+
|
21
|
+
### 1.11.0 (Jul 24, 2019)
|
22
|
+
|
23
|
+
* Deprecate settings.issuer in favor of settings.sp_entity_id
|
24
|
+
* Add support for certification expiration
|
25
|
+
|
3
26
|
### 1.10.2 (Apr 29, 2019)
|
4
27
|
|
5
28
|
* Add valid until, accessor
|
@@ -79,7 +79,7 @@ module OneLogin
|
|
79
79
|
self.class.single_value_compatibility ? single(canonize_name(name)) : multi(canonize_name(name))
|
80
80
|
end
|
81
81
|
|
82
|
-
# @return [
|
82
|
+
# @return [Hash] Return all attributes as a hash
|
83
83
|
#
|
84
84
|
def all
|
85
85
|
attributes
|
@@ -113,6 +113,29 @@ module OneLogin
|
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
116
|
+
# Fetch attribute value using name or regex
|
117
|
+
# @param name [String|Regexp] The attribute name
|
118
|
+
# @return [String|Array] Depending on the single value compatibility status this returns:
|
119
|
+
# - First value if single_value_compatibility = true
|
120
|
+
# response.attributes['mail'] # => 'user@example.com'
|
121
|
+
# - All values if single_value_compatibility = false
|
122
|
+
# response.attributes['mail'] # => ['user@example.com','user@example.net']
|
123
|
+
#
|
124
|
+
def fetch(name)
|
125
|
+
attributes.each_key do |attribute_key|
|
126
|
+
if name.is_a?(Regexp)
|
127
|
+
if name.method_exists? :match?
|
128
|
+
return self[attribute_key] if name.match?(attribute_key)
|
129
|
+
else
|
130
|
+
return self[attribute_key] if name.match(attribute_key)
|
131
|
+
end
|
132
|
+
elsif canonize_name(name) == canonize_name(attribute_key)
|
133
|
+
return self[attribute_key]
|
134
|
+
end
|
135
|
+
end
|
136
|
+
nil
|
137
|
+
end
|
138
|
+
|
116
139
|
protected
|
117
140
|
|
118
141
|
# stringifies all names so both 'email' and :email return the same result
|
@@ -3,6 +3,7 @@ require "rexml/document"
|
|
3
3
|
require "onelogin/ruby-saml/logging"
|
4
4
|
require "onelogin/ruby-saml/saml_message"
|
5
5
|
require "onelogin/ruby-saml/utils"
|
6
|
+
require "onelogin/ruby-saml/setting_error"
|
6
7
|
|
7
8
|
# Only supports SAML 2.0
|
8
9
|
module OneLogin
|
@@ -23,6 +24,10 @@ module OneLogin
|
|
23
24
|
@uuid = OneLogin::RubySaml::Utils.uuid
|
24
25
|
end
|
25
26
|
|
27
|
+
def request_id
|
28
|
+
@uuid
|
29
|
+
end
|
30
|
+
|
26
31
|
# Creates the AuthNRequest string.
|
27
32
|
# @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings
|
28
33
|
# @param params [Hash] Some extra parameters to be added in the GET for example the RelayState
|
@@ -30,14 +35,14 @@ module OneLogin
|
|
30
35
|
#
|
31
36
|
def create(settings, params = {})
|
32
37
|
params = create_params(settings, params)
|
33
|
-
params_prefix = (settings.
|
38
|
+
params_prefix = (settings.idp_sso_service_url =~ /\?/) ? '&' : '?'
|
34
39
|
saml_request = CGI.escape(params.delete("SAMLRequest"))
|
35
40
|
request_params = "#{params_prefix}SAMLRequest=#{saml_request}"
|
36
41
|
params.each_pair do |key, value|
|
37
42
|
request_params << "&#{key.to_s}=#{CGI.escape(value.to_s)}"
|
38
43
|
end
|
39
|
-
raise "Invalid settings,
|
40
|
-
@login_url = settings.
|
44
|
+
raise SettingError.new "Invalid settings, idp_sso_service_url is not set!" if settings.idp_sso_service_url.nil? or settings.idp_sso_service_url.empty?
|
45
|
+
@login_url = settings.idp_sso_service_url + request_params
|
41
46
|
end
|
42
47
|
|
43
48
|
# Creates the Get parameters for the request.
|
@@ -107,7 +112,7 @@ module OneLogin
|
|
107
112
|
root.attributes['ID'] = uuid
|
108
113
|
root.attributes['IssueInstant'] = time
|
109
114
|
root.attributes['Version'] = "2.0"
|
110
|
-
root.attributes['Destination'] = settings.
|
115
|
+
root.attributes['Destination'] = settings.idp_sso_service_url unless settings.idp_sso_service_url.nil? or settings.idp_sso_service_url.empty?
|
111
116
|
root.attributes['IsPassive'] = settings.passive unless settings.passive.nil?
|
112
117
|
root.attributes['ProtocolBinding'] = settings.protocol_binding unless settings.protocol_binding.nil?
|
113
118
|
root.attributes["AttributeConsumingServiceIndex"] = settings.attributes_index unless settings.attributes_index.nil?
|
@@ -15,10 +15,10 @@ module OneLogin
|
|
15
15
|
|
16
16
|
module SamlMetadata
|
17
17
|
module Vocabulary
|
18
|
-
METADATA = "urn:oasis:names:tc:SAML:2.0:metadata"
|
19
|
-
DSIG = "http://www.w3.org/2000/09/xmldsig#"
|
20
|
-
NAME_FORMAT = "urn:oasis:names:tc:SAML:2.0:attrname-format:*"
|
21
|
-
SAML_ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
|
18
|
+
METADATA = "urn:oasis:names:tc:SAML:2.0:metadata".freeze
|
19
|
+
DSIG = "http://www.w3.org/2000/09/xmldsig#".freeze
|
20
|
+
NAME_FORMAT = "urn:oasis:names:tc:SAML:2.0:attrname-format:*".freeze
|
21
|
+
SAML_ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion".freeze
|
22
22
|
end
|
23
23
|
|
24
24
|
NAMESPACE = {
|
@@ -26,7 +26,7 @@ module OneLogin
|
|
26
26
|
"NameFormat" => Vocabulary::NAME_FORMAT,
|
27
27
|
"saml" => Vocabulary::SAML_ASSERTION,
|
28
28
|
"ds" => Vocabulary::DSIG
|
29
|
-
}
|
29
|
+
}.freeze
|
30
30
|
end
|
31
31
|
|
32
32
|
include SamlMetadata::Vocabulary
|
@@ -34,6 +34,16 @@ module OneLogin
|
|
34
34
|
attr_reader :response
|
35
35
|
attr_reader :options
|
36
36
|
|
37
|
+
# fetch IdP descriptors from a metadata document
|
38
|
+
def self.get_idps(metadata_document, only_entity_id=nil)
|
39
|
+
path = "//md:EntityDescriptor#{only_entity_id && '[@entityID="' + only_entity_id + '"]'}/md:IDPSSODescriptor"
|
40
|
+
REXML::XPath.match(
|
41
|
+
metadata_document,
|
42
|
+
path,
|
43
|
+
SamlMetadata::NAMESPACE
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
37
47
|
# Parse the Identity Provider metadata and update the settings with the
|
38
48
|
# IdP values
|
39
49
|
#
|
@@ -103,6 +113,16 @@ module OneLogin
|
|
103
113
|
def parse(idp_metadata, options = {})
|
104
114
|
parsed_metadata = parse_to_hash(idp_metadata, options)
|
105
115
|
|
116
|
+
unless parsed_metadata[:cache_duration].nil?
|
117
|
+
cache_valid_until_timestamp = OneLogin::RubySaml::Utils.parse_duration(parsed_metadata[:cache_duration])
|
118
|
+
if parsed_metadata[:valid_until].nil? || cache_valid_until_timestamp < Time.parse(parsed_metadata[:valid_until], Time.now.utc).to_i
|
119
|
+
parsed_metadata[:valid_until] = Time.at(cache_valid_until_timestamp).utc.strftime("%Y-%m-%dT%H:%M:%SZ")
|
120
|
+
end
|
121
|
+
end
|
122
|
+
# Remove the cache_duration because on the settings
|
123
|
+
# we only gonna suppot valid_until
|
124
|
+
parsed_metadata.delete(:cache_duration)
|
125
|
+
|
106
126
|
settings = options[:settings]
|
107
127
|
|
108
128
|
if settings.nil?
|
@@ -139,17 +159,21 @@ module OneLogin
|
|
139
159
|
#
|
140
160
|
# @return [Array<Hash>]
|
141
161
|
def parse_to_array(idp_metadata, options = {})
|
162
|
+
parse_to_idp_metadata_array(idp_metadata, options).map{|idp_md| idp_md.to_hash(options)}
|
163
|
+
end
|
164
|
+
|
165
|
+
def parse_to_idp_metadata_array(idp_metadata, options = {})
|
142
166
|
@document = REXML::Document.new(idp_metadata)
|
143
167
|
@options = options
|
144
168
|
|
145
|
-
idpsso_descriptors =
|
169
|
+
idpsso_descriptors = self.class.get_idps(@document, options[:entity_id])
|
146
170
|
if !idpsso_descriptors.any?
|
147
171
|
raise ArgumentError.new("idp_metadata must contain an IDPSSODescriptor element")
|
148
172
|
end
|
149
173
|
|
150
|
-
return idpsso_descriptors.map{|id| IdpMetadata.new(id, id.parent.attributes["entityID"])
|
174
|
+
return idpsso_descriptors.map{|id| IdpMetadata.new(id, id.parent.attributes["entityID"])}
|
151
175
|
end
|
152
|
-
|
176
|
+
|
153
177
|
private
|
154
178
|
|
155
179
|
# Retrieve the remote IdP metadata from the URL or a cached copy.
|
@@ -175,6 +199,7 @@ module OneLogin
|
|
175
199
|
end
|
176
200
|
|
177
201
|
get = Net::HTTP::Get.new(uri.request_uri)
|
202
|
+
get.basic_auth uri.user, uri.password if uri.user
|
178
203
|
@response = http.request(get)
|
179
204
|
return response.body if response.is_a? Net::HTTPSuccess
|
180
205
|
|
@@ -184,15 +209,8 @@ module OneLogin
|
|
184
209
|
end
|
185
210
|
|
186
211
|
class IdpMetadata
|
187
|
-
|
188
|
-
|
189
|
-
REXML::XPath.match(
|
190
|
-
metadata_document,
|
191
|
-
path,
|
192
|
-
SamlMetadata::NAMESPACE
|
193
|
-
)
|
194
|
-
end
|
195
|
-
|
212
|
+
attr_reader :idpsso_descriptor, :entity_id
|
213
|
+
|
196
214
|
def initialize(idpsso_descriptor, entity_id)
|
197
215
|
@idpsso_descriptor = idpsso_descriptor
|
198
216
|
@entity_id = entity_id
|
@@ -202,13 +220,15 @@ module OneLogin
|
|
202
220
|
{
|
203
221
|
:idp_entity_id => @entity_id,
|
204
222
|
:name_identifier_format => idp_name_id_format,
|
205
|
-
:
|
206
|
-
:
|
223
|
+
:idp_sso_service_url => single_signon_service_url(options),
|
224
|
+
:idp_slo_service_url => single_logout_service_url(options),
|
225
|
+
:idp_slo_response_service_url => single_logout_response_service_url(options),
|
207
226
|
:idp_attribute_names => attribute_names,
|
208
227
|
:idp_cert => nil,
|
209
228
|
:idp_cert_fingerprint => nil,
|
210
229
|
:idp_cert_multi => nil,
|
211
|
-
:valid_until => valid_until
|
230
|
+
:valid_until => valid_until,
|
231
|
+
:cache_duration => cache_duration,
|
212
232
|
}.tap do |response_hash|
|
213
233
|
merge_certificates_into(response_hash) unless certificates.nil?
|
214
234
|
end
|
@@ -232,6 +252,13 @@ module OneLogin
|
|
232
252
|
root.attributes['validUntil'] if root && root.attributes
|
233
253
|
end
|
234
254
|
|
255
|
+
# @return [String|nil] 'cacheDuration' attribute of metadata
|
256
|
+
#
|
257
|
+
def cache_duration
|
258
|
+
root = @idpsso_descriptor.root
|
259
|
+
root.attributes['cacheDuration'] if root && root.attributes
|
260
|
+
end
|
261
|
+
|
235
262
|
# @param binding_priority [Array]
|
236
263
|
# @return [String|nil] SingleSignOnService binding if exists
|
237
264
|
#
|
@@ -296,6 +323,21 @@ module OneLogin
|
|
296
323
|
return node.value if node
|
297
324
|
end
|
298
325
|
|
326
|
+
# @param options [Hash]
|
327
|
+
# @return [String|nil] SingleLogoutService response url if exists
|
328
|
+
#
|
329
|
+
def single_logout_response_service_url(options = {})
|
330
|
+
binding = single_logout_service_binding(options[:slo_binding])
|
331
|
+
return if binding.nil?
|
332
|
+
|
333
|
+
node = REXML::XPath.first(
|
334
|
+
@idpsso_descriptor,
|
335
|
+
"md:SingleLogoutService[@Binding=\"#{binding}\"]/@ResponseLocation",
|
336
|
+
SamlMetadata::NAMESPACE
|
337
|
+
)
|
338
|
+
return node.value if node
|
339
|
+
end
|
340
|
+
|
299
341
|
# @return [String|nil] Unformatted Certificate if exists
|
300
342
|
#
|
301
343
|
def certificates
|
@@ -393,10 +435,6 @@ module OneLogin
|
|
393
435
|
|
394
436
|
settings
|
395
437
|
end
|
396
|
-
|
397
|
-
if self.respond_to?(:private_constant)
|
398
|
-
private_constant :SamlMetadata, :IdpMetadata
|
399
|
-
end
|
400
438
|
end
|
401
439
|
end
|
402
440
|
end
|