ruby-saml 1.13.0 → 1.17.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 +5 -5
- data/.github/FUNDING.yml +3 -0
- data/.github/workflows/test.yml +67 -3
- data/CHANGELOG.md +31 -1
- data/LICENSE +2 -1
- data/README.md +141 -40
- data/UPGRADING.md +1 -1
- data/lib/onelogin/ruby-saml/authrequest.rb +8 -9
- data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +3 -3
- data/lib/onelogin/ruby-saml/logoutrequest.rb +7 -7
- data/lib/onelogin/ruby-saml/logoutresponse.rb +1 -1
- data/lib/onelogin/ruby-saml/metadata.rb +21 -25
- data/lib/onelogin/ruby-saml/response.rb +25 -20
- data/lib/onelogin/ruby-saml/saml_message.rb +2 -3
- data/lib/onelogin/ruby-saml/settings.rb +137 -42
- data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +39 -33
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +8 -8
- data/lib/onelogin/ruby-saml/utils.rb +96 -26
- data/lib/onelogin/ruby-saml/version.rb +1 -1
- data/lib/xml_security.rb +22 -9
- data/ruby-saml.gemspec +41 -13
- metadata +50 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 188af740c1b9627be73d3a4cbd8261316773d3b3da0b74b0ef49b5d1c9c04f02
|
4
|
+
data.tar.gz: 85cb561ba597924b7037be197dac6fd175c1a626969829e170bb44d8e8a1a50d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b154ce0a94f1f1b525179d33f8a98d5422cabafe527f32104646135ae0a9218064638639b7ec5735de1dda8ef55faa5571f22f563d57f44e040a81b6116b5a1
|
7
|
+
data.tar.gz: 78ea0021299530423e28935782b851894ed9740c1e93c610575518532d22bcc20829dbe26b0569f38ad21894cc145b3d785c75765c8bd0dde6e078598d864545
|
data/.github/FUNDING.yml
ADDED
data/.github/workflows/test.yml
CHANGED
@@ -8,11 +8,57 @@ jobs:
|
|
8
8
|
strategy:
|
9
9
|
fail-fast: false
|
10
10
|
matrix:
|
11
|
-
os:
|
12
|
-
|
11
|
+
os:
|
12
|
+
- ubuntu-20.04
|
13
|
+
- macos-latest
|
14
|
+
- windows-latest
|
15
|
+
ruby-version:
|
16
|
+
- 2.1
|
17
|
+
- 2.2
|
18
|
+
- 2.3
|
19
|
+
- 2.4
|
20
|
+
- 2.5
|
21
|
+
- 2.6
|
22
|
+
- 2.7
|
23
|
+
- 3.0
|
24
|
+
- 3.1
|
25
|
+
- 3.2
|
26
|
+
- 3.3
|
27
|
+
- jruby-9.1
|
28
|
+
- jruby-9.2
|
29
|
+
- jruby-9.3
|
30
|
+
- jruby-9.4
|
31
|
+
- truffleruby
|
32
|
+
exclude:
|
33
|
+
- os: macos-latest
|
34
|
+
ruby-version: 2.1
|
35
|
+
- os: macos-latest
|
36
|
+
ruby-version: 2.2
|
37
|
+
- os: macos-latest
|
38
|
+
ruby-version: 2.3
|
39
|
+
- os: macos-latest
|
40
|
+
ruby-version: 2.4
|
41
|
+
- os: macos-latest
|
42
|
+
ruby-version: 2.5
|
43
|
+
- os: macos-latest
|
44
|
+
ruby-version: jruby-9.1
|
45
|
+
- os: macos-latest
|
46
|
+
ruby-version: jruby-9.2
|
47
|
+
- os: windows-latest
|
48
|
+
ruby-version: 2.1
|
49
|
+
- os: windows-latest
|
50
|
+
ruby-version: jruby-9.1
|
51
|
+
- os: windows-latest
|
52
|
+
ruby-version: jruby-9.2
|
53
|
+
- os: windows-latest
|
54
|
+
ruby-version: jruby-9.3
|
55
|
+
- os: windows-latest
|
56
|
+
ruby-version: jruby-9.4
|
57
|
+
- os: windows-latest
|
58
|
+
ruby-version: truffleruby
|
13
59
|
runs-on: ${{ matrix.os }}
|
14
60
|
steps:
|
15
|
-
- uses: actions/checkout@
|
61
|
+
- uses: actions/checkout@v4
|
16
62
|
- name: Set up Ruby ${{ matrix.ruby-version }}
|
17
63
|
uses: ruby/setup-ruby@v1
|
18
64
|
with:
|
@@ -23,3 +69,21 @@ jobs:
|
|
23
69
|
|
24
70
|
- name: Run tests
|
25
71
|
run: bundle exec rake
|
72
|
+
|
73
|
+
- name: Coveralls
|
74
|
+
uses: coverallsapp/github-action@master
|
75
|
+
with:
|
76
|
+
github-token: ${{ secrets.github_token }}
|
77
|
+
parallel: true
|
78
|
+
flag-name: run-${{ matrix.ruby-version }}
|
79
|
+
|
80
|
+
finish:
|
81
|
+
needs: test
|
82
|
+
runs-on: ubuntu-latest
|
83
|
+
steps:
|
84
|
+
- name: Coveralls Finished
|
85
|
+
uses: coverallsapp/github-action@master
|
86
|
+
with:
|
87
|
+
github-token: ${{ secrets.github_token }}
|
88
|
+
flag-name: run-${{ matrix.ruby-version }}
|
89
|
+
parallel-finished: true
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,32 @@
|
|
1
1
|
# Ruby SAML Changelog
|
2
2
|
|
3
|
+
### 1.17.0 (Sep 10, 2024)
|
4
|
+
* Fix for critical vulnerability CVE-2024-45409: SAML authentication bypass via Incorrect XPath selector
|
5
|
+
* [#687](https://github.com/SAML-Toolkits/ruby-saml/pull/687) Add CI coverage for Ruby 3.3 and Windows.
|
6
|
+
* [#673](https://github.com/SAML-Toolkits/ruby-saml/pull/673) Add `Settings#sp_cert_multi` paramter to facilitate SP certificate and key rotation.
|
7
|
+
* [#673](https://github.com/SAML-Toolkits/ruby-saml/pull/673) Support multiple simultaneous SP decryption keys via `Settings#sp_cert_multi` parameter.
|
8
|
+
* [#673](https://github.com/SAML-Toolkits/ruby-saml/pull/673) Deprecate `Settings#certificate_new` parameter.
|
9
|
+
* [#673](https://github.com/SAML-Toolkits/ruby-saml/pull/673) `:check_sp_cert_expiration` will use the first non-expired certificate/key when signing/decrypting. It will raise an error only if there are no valid certificates/keys.
|
10
|
+
* [#673](https://github.com/SAML-Toolkits/ruby-saml/pull/673) `:check_sp_cert_expiration` now validates the certificate `not_before` condition; previously it was only validating `not_after`.
|
11
|
+
* [#673](https://github.com/SAML-Toolkits/ruby-saml/pull/673) `:check_sp_cert_expiration` now causes the generated SP metadata to exclude any inactive/expired certificates.
|
12
|
+
|
13
|
+
### 1.16.0 (Oct 09, 2023)
|
14
|
+
* [#671](https://github.com/SAML-Toolkits/ruby-saml/pull/671) Add support on LogoutRequest with Encrypted NameID
|
15
|
+
|
16
|
+
### 1.15.0 (Jan 04, 2023)
|
17
|
+
* [#650](https://github.com/SAML-Toolkits/ruby-saml/pull/650) Replace strip! by strip on compute_digest method
|
18
|
+
* [#638](https://github.com/SAML-Toolkits/ruby-saml/pull/638) Fix dateTime format for the validUntil attribute of the generated metadata
|
19
|
+
* [#576](https://github.com/SAML-Toolkits/ruby-saml/pull/576) Support `Settings#idp_cert_multi` with string keys
|
20
|
+
* [#567](https://github.com/SAML-Toolkits/ruby-saml/pull/567) Improve Code quality
|
21
|
+
* Add info about new repo, new maintainer, new security contact
|
22
|
+
* Fix tests, Adjust dependencies, Add ruby 3.2 and new jruby versions tests to the CI. Add coveralls support
|
23
|
+
|
24
|
+
### 1.14.0 (Feb 01, 2022)
|
25
|
+
* [#627](https://github.com/onelogin/ruby-saml/pull/627) Support escape downcasing for validating SLO Signatures of ADFS/Azure
|
26
|
+
* [#633](https://github.com/onelogin/ruby-saml/pull/633) Support ability to change ID prefix
|
27
|
+
* Make the uuid editable on the SAML Messages generated by the toolkit
|
28
|
+
* [#622](https://github.com/onelogin/ruby-saml/pull/622) Add security setting to more strictly enforce audience validation
|
29
|
+
|
3
30
|
### 1.13.0 (Sept 06, 2021)
|
4
31
|
* [#611](https://github.com/onelogin/ruby-saml/pull/601) Replace MAX_BYTE_SIZE constant with setting: message_max_bytesize
|
5
32
|
* [#605](https://github.com/onelogin/ruby-saml/pull/605) :allowed_clock_drift is now bidrectional
|
@@ -13,6 +40,9 @@
|
|
13
40
|
* Add warning about the use of IdpMetadataParser class and SSRF
|
14
41
|
* CI: Migrate from Travis to Github Actions
|
15
42
|
|
43
|
+
### 1.12.3 (Sep 10, 2024)
|
44
|
+
* Fix for critical vulnerability CVE-2024-45409: SAML authentication bypass via Incorrect XPath selector
|
45
|
+
|
16
46
|
### 1.12.2 (Apr 08, 2021)
|
17
47
|
* [#575](https://github.com/onelogin/ruby-saml/pull/575) Fix SloLogoutresponse bug on LogoutRequest
|
18
48
|
|
@@ -34,7 +64,7 @@
|
|
34
64
|
* Support Process Transform
|
35
65
|
* Raise SettingError if invoking an action with no endpoint defined on the settings
|
36
66
|
* Made IdpMetadataParser more extensible for subclasses
|
37
|
-
*[#548](https://github.com/onelogin/ruby-saml/pull/548) Add :skip_audience option
|
67
|
+
* [#548](https://github.com/onelogin/ruby-saml/pull/548) Add :skip_audience option
|
38
68
|
* [#555](https://github.com/onelogin/ruby-saml/pull/555) Define 'soft' variable to prevent exception when doc cert is invalid
|
39
69
|
* Improve documentation
|
40
70
|
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
# Ruby SAML
|
2
|
-
[](https://github.com/SAML-Toolkits/ruby-saml/actions/workflows/test.yml)
|
3
|
+
[](https://coveralls.io/github/SAML-Toolkits/ruby-saml?branch=master)
|
4
|
+
[](https://badge.fury.io/rb/ruby-saml)
|
5
|
+
[](https://badge.fury.io/gh/SAML-Toolkits%2Fruby-saml)   
|
4
6
|
|
5
7
|
Ruby SAML minor and tiny versions may introduce breaking changes. Please read
|
6
8
|
[UPGRADING.md](UPGRADING.md) for guidance on upgrading to new Ruby SAML versions.
|
7
9
|
|
10
|
+
There is a critical vulnerability affecting ruby-saml < 1.17.0 (CVE-2024-45409). Make sure you are using an updated version. (1.12.3 is safe)
|
11
|
+
|
8
12
|
## Overview
|
9
13
|
|
10
14
|
The Ruby SAML library is for implementing the client side of a SAML authorization,
|
@@ -14,32 +18,16 @@ requests from identity providers.
|
|
14
18
|
SAML authorization is a two step process and you are expected to implement support for both.
|
15
19
|
|
16
20
|
We created a demo project for Rails 4 that uses the latest version of this library:
|
17
|
-
[ruby-saml-example](https://github.com/
|
21
|
+
[ruby-saml-example](https://github.com/saml-toolkits/ruby-saml-example)
|
18
22
|
|
19
23
|
### Supported Ruby Versions
|
20
24
|
|
21
25
|
The following Ruby versions are covered by CI testing:
|
22
26
|
|
23
|
-
* 2.1.
|
24
|
-
*
|
25
|
-
* 2.3.x
|
26
|
-
* 2.4.x
|
27
|
-
* 2.5.x
|
28
|
-
* 2.6.x
|
29
|
-
* 2.7.x
|
30
|
-
* 3.0.x
|
31
|
-
* JRuby 9.1.x
|
32
|
-
* JRuby 9.2.x
|
27
|
+
* Ruby (MRI) 2.1 to 3.3
|
28
|
+
* JRuby 9.1 to 9.4
|
33
29
|
* TruffleRuby (latest)
|
34
30
|
|
35
|
-
In addition, the following may work but are untested:
|
36
|
-
|
37
|
-
* 1.8.7
|
38
|
-
* 1.9.x
|
39
|
-
* 2.0.x
|
40
|
-
* JRuby 1.7.x
|
41
|
-
* JRuby 9.0.x
|
42
|
-
|
43
31
|
## Adding Features, Pull Requests
|
44
32
|
|
45
33
|
* Fork the repository
|
@@ -52,8 +40,7 @@ In addition, the following may work but are untested:
|
|
52
40
|
## Security Guidelines
|
53
41
|
|
54
42
|
If you believe you have discovered a security vulnerability in this gem, please report it
|
55
|
-
|
56
|
-
guidelines, and will work with you to quickly find a resolution.
|
43
|
+
by mail to the maintainer: sixto.martin.garcia+security@gmail.com
|
57
44
|
|
58
45
|
### Security Warning
|
59
46
|
|
@@ -66,7 +53,7 @@ However, ruby-saml never enables this dangerous Nokogiri configuration;
|
|
66
53
|
ruby-saml never enables DTDLOAD, and it never disables NONET.
|
67
54
|
|
68
55
|
The OneLogin::RubySaml::IdpMetadataParser class does not validate in any way the URL
|
69
|
-
that is introduced in order to be parsed.
|
56
|
+
that is introduced in order to be parsed.
|
70
57
|
|
71
58
|
Usually the same administrator that handles the Service Provider also sets the URL to
|
72
59
|
the IdP, which should be a trusted resource.
|
@@ -87,7 +74,7 @@ Using `Gemfile`
|
|
87
74
|
gem 'ruby-saml', '~> 1.11.0'
|
88
75
|
|
89
76
|
# or track master for bleeding-edge
|
90
|
-
gem 'ruby-saml', :github => '
|
77
|
+
gem 'ruby-saml', :github => 'saml-toolkit/ruby-saml'
|
91
78
|
```
|
92
79
|
|
93
80
|
Using RubyGems
|
@@ -386,12 +373,78 @@ IdpMetadataParser by its Entity Id value:
|
|
386
373
|
)
|
387
374
|
```
|
388
375
|
|
376
|
+
### Retrieve one Entity Descriptor with an specific binding and nameid format when several are available
|
377
|
+
|
378
|
+
If the Metadata contains several bindings and nameids, the relevant ones
|
379
|
+
also can be specified when retrieving the settings from the IdpMetadataParser
|
380
|
+
by the values of binding and nameid:
|
381
|
+
|
382
|
+
```ruby
|
383
|
+
validate_cert = true
|
384
|
+
options = {
|
385
|
+
entity_id: "http//example.com/target/entity",
|
386
|
+
name_id_format: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
|
387
|
+
sso_binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
|
388
|
+
slo_binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
|
389
|
+
}
|
390
|
+
settings = idp_metadata_parser.parse_remote(
|
391
|
+
"https://example.com/auth/saml2/idp/metadata",
|
392
|
+
validate_cert,
|
393
|
+
options
|
394
|
+
)
|
395
|
+
```
|
396
|
+
|
389
397
|
### Parsing Metadata into an Hash
|
390
398
|
|
391
399
|
The `OneLogin::RubySaml::IdpMetadataParser` also provides the methods `#parse_to_hash` and `#parse_remote_to_hash`.
|
392
400
|
Those return an Hash instead of a `Settings` object, which may be useful for configuring
|
393
401
|
[omniauth-saml](https://github.com/omniauth/omniauth-saml), for instance.
|
394
402
|
|
403
|
+
|
404
|
+
### Validating Signature of Metadata and retrieve settings
|
405
|
+
|
406
|
+
Right now there is no method at ruby_saml to validate the signature of the metadata that gonna be parsed,
|
407
|
+
but it can be done as follows:
|
408
|
+
* Download the XML.
|
409
|
+
* Validate the Signature, providing the cert.
|
410
|
+
* Provide the XML to the parse method if the signature was validated
|
411
|
+
|
412
|
+
```ruby
|
413
|
+
require "xml_security"
|
414
|
+
require "onelogin/ruby-saml/utils"
|
415
|
+
require "onelogin/ruby-saml/idp_metadata_parser"
|
416
|
+
|
417
|
+
url = "<url_to_the_metadata>"
|
418
|
+
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
|
419
|
+
|
420
|
+
uri = URI.parse(url)
|
421
|
+
raise ArgumentError.new("url must begin with http or https") unless /^https?/ =~ uri.scheme
|
422
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
423
|
+
if uri.scheme == "https"
|
424
|
+
http.use_ssl = true
|
425
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
426
|
+
end
|
427
|
+
|
428
|
+
get = Net::HTTP::Get.new(uri.request_uri)
|
429
|
+
get.basic_auth uri.user, uri.password if uri.user
|
430
|
+
response = http.request(get)
|
431
|
+
xml = response.body
|
432
|
+
errors = []
|
433
|
+
doc = XMLSecurity::SignedDocument.new(xml, errors)
|
434
|
+
cert_str = "<include_cert_here>"
|
435
|
+
cert = OneLogin::RubySaml::Utils.format_cert("cert_str")
|
436
|
+
metadata_sign_cert = OpenSSL::X509::Certificate.new(cert)
|
437
|
+
valid = doc.validate_document_with_cert(metadata_sign_cert, true)
|
438
|
+
if valid
|
439
|
+
settings = idp_metadata_parser.parse(
|
440
|
+
xml,
|
441
|
+
entity_id: "<entity_id_of_the_entity_to_be_retrieved>"
|
442
|
+
)
|
443
|
+
else
|
444
|
+
print "Metadata Signarture failed to be verified with the cert provided"
|
445
|
+
end
|
446
|
+
```
|
447
|
+
|
395
448
|
## Retrieving Attributes
|
396
449
|
|
397
450
|
If you are using `saml:AttributeStatement` to transfer data like the username, you can access all the attributes through `response.attributes`. It contains all the `saml:AttributeStatement`s with its 'Name' as an indifferent key and one or more `saml:AttributeValue`s as values. The value returned depends on the value of the
|
@@ -627,7 +680,7 @@ signature validation process will fail at the Identity Provider.
|
|
627
680
|
Ruby SAML supports EncryptedAssertion. The Identity Provider will encrypt the Assertion with the
|
628
681
|
public cert of the Service Provider. The Service Provider will decrypt the EncryptedAssertion with its private key.
|
629
682
|
|
630
|
-
You may enable EncryptedAssertion as follows. This will add `<md:KeyDescriptor use="
|
683
|
+
You may enable EncryptedAssertion as follows. This will add `<md:KeyDescriptor use="encryption">` to your
|
631
684
|
SP Metadata XML, to be read by the IdP.
|
632
685
|
|
633
686
|
```ruby
|
@@ -664,27 +717,69 @@ validation fails. You may disable such exceptions using the `settings.security[:
|
|
664
717
|
settings.security[:soft] = true # Do not raise error on failed signature/certificate validations
|
665
718
|
```
|
666
719
|
|
667
|
-
#### Key Rollover
|
720
|
+
#### Advanced SP Certificate Usage & Key Rollover
|
668
721
|
|
669
|
-
|
670
|
-
|
671
|
-
|
722
|
+
Ruby SAML provides the `settings.sp_cert_multi` parameter to enable the following
|
723
|
+
advanced usage scenarios:
|
724
|
+
- Rotating SP certificates and private keys without disruption of service.
|
725
|
+
- Specifying separate SP certificates for signing and encryption.
|
672
726
|
|
673
|
-
|
674
|
-
|
675
|
-
and encryption at this time.
|
727
|
+
The `sp_cert_multi` parameter replaces `certificate` and `private_key`
|
728
|
+
(you may not specify both pparameters at the same time.) `sp_cert_multi` has the following shape:
|
676
729
|
|
677
730
|
```ruby
|
678
|
-
|
679
|
-
|
680
|
-
|
731
|
+
settings.sp_cert_multi = {
|
732
|
+
signing: [
|
733
|
+
{ certificate: cert1, private_key: private_key1 },
|
734
|
+
{ certificate: cert2, private_key: private_key2 }
|
735
|
+
],
|
736
|
+
encryption: [
|
737
|
+
{ certificate: cert1, private_key: private_key1 },
|
738
|
+
{ certificate: cert3, private_key: private_key1 }
|
739
|
+
],
|
740
|
+
}
|
681
741
|
```
|
682
742
|
|
683
|
-
|
743
|
+
Certificate rotation is acheived by inserting new certificates at the bottom of each list,
|
744
|
+
and then removing the old certificates from the top of the list once your IdPs have migrated.
|
745
|
+
A common practice is for apps to publish the current SP metadata at a URL endpoint and have
|
746
|
+
the IdP regularly poll for updates.
|
747
|
+
|
748
|
+
Note the following:
|
749
|
+
- You may re-use the same certificate and/or private key in multiple places, including for both signing and encryption.
|
750
|
+
- The IdP should attempt to verify signatures with *all* `:signing` certificates,
|
751
|
+
and permit if *any one* succeeds. When signing, Ruby SAML will use the first SP certificate
|
752
|
+
in the `sp_cert_multi[:signing]` array. This will be the first active/non-expired certificate
|
753
|
+
in the array if `settings.security[:check_sp_cert_expiration]` is true.
|
754
|
+
- The IdP may encrypt with any of the SP certificates in the `sp_cert_multi[:encryption]`
|
755
|
+
array. When decrypting, Ruby SAML attempt to decrypt with each SP private key in
|
756
|
+
`sp_cert_multi[:encryption]` until the decryption is successful. This will skip private
|
757
|
+
keys for inactive/expired certificates if `:check_sp_cert_expiration` is true.
|
758
|
+
- If `:check_sp_cert_expiration` is true, the generated SP metadata XML will not include
|
759
|
+
inactive/expired certificates. This avoids validation errors when the IdP reads the SP
|
760
|
+
metadata.
|
761
|
+
|
762
|
+
#### Audience Validation
|
763
|
+
|
764
|
+
A service provider should only consider a SAML response valid if the IdP includes an <AudienceRestriction>
|
765
|
+
element containting an <Audience> element that uniquely identifies the service provider. Unless you specify
|
766
|
+
the `skip_audience` option, Ruby SAML will validate that each SAML response includes an <Audience> element
|
767
|
+
whose contents matches `settings.sp_entity_id`.
|
768
|
+
|
769
|
+
By default, Ruby SAML considers an <AudienceRestriction> element containing only empty <Audience> elements
|
770
|
+
to be valid. That means an otherwise valid SAML response with a condition like this would be valid:
|
771
|
+
|
772
|
+
```xml
|
773
|
+
<AudienceRestriction>
|
774
|
+
<Audience />
|
775
|
+
</AudienceRestriction>
|
776
|
+
```
|
777
|
+
|
778
|
+
You may enforce that an <AudienceRestriction> element containing only empty <Audience> elements
|
779
|
+
is invalid using the `settings.security[:strict_audience_validation]` parameter.
|
684
780
|
|
685
781
|
```ruby
|
686
|
-
|
687
|
-
settings.private_key = "PRIVATE KEY FOR CERT B"
|
782
|
+
settings.security[:strict_audience_validation] = true
|
688
783
|
```
|
689
784
|
|
690
785
|
## Single Log Out
|
@@ -767,7 +862,13 @@ Here is an example that we could add to our previous controller to process a SAM
|
|
767
862
|
# Method to handle IdP initiated logouts
|
768
863
|
def idp_logout_request
|
769
864
|
settings = Account.get_saml_settings
|
770
|
-
|
865
|
+
# ADFS URL-Encodes SAML data as lowercase, and the toolkit by default uses
|
866
|
+
# uppercase. Turn it True for ADFS compatibility on signature verification
|
867
|
+
settings.security[:lowercase_url_encoding] = true
|
868
|
+
|
869
|
+
logout_request = OneLogin::RubySaml::SloLogoutrequest.new(
|
870
|
+
params[:SAMLRequest], settings: settings
|
871
|
+
)
|
771
872
|
if !logout_request.is_valid?
|
772
873
|
logger.error "IdP initiated LogoutRequest was not valid!"
|
773
874
|
return render :inline => logger.error
|
data/UPGRADING.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Ruby SAML Migration Guide
|
2
2
|
|
3
|
-
## Updating from 1.12.x to 1.13.0
|
3
|
+
## Updating from 1.12.x to 1.13.0
|
4
4
|
|
5
5
|
Version `1.13.0` adds `settings.idp_sso_service_binding` and `settings.idp_slo_service_binding`, and
|
6
6
|
deprecates `settings.security[:embed_sign]`. If specified, new binding parameters will be used in place of `:embed_sign`
|
@@ -15,7 +15,7 @@ module OneLogin
|
|
15
15
|
class Authrequest < SamlMessage
|
16
16
|
|
17
17
|
# AuthNRequest ID
|
18
|
-
|
18
|
+
attr_accessor :uuid
|
19
19
|
|
20
20
|
# Initializes the AuthNRequest. An Authrequest Object that is an extension of the SamlMessage class.
|
21
21
|
# Asigns an ID, a random uuid.
|
@@ -39,7 +39,7 @@ module OneLogin
|
|
39
39
|
saml_request = CGI.escape(params.delete("SAMLRequest"))
|
40
40
|
request_params = "#{params_prefix}SAMLRequest=#{saml_request}"
|
41
41
|
params.each_pair do |key, value|
|
42
|
-
request_params << "&#{key
|
42
|
+
request_params << "&#{key}=#{CGI.escape(value.to_s)}"
|
43
43
|
end
|
44
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
45
|
@login_url = settings.idp_sso_service_url + request_params
|
@@ -72,9 +72,10 @@ module OneLogin
|
|
72
72
|
request = deflate(request) if settings.compress_request
|
73
73
|
base64_request = encode(request)
|
74
74
|
request_params = {"SAMLRequest" => base64_request}
|
75
|
+
sp_signing_key = settings.get_sp_signing_key
|
75
76
|
|
76
|
-
if settings.idp_sso_service_binding == Utils::BINDINGS[:redirect] && settings.security[:authn_requests_signed] &&
|
77
|
-
params['SigAlg']
|
77
|
+
if settings.idp_sso_service_binding == Utils::BINDINGS[:redirect] && settings.security[:authn_requests_signed] && sp_signing_key
|
78
|
+
params['SigAlg'] = settings.security[:signature_method]
|
78
79
|
url_string = OneLogin::RubySaml::Utils.build_query(
|
79
80
|
:type => 'SAMLRequest',
|
80
81
|
:data => base64_request,
|
@@ -82,7 +83,7 @@ module OneLogin
|
|
82
83
|
:sig_alg => params['SigAlg']
|
83
84
|
)
|
84
85
|
sign_algorithm = XMLSecurity::BaseDocument.new.algorithm(settings.security[:signature_method])
|
85
|
-
signature =
|
86
|
+
signature = sp_signing_key.sign(sign_algorithm.new, url_string)
|
86
87
|
params['Signature'] = encode(signature)
|
87
88
|
end
|
88
89
|
|
@@ -179,15 +180,13 @@ module OneLogin
|
|
179
180
|
end
|
180
181
|
|
181
182
|
def sign_document(document, settings)
|
182
|
-
|
183
|
-
|
184
|
-
cert = settings.get_sp_cert
|
183
|
+
cert, private_key = settings.get_sp_signing_pair
|
184
|
+
if settings.idp_sso_service_binding == Utils::BINDINGS[:post] && settings.security[:authn_requests_signed] && private_key && cert
|
185
185
|
document.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
|
186
186
|
end
|
187
187
|
|
188
188
|
document
|
189
189
|
end
|
190
|
-
|
191
190
|
end
|
192
191
|
end
|
193
192
|
end
|
@@ -186,8 +186,6 @@ module OneLogin
|
|
186
186
|
idpsso_descriptors.map {|id| IdpMetadata.new(id, id.parent.attributes["entityID"])}
|
187
187
|
end
|
188
188
|
|
189
|
-
private
|
190
|
-
|
191
189
|
# Retrieve the remote IdP metadata from the URL or a cached copy.
|
192
190
|
# @param url [String] Url where the XML of the Identity Provider Metadata is published.
|
193
191
|
# @param validate_cert [Boolean] If true and the URL is HTTPs, the cert of the domain is checked.
|
@@ -220,6 +218,8 @@ module OneLogin
|
|
220
218
|
)
|
221
219
|
end
|
222
220
|
|
221
|
+
private
|
222
|
+
|
223
223
|
class IdpMetadata
|
224
224
|
attr_reader :idpsso_descriptor, :entity_id
|
225
225
|
|
@@ -442,7 +442,7 @@ module OneLogin
|
|
442
442
|
priority = Array(priority)
|
443
443
|
if priority.any?
|
444
444
|
values = nodes.map(&:text)
|
445
|
-
|
445
|
+
priority.detect { |candidate| values.include?(candidate) }
|
446
446
|
else
|
447
447
|
nodes.first.text
|
448
448
|
end
|
@@ -12,7 +12,7 @@ module OneLogin
|
|
12
12
|
class Logoutrequest < SamlMessage
|
13
13
|
|
14
14
|
# Logout Request ID
|
15
|
-
|
15
|
+
attr_accessor :uuid
|
16
16
|
|
17
17
|
# Initializes the Logout Request. A Logoutrequest Object that is an extension of the SamlMessage class.
|
18
18
|
# Asigns an ID, a random uuid.
|
@@ -36,7 +36,7 @@ module OneLogin
|
|
36
36
|
saml_request = CGI.escape(params.delete("SAMLRequest"))
|
37
37
|
request_params = "#{params_prefix}SAMLRequest=#{saml_request}"
|
38
38
|
params.each_pair do |key, value|
|
39
|
-
request_params << "&#{key
|
39
|
+
request_params << "&#{key}=#{CGI.escape(value.to_s)}"
|
40
40
|
end
|
41
41
|
raise SettingError.new "Invalid settings, idp_slo_service_url is not set!" if settings.idp_slo_service_url.nil? or settings.idp_slo_service_url.empty?
|
42
42
|
@logout_url = settings.idp_slo_service_url + request_params
|
@@ -69,8 +69,9 @@ module OneLogin
|
|
69
69
|
request = deflate(request) if settings.compress_request
|
70
70
|
base64_request = encode(request)
|
71
71
|
request_params = {"SAMLRequest" => base64_request}
|
72
|
+
sp_signing_key = settings.get_sp_signing_key
|
72
73
|
|
73
|
-
if settings.idp_slo_service_binding == Utils::BINDINGS[:redirect] && settings.security[:logout_requests_signed] &&
|
74
|
+
if settings.idp_slo_service_binding == Utils::BINDINGS[:redirect] && settings.security[:logout_requests_signed] && sp_signing_key
|
74
75
|
params['SigAlg'] = settings.security[:signature_method]
|
75
76
|
url_string = OneLogin::RubySaml::Utils.build_query(
|
76
77
|
:type => 'SAMLRequest',
|
@@ -79,7 +80,7 @@ module OneLogin
|
|
79
80
|
:sig_alg => params['SigAlg']
|
80
81
|
)
|
81
82
|
sign_algorithm = XMLSecurity::BaseDocument.new.algorithm(settings.security[:signature_method])
|
82
|
-
signature = settings.
|
83
|
+
signature = settings.get_sp_signing_key.sign(sign_algorithm.new, url_string)
|
83
84
|
params['Signature'] = encode(signature)
|
84
85
|
end
|
85
86
|
|
@@ -138,9 +139,8 @@ module OneLogin
|
|
138
139
|
|
139
140
|
def sign_document(document, settings)
|
140
141
|
# embed signature
|
141
|
-
|
142
|
-
|
143
|
-
cert = settings.get_sp_cert
|
142
|
+
cert, private_key = settings.get_sp_signing_pair
|
143
|
+
if settings.idp_slo_service_binding == Utils::BINDINGS[:post] && settings.security[:logout_requests_signed] && private_key && cert
|
144
144
|
document.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
|
145
145
|
end
|
146
146
|
|
@@ -212,7 +212,7 @@ module OneLogin
|
|
212
212
|
return true unless options.has_key? :get_params
|
213
213
|
return true unless options[:get_params].has_key? 'Signature'
|
214
214
|
|
215
|
-
options[:raw_get_params] = OneLogin::RubySaml::Utils.prepare_raw_get_params(options[:raw_get_params], options[:get_params])
|
215
|
+
options[:raw_get_params] = OneLogin::RubySaml::Utils.prepare_raw_get_params(options[:raw_get_params], options[:get_params], settings.security[:lowercase_url_encoding])
|
216
216
|
|
217
217
|
if options[:get_params]['SigAlg'].nil? && !options[:raw_get_params]['SigAlg'].nil?
|
218
218
|
options[:get_params]['SigAlg'] = CGI.unescape(options[:raw_get_params]['SigAlg'])
|
@@ -49,7 +49,7 @@ module OneLogin
|
|
49
49
|
root = meta_doc.add_element("md:EntityDescriptor", namespaces)
|
50
50
|
root.attributes["ID"] = OneLogin::RubySaml::Utils.uuid
|
51
51
|
root.attributes["entityID"] = settings.sp_entity_id if settings.sp_entity_id
|
52
|
-
root.attributes["validUntil"] = valid_until.strftime('%Y-%m-%dT%H:%M:%
|
52
|
+
root.attributes["validUntil"] = valid_until.utc.strftime('%Y-%m-%dT%H:%M:%SZ') if valid_until
|
53
53
|
root.attributes["cacheDuration"] = "PT" + cache_duration.to_s + "S" if cache_duration
|
54
54
|
root
|
55
55
|
end
|
@@ -62,29 +62,14 @@ module OneLogin
|
|
62
62
|
}
|
63
63
|
end
|
64
64
|
|
65
|
-
# Add KeyDescriptor
|
66
|
-
# with SP certificate, and new SP certificate if any
|
65
|
+
# Add KeyDescriptor elements for SP certificates.
|
67
66
|
def add_sp_certificates(sp_sso, settings)
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
kd = sp_sso.add_element "md:KeyDescriptor", { "use" => "signing" }
|
75
|
-
ki = kd.add_element "ds:KeyInfo", {"xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#"}
|
76
|
-
xd = ki.add_element "ds:X509Data"
|
77
|
-
xc = xd.add_element "ds:X509Certificate"
|
78
|
-
xc.text = cert_text
|
79
|
-
|
80
|
-
if settings.security[:want_assertions_encrypted]
|
81
|
-
kd2 = sp_sso.add_element "md:KeyDescriptor", { "use" => "encryption" }
|
82
|
-
ki2 = kd2.add_element "ds:KeyInfo", {"xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#"}
|
83
|
-
xd2 = ki2.add_element "ds:X509Data"
|
84
|
-
xc2 = xd2.add_element "ds:X509Certificate"
|
85
|
-
xc2.text = cert_text
|
86
|
-
end
|
87
|
-
end
|
67
|
+
certs = settings.get_sp_certs
|
68
|
+
|
69
|
+
certs[:signing].each { |cert, _| add_sp_cert_element(sp_sso, cert, :signing) }
|
70
|
+
|
71
|
+
if settings.security[:want_assertions_encrypted]
|
72
|
+
certs[:encryption].each { |cert, _| add_sp_cert_element(sp_sso, cert, :encryption) }
|
88
73
|
end
|
89
74
|
|
90
75
|
sp_sso
|
@@ -153,8 +138,7 @@ module OneLogin
|
|
153
138
|
def embed_signature(meta_doc, settings)
|
154
139
|
return unless settings.security[:metadata_signed]
|
155
140
|
|
156
|
-
private_key = settings.
|
157
|
-
cert = settings.get_sp_cert
|
141
|
+
cert, private_key = settings.get_sp_signing_pair
|
158
142
|
return unless private_key && cert
|
159
143
|
|
160
144
|
meta_doc.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
|
@@ -172,6 +156,18 @@ module OneLogin
|
|
172
156
|
|
173
157
|
ret
|
174
158
|
end
|
159
|
+
|
160
|
+
private
|
161
|
+
|
162
|
+
def add_sp_cert_element(sp_sso, cert, use)
|
163
|
+
return unless cert
|
164
|
+
cert_text = Base64.encode64(cert.to_der).gsub("\n", '')
|
165
|
+
kd = sp_sso.add_element "md:KeyDescriptor", { "use" => use.to_s }
|
166
|
+
ki = kd.add_element "ds:KeyInfo", { "xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#" }
|
167
|
+
xd = ki.add_element "ds:X509Data"
|
168
|
+
xc = xd.add_element "ds:X509Certificate"
|
169
|
+
xc.text = cert_text
|
170
|
+
end
|
175
171
|
end
|
176
172
|
end
|
177
173
|
end
|