ruby-saml 1.12.2 → 1.13.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/.github/workflows/test.yml +25 -0
- data/{changelog.md → CHANGELOG.md} +19 -6
- data/README.md +288 -226
- data/UPGRADING.md +149 -0
- data/lib/onelogin/ruby-saml/authrequest.rb +2 -3
- data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +110 -77
- data/lib/onelogin/ruby-saml/logoutrequest.rb +3 -3
- data/lib/onelogin/ruby-saml/logoutresponse.rb +1 -1
- data/lib/onelogin/ruby-saml/metadata.rb +59 -22
- data/lib/onelogin/ruby-saml/response.rb +13 -15
- data/lib/onelogin/ruby-saml/saml_message.rb +6 -7
- data/lib/onelogin/ruby-saml/settings.rb +76 -68
- data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +12 -4
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +2 -2
- data/lib/onelogin/ruby-saml/utils.rb +27 -22
- data/lib/onelogin/ruby-saml/version.rb +1 -1
- data/lib/xml_security.rb +5 -7
- metadata +7 -5
- data/.travis.yml +0 -48
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ded4e8f9560644f26e90079ecf0021f81fb8fb90
|
4
|
+
data.tar.gz: 034e0d8ee8d11aa443435b20d071015dfbcf5161
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 957e2b7598309e9b770019902f28bdec07a28a19a77abfb7e72d503ab3c8b4c57138451d3bb0bced671aca4d454d6637821a3931e91e6f4d79ef4d5d1a91a25e
|
7
|
+
data.tar.gz: 74d06dcdc7ba3f3c0dc797ad3e329987f0bd32bfc5b0bdee62f9c081688dd97bb4892ef42de795c09c59b2c48487b673476a6dd12aedca0770b600c770e2c4b7
|
@@ -0,0 +1,25 @@
|
|
1
|
+
name: ruby-saml CI
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
test:
|
7
|
+
name: Unit test
|
8
|
+
strategy:
|
9
|
+
fail-fast: false
|
10
|
+
matrix:
|
11
|
+
os: [ubuntu-latest, macos-latest]
|
12
|
+
ruby-version: [2.1.9, 2.2.10, 2.3.8, 2.4.6, 2.5.8, 2.6.6, 2.7.2, 3.0.1, jruby-9.1.17.0, jruby-9.2.17.0, truffleruby]
|
13
|
+
runs-on: ${{ matrix.os }}
|
14
|
+
steps:
|
15
|
+
- uses: actions/checkout@v2
|
16
|
+
- name: Set up Ruby ${{ matrix.ruby-version }}
|
17
|
+
uses: ruby/setup-ruby@v1
|
18
|
+
with:
|
19
|
+
ruby-version: ${{ matrix.ruby-version }}
|
20
|
+
|
21
|
+
- name: Install dependencies
|
22
|
+
run: bundle install
|
23
|
+
|
24
|
+
- name: Run tests
|
25
|
+
run: bundle exec rake
|
@@ -1,9 +1,22 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
### 1.
|
4
|
-
* [
|
5
|
-
|
6
|
-
|
1
|
+
# Ruby SAML Changelog
|
2
|
+
|
3
|
+
### 1.13.0 (Sept 06, 2021)
|
4
|
+
* [#611](https://github.com/onelogin/ruby-saml/pull/601) Replace MAX_BYTE_SIZE constant with setting: message_max_bytesize
|
5
|
+
* [#605](https://github.com/onelogin/ruby-saml/pull/605) :allowed_clock_drift is now bidrectional
|
6
|
+
* [#614](https://github.com/onelogin/ruby-saml/pull/614) Support :name_id_format option for IdpMetadataParser
|
7
|
+
* [#611](https://github.com/onelogin/ruby-saml/pull/611) IdpMetadataParser should always set idp_cert_multi, even when there is only one cert
|
8
|
+
* [#610](https://github.com/onelogin/ruby-saml/pull/610) New IDP sso/slo binding params which deprecate :embed_sign
|
9
|
+
* [#602](https://github.com/onelogin/ruby-saml/pull/602) Refactor the OneLogin::RubySaml::Metadata class
|
10
|
+
* [#586](https://github.com/onelogin/ruby-saml/pull/586) Support milliseconds in cacheDuration parsing
|
11
|
+
* [#585](https://github.com/onelogin/ruby-saml/pull/585) Do not append " | " to StatusCode unnecessarily
|
12
|
+
* [#607](https://github.com/onelogin/ruby-saml/pull/607) Clean up
|
13
|
+
* Add warning about the use of IdpMetadataParser class and SSRF
|
14
|
+
* CI: Migrate from Travis to Github Actions
|
15
|
+
|
16
|
+
### 1.12.2 (Apr 08, 2021)
|
17
|
+
* [#575](https://github.com/onelogin/ruby-saml/pull/575) Fix SloLogoutresponse bug on LogoutRequest
|
18
|
+
|
19
|
+
### 1.12.1 (Apr 05, 2021)
|
7
20
|
* Fix XPath typo incompatible with Rexml 3.2.5
|
8
21
|
* Refactor GCM support
|
9
22
|
|
data/README.md
CHANGED
@@ -1,122 +1,25 @@
|
|
1
|
-
# Ruby SAML
|
1
|
+
# Ruby SAML
|
2
|
+
[![Build Status](https://github.com/onelogin/ruby-saml/actions/workflows/test.yml/badge.svg?query=branch%3Amaster)](https://github.com/onelogin/ruby-saml/actions/workflows/test.yml?query=branch%3Amaster)
|
3
|
+
[![Coverage Status](https://coveralls.io/repos/onelogin/ruby-saml/badge.svg?branch=master)](https://coveralls.io/r/onelogin/ruby-saml?branch=master)
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
-
change/adds specific error messages for signature validations
|
6
|
-
|
7
|
-
`idp_sso_target_url` and `idp_slo_target_url` attributes of the Settings class deprecated in favor of `idp_sso_service_url` and `idp_slo_service_url`.
|
8
|
-
In IDPMetadataParser, `parse`, `parse_to_hash` and `parse_to_array` methods now retrieve SSO URL and SLO URL endpoints with
|
9
|
-
`idp_sso_service_url` and `idp_slo_service_url` (previously `idp_sso_target_url` and `idp_slo_target_url` respectively).
|
10
|
-
|
11
|
-
## Updating from 1.10.x to 1.11.0
|
12
|
-
Version `1.11.0` deprecates the use of `settings.issuer` in favour of `settings.sp_entity_id`.
|
13
|
-
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.
|
14
|
-
|
15
|
-
Version `1.10.2` includes the `valid_until` attribute in parsed IdP metadata.
|
16
|
-
|
17
|
-
Version `1.10.1` improves Ruby 1.8.7 support.
|
18
|
-
|
19
|
-
## Updating from 1.9.0 to 1.10.0
|
20
|
-
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/
|
21
|
-
|
22
|
-
## Updating from 1.8.0 to 1.9.0
|
23
|
-
Version `1.9.0` better supports Ruby 2.4+ and JRuby 9.2.0.0. `Settings` initialization now has a second parameter, `keep_security_settings` (default: false), which saves security settings attributes that are not explicitly overridden, if set to true.
|
24
|
-
|
25
|
-
## Updating from 1.7.X to 1.8.0
|
26
|
-
On Version `1.8.0`, creating AuthRequests/LogoutRequests/LogoutResponses with nil RelayState param will not generate a URL with an empty RelayState parameter anymore. It also changes the invalid audience error message.
|
27
|
-
|
28
|
-
## Updating from 1.6.0 to 1.7.0
|
29
|
-
|
30
|
-
Version `1.7.0` is a recommended update for all Ruby SAML users as it includes a fix for the [CVE-2017-11428](https://www.cvedetails.com/cve/CVE-2017-11428/) vulnerability.
|
31
|
-
|
32
|
-
## Updating from 1.5.0 to 1.6.0
|
33
|
-
|
34
|
-
Version `1.6.0` changes the preferred way to construct instances of `Logoutresponse` and `SloLogoutrequest`. Previously the _SAMLResponse_, _RelayState_, and _SigAlg_ parameters of these message types were provided via the constructor's `options[:get_params]` parameter. Unfortunately this can result in incompatibility with other SAML implementations; signatures are specified to be computed based on the _sender's_ URI-encoding of the message, which can differ from that of Ruby SAML. In particular, Ruby SAML's URI-encoding does not match that of Microsoft ADFS, so messages from ADFS can fail signature validation.
|
35
|
-
|
36
|
-
The new preferred way to provide _SAMLResponse_, _RelayState_, and _SigAlg_ is via the `options[:raw_get_params]` parameter. For example:
|
37
|
-
|
38
|
-
```ruby
|
39
|
-
# In this example `query_params` is assumed to contain decoded query parameters,
|
40
|
-
# and `raw_query_params` is assumed to contain encoded query parameters as sent by the IDP.
|
41
|
-
settings = {
|
42
|
-
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
|
43
|
-
settings.soft = false
|
44
|
-
}
|
45
|
-
options = {
|
46
|
-
get_params: {
|
47
|
-
"Signature" => query_params["Signature"],
|
48
|
-
},
|
49
|
-
raw_get_params: {
|
50
|
-
"SAMLRequest" => raw_query_params["SAMLRequest"],
|
51
|
-
"SigAlg" => raw_query_params["SigAlg"],
|
52
|
-
"RelayState" => raw_query_params["RelayState"],
|
53
|
-
},
|
54
|
-
}
|
55
|
-
slo_logout_request = OneLogin::RubySaml::SloLogoutrequest.new(query_params["SAMLRequest"], settings, options)
|
56
|
-
raise "Invalid Logout Request" unless slo_logout_request.is_valid?
|
57
|
-
```
|
58
|
-
|
59
|
-
The old form is still supported for backward compatibility, but all Ruby SAML users should prefer `options[:raw_get_params]` where possible to ensure compatibility with other SAML implementations.
|
60
|
-
|
61
|
-
## Updating from 1.4.2 to 1.4.3
|
62
|
-
|
63
|
-
Version `1.4.3` introduces Recipient validation of SubjectConfirmation elements.
|
64
|
-
The 'Recipient' value is compared with the settings.assertion_consumer_service_url
|
65
|
-
value.
|
66
|
-
If you want to skip that validation, add the :skip_recipient_check option to the
|
67
|
-
initialize method of the Response object.
|
68
|
-
|
69
|
-
Parsing metadata that contains more than one certificate will propagate the
|
70
|
-
idp_cert_multi property rather than idp_cert. See [signature validation
|
71
|
-
section](#signature-validation) for details.
|
72
|
-
|
73
|
-
## Updating from 1.3.x to 1.4.X
|
74
|
-
|
75
|
-
Version `1.4.0` is a recommended update for all Ruby SAML users as it includes security improvements.
|
76
|
-
|
77
|
-
## Updating from 1.2.x to 1.3.X
|
78
|
-
|
79
|
-
Version `1.3.0` is a recommended update for all Ruby SAML users as it includes security fixes. It adds security improvements in order to prevent Signature wrapping attacks. [CVE-2016-5697](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-5697)
|
80
|
-
|
81
|
-
## Updating from 1.1.x to 1.2.X
|
82
|
-
|
83
|
-
Version `1.2` adds IDP metadata parsing improvements, uuid deprecation in favour of SecureRandom, refactor error handling and some minor improvements
|
84
|
-
|
85
|
-
There is no compatibility issue detected.
|
86
|
-
|
87
|
-
For more details, please review [the changelog](changelog.md).
|
88
|
-
|
89
|
-
## Updating from 1.0.x to 1.1.X
|
90
|
-
|
91
|
-
Version `1.1` adds some improvements on signature validation and solves some namespace conflicts.
|
92
|
-
|
93
|
-
## Updating from 0.9.x to 1.0.X
|
94
|
-
|
95
|
-
Version `1.0` is a recommended update for all Ruby SAML users as it includes security fixes.
|
96
|
-
|
97
|
-
Version `1.0` adds security improvements like entity expansion limitation, more SAML message validations, and other important improvements like decrypt support.
|
98
|
-
|
99
|
-
### Important Changes
|
100
|
-
Please note the `get_idp_metadata` method raises an exception when it is not able to fetch the idp metadata, so review your integration if you are using this functionality.
|
101
|
-
|
102
|
-
## Updating from 0.8.x to 0.9.x
|
103
|
-
Version `0.9` adds many new features and improvements.
|
104
|
-
|
105
|
-
## Updating from 0.7.x to 0.8.x
|
106
|
-
Version `0.8.x` changes the namespace of the gem from `OneLogin::Saml` to `OneLogin::RubySaml`. Please update your implementations of the gem accordingly.
|
5
|
+
Ruby SAML minor and tiny versions may introduce breaking changes. Please read
|
6
|
+
[UPGRADING.md](UPGRADING.md) for guidance on upgrading to new Ruby SAML versions.
|
107
7
|
|
108
8
|
## Overview
|
109
9
|
|
110
|
-
The Ruby SAML library is for implementing the client side of a SAML authorization,
|
10
|
+
The Ruby SAML library is for implementing the client side of a SAML authorization,
|
11
|
+
i.e. it provides a means for managing authorization initialization and confirmation
|
12
|
+
requests from identity providers.
|
111
13
|
|
112
14
|
SAML authorization is a two step process and you are expected to implement support for both.
|
113
15
|
|
114
|
-
We created a demo project for
|
16
|
+
We created a demo project for Rails 4 that uses the latest version of this library:
|
17
|
+
[ruby-saml-example](https://github.com/onelogin/ruby-saml-example)
|
18
|
+
|
19
|
+
### Supported Ruby Versions
|
20
|
+
|
21
|
+
The following Ruby versions are covered by CI testing:
|
115
22
|
|
116
|
-
### Supported versions of Ruby
|
117
|
-
* 1.8.7
|
118
|
-
* 1.9.x
|
119
|
-
* 2.0.x
|
120
23
|
* 2.1.x
|
121
24
|
* 2.2.x
|
122
25
|
* 2.3.x
|
@@ -125,24 +28,34 @@ We created a demo project for Rails4 that uses the latest version of this librar
|
|
125
28
|
* 2.6.x
|
126
29
|
* 2.7.x
|
127
30
|
* 3.0.x
|
128
|
-
* JRuby 1.7.x
|
129
|
-
* JRuby 9.0.x
|
130
31
|
* JRuby 9.1.x
|
131
32
|
* JRuby 9.2.x
|
33
|
+
* TruffleRuby (latest)
|
34
|
+
|
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
|
132
42
|
|
133
43
|
## Adding Features, Pull Requests
|
44
|
+
|
134
45
|
* Fork the repository
|
135
46
|
* Make your feature addition or bug fix
|
136
47
|
* Add tests for your new features. This is important so we don't break any features in a future version unintentionally.
|
137
|
-
* Ensure all tests pass
|
48
|
+
* Ensure all tests pass by running `bundle exec rake test`.
|
138
49
|
* Do not change rakefile, version, or history.
|
139
50
|
* Open a pull request, following [this template](https://gist.github.com/Lordnibbler/11002759).
|
140
51
|
|
141
52
|
## Security Guidelines
|
142
53
|
|
143
|
-
If you believe you have discovered a security vulnerability in this gem, please report it
|
54
|
+
If you believe you have discovered a security vulnerability in this gem, please report it
|
55
|
+
at https://www.onelogin.com/security with a description. We follow responsible disclosure
|
56
|
+
guidelines, and will work with you to quickly find a resolution.
|
144
57
|
|
145
|
-
### Security
|
58
|
+
### Security Warning
|
146
59
|
|
147
60
|
Some tools may incorrectly report ruby-saml is a potential security vulnerability.
|
148
61
|
ruby-saml depends on Nokogiri, and it's possible to use Nokogiri in a dangerous way
|
@@ -152,9 +65,20 @@ can create an XML External Entity (XXE) vulnerability if the XML data is not tru
|
|
152
65
|
However, ruby-saml never enables this dangerous Nokogiri configuration;
|
153
66
|
ruby-saml never enables DTDLOAD, and it never disables NONET.
|
154
67
|
|
68
|
+
The OneLogin::RubySaml::IdpMetadataParser class does not validate in any way the URL
|
69
|
+
that is introduced in order to be parsed.
|
70
|
+
|
71
|
+
Usually the same administrator that handles the Service Provider also sets the URL to
|
72
|
+
the IdP, which should be a trusted resource.
|
73
|
+
|
74
|
+
But there are other scenarios, like a SAAS app where the administrator of the app
|
75
|
+
delegates this functionality to other users. In this case, extra precaution should
|
76
|
+
be taken in order to validate such URL inputs and avoid attacks like SSRF.
|
155
77
|
|
156
78
|
## Getting Started
|
157
|
-
|
79
|
+
|
80
|
+
In order to use Ruby SAML you will need to install the gem (either manually or using Bundler),
|
81
|
+
and require the library in your Ruby application:
|
158
82
|
|
159
83
|
Using `Gemfile`
|
160
84
|
|
@@ -172,7 +96,8 @@ Using RubyGems
|
|
172
96
|
gem install ruby-saml
|
173
97
|
```
|
174
98
|
|
175
|
-
|
99
|
+
You may require the entire Ruby SAML gem:
|
100
|
+
|
176
101
|
```ruby
|
177
102
|
require 'onelogin/ruby-saml'
|
178
103
|
```
|
@@ -185,7 +110,9 @@ require 'onelogin/ruby-saml/authrequest'
|
|
185
110
|
|
186
111
|
### Installation on Ruby 1.8.7
|
187
112
|
|
188
|
-
This gem uses Nokogiri as a dependency, which dropped support for Ruby 1.8.x in Nokogiri 1.6.
|
113
|
+
This gem uses Nokogiri as a dependency, which dropped support for Ruby 1.8.x in Nokogiri 1.6.
|
114
|
+
When installing this gem on Ruby 1.8.7, you will need to make sure a version of Nokogiri
|
115
|
+
prior to 1.6 is installed or specified if it hasn't been already.
|
189
116
|
|
190
117
|
Using `Gemfile`
|
191
118
|
|
@@ -214,7 +141,10 @@ OneLogin::RubySaml::Logging.logger = Logger.new('/var/log/ruby-saml.log')
|
|
214
141
|
|
215
142
|
## The Initialization Phase
|
216
143
|
|
217
|
-
This is the first request you will get from the identity provider. It will hit your application
|
144
|
+
This is the first request you will get from the identity provider. It will hit your application
|
145
|
+
at a specific URL that you've announced as your SAML initialization point. The response to
|
146
|
+
this initialization is a redirect back to the identity provider, which can look something
|
147
|
+
like this (ignore the saml_settings method call for now):
|
218
148
|
|
219
149
|
```ruby
|
220
150
|
def init
|
@@ -234,7 +164,10 @@ def init
|
|
234
164
|
end
|
235
165
|
```
|
236
166
|
|
237
|
-
Once you've redirected back to the identity provider, it will ensure that the user has been
|
167
|
+
Once you've redirected back to the identity provider, it will ensure that the user has been
|
168
|
+
authorized and redirect back to your application for final consumption.
|
169
|
+
This can look something like this (the `authorize_success` and `authorize_failure`
|
170
|
+
methods are specific to your application):
|
238
171
|
|
239
172
|
```ruby
|
240
173
|
def consume
|
@@ -252,16 +185,19 @@ def consume
|
|
252
185
|
end
|
253
186
|
```
|
254
187
|
|
255
|
-
In the above there are a few assumptions, one being that `response.nameid` is an email address.
|
188
|
+
In the above there are a few assumptions, one being that `response.nameid` is an email address.
|
189
|
+
This is all handled with how you specify the settings that are in play via the `saml_settings` method.
|
190
|
+
That could be implemented along the lines of this:
|
256
191
|
|
257
192
|
```
|
258
193
|
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
|
259
194
|
response.settings = saml_settings
|
260
195
|
```
|
261
196
|
|
262
|
-
If the assertion of the SAMLResponse is not encrypted, you can initialize the Response
|
263
|
-
|
264
|
-
|
197
|
+
If the assertion of the SAMLResponse is not encrypted, you can initialize the Response
|
198
|
+
without the `:settings` parameter and set it later. If the SAMLResponse contains an encrypted
|
199
|
+
assertion, you need to provide the settings in the initialize method in order to obtain the
|
200
|
+
decrypted assertion, using the service provider private key in order to decrypt.
|
265
201
|
If you don't know what expect, always use the former (set the settings on initialize).
|
266
202
|
|
267
203
|
```ruby
|
@@ -271,8 +207,10 @@ def saml_settings
|
|
271
207
|
settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
|
272
208
|
settings.sp_entity_id = "http://#{request.host}/saml/metadata"
|
273
209
|
settings.idp_entity_id = "https://app.onelogin.com/saml/metadata/#{OneLoginAppId}"
|
274
|
-
settings.idp_sso_service_url
|
275
|
-
settings.
|
210
|
+
settings.idp_sso_service_url = "https://app.onelogin.com/trust/saml2/http-post/sso/#{OneLoginAppId}"
|
211
|
+
settings.idp_sso_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" # or :post, :redirect
|
212
|
+
settings.idp_slo_service_url = "https://app.onelogin.com/trust/saml2/http-redirect/slo/#{OneLoginAppId}"
|
213
|
+
settings.idp_slo_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" # or :post, :redirect
|
276
214
|
settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
|
277
215
|
settings.idp_cert_fingerprint_algorithm = "http://www.w3.org/2000/09/xmldsig#sha1"
|
278
216
|
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
@@ -285,9 +223,9 @@ def saml_settings
|
|
285
223
|
"urn:oasis:names:tc:SAML:2.0:ac:classes:Password"
|
286
224
|
]
|
287
225
|
|
288
|
-
# Optional bindings (defaults to Redirect for logout POST for
|
289
|
-
settings.single_logout_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
|
290
|
-
settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
|
226
|
+
# Optional bindings (defaults to Redirect for logout POST for ACS)
|
227
|
+
settings.single_logout_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" # or :post, :redirect
|
228
|
+
settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" # or :post, :redirect
|
291
229
|
|
292
230
|
settings
|
293
231
|
end
|
@@ -295,17 +233,20 @@ end
|
|
295
233
|
|
296
234
|
The use of settings.issuer is deprecated in favour of settings.sp_entity_id since version 1.11.0
|
297
235
|
|
298
|
-
Some assertion validations can be skipped by passing parameters to `OneLogin::RubySaml::Response.new()`.
|
236
|
+
Some assertion validations can be skipped by passing parameters to `OneLogin::RubySaml::Response.new()`.
|
237
|
+
For example, you can skip the `AuthnStatement`, `Conditions`, `Recipient`, or the `SubjectConfirmation`
|
238
|
+
validations by initializing the response with different options:
|
299
239
|
|
300
240
|
```ruby
|
301
241
|
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_authnstatement: true}) # skips AuthnStatement
|
302
242
|
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_conditions: true}) # skips conditions
|
303
243
|
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_subject_confirmation: true}) # skips subject confirmation
|
304
|
-
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_recipient_check: true}) #
|
244
|
+
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_recipient_check: true}) # doesn't skip subject confirmation, but skips the recipient check which is a sub check of the subject_confirmation check
|
305
245
|
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_audience: true}) # skips audience check
|
306
246
|
```
|
307
247
|
|
308
|
-
All that's left is to wrap everything in a controller and reference it in the initialization and
|
248
|
+
All that's left is to wrap everything in a controller and reference it in the initialization and
|
249
|
+
consumption URLs in OneLogin. A full controller example could look like this:
|
309
250
|
|
310
251
|
```ruby
|
311
252
|
# This controller expects you to use the URLs /saml/init and /saml/consume in your OneLogin application.
|
@@ -358,44 +299,56 @@ class SamlController < ApplicationController
|
|
358
299
|
end
|
359
300
|
```
|
360
301
|
|
302
|
+
## Signature Validation
|
303
|
+
|
304
|
+
Ruby SAML allows different ways to validate the signature of the SAMLResponse:
|
305
|
+
- You can provide the IdP X.509 public certificate at the `idp_cert` setting.
|
306
|
+
- You can provide the IdP X.509 public certificate in fingerprint format using the
|
307
|
+
`idp_cert_fingerprint` setting parameter and additionally the `idp_cert_fingerprint_algorithm` parameter.
|
361
308
|
|
362
|
-
|
309
|
+
When validating the signature of redirect binding, the fingerprint is useless and the certificate
|
310
|
+
of the IdP is required in order to execute the validation. You can pass the option
|
311
|
+
`:relax_signature_validation` to `SloLogoutrequest` and `Logoutresponse` if want to avoid signature
|
312
|
+
validation if no certificate of the IdP is provided.
|
363
313
|
|
364
|
-
|
365
|
-
|
366
|
-
|
314
|
+
In production also we highly recommend to register on the settings the IdP certificate instead
|
315
|
+
of using the fingerprint method. The fingerprint, is a hash, so at the end is open to a collision
|
316
|
+
attack that can end on a signature validation bypass. Other SAML toolkits deprecated that mechanism,
|
317
|
+
we maintain it for compatibility and also to be used on test environment.
|
367
318
|
|
368
|
-
|
369
|
-
You can pass the option :relax_signature_validation to SloLogoutrequest and Logoutresponse if want to avoid signature validation if no certificate of the IdP is provided.
|
319
|
+
## Handling Multiple IdP Certificates
|
370
320
|
|
371
|
-
|
321
|
+
If the IdP metadata XML includes multiple certificates, you may specify the `idp_cert_multi`
|
322
|
+
parameter. When used, the `idp_cert` and `idp_cert_fingerprint` parameters are ignored.
|
323
|
+
This is useful in the following scenarios:
|
372
324
|
|
373
|
-
|
325
|
+
* The IdP uses different certificates for signing versus encryption.
|
326
|
+
* The IdP is undergoing a key rollover and is publishing the old and new certificates in parallel.
|
374
327
|
|
375
|
-
|
376
|
-
|
328
|
+
The `idp_cert_multi` must be a `Hash` as follows. The `:signing` and `:encryption` arrays below,
|
329
|
+
add the IdP X.509 public certificates which were published in the IdP metadata.
|
377
330
|
|
378
|
-
|
331
|
+
```ruby
|
379
332
|
{
|
380
333
|
:signing => [],
|
381
334
|
:encryption => []
|
382
335
|
}
|
383
|
-
|
384
|
-
And on 'signing' and 'encryption' arrays, add the different IdP x509 public certificates published on the IdP metadata.
|
385
|
-
|
336
|
+
```
|
386
337
|
|
387
338
|
## Metadata Based Configuration
|
388
339
|
|
389
|
-
The method above requires a little extra work to manually specify attributes about
|
390
|
-
|
340
|
+
The method above requires a little extra work to manually specify attributes about both the IdP and your SP application.
|
341
|
+
There's an easier method: use a metadata exchange. Metadata is an XML file that defines the capabilities of both the IdP
|
342
|
+
and the SP application. It also contains the X.509 public key certificates which add to the trusted relationship.
|
343
|
+
The IdP administrator can also configure custom settings for an SP based on the metadata.
|
391
344
|
|
392
|
-
Using
|
345
|
+
Using `IdpMetadataParser#parse_remote`, the IdP metadata will be added to the settings.
|
393
346
|
|
394
347
|
```ruby
|
395
348
|
def saml_settings
|
396
349
|
|
397
350
|
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
|
398
|
-
# Returns OneLogin::RubySaml::Settings
|
351
|
+
# Returns OneLogin::RubySaml::Settings pre-populated with IdP metadata
|
399
352
|
settings = idp_metadata_parser.parse_remote("https://example.com/auth/saml2/idp/metadata")
|
400
353
|
|
401
354
|
settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
|
@@ -407,6 +360,7 @@ def saml_settings
|
|
407
360
|
settings
|
408
361
|
end
|
409
362
|
```
|
363
|
+
|
410
364
|
The following attributes are set:
|
411
365
|
* idp_entity_id
|
412
366
|
* name_identifier_format
|
@@ -425,11 +379,11 @@ IdpMetadataParser by its Entity Id value:
|
|
425
379
|
|
426
380
|
```ruby
|
427
381
|
validate_cert = true
|
428
|
-
settings =
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
382
|
+
settings = idp_metadata_parser.parse_remote(
|
383
|
+
"https://example.com/auth/saml2/idp/metadata",
|
384
|
+
validate_cert,
|
385
|
+
entity_id: "http//example.com/target/entity"
|
386
|
+
)
|
433
387
|
```
|
434
388
|
|
435
389
|
### Parsing Metadata into an Hash
|
@@ -444,7 +398,7 @@ If you are using `saml:AttributeStatement` to transfer data like the username, y
|
|
444
398
|
`single_value_compatibility` (when activated, only the first value is returned)
|
445
399
|
|
446
400
|
```ruby
|
447
|
-
response
|
401
|
+
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
|
448
402
|
response.settings = saml_settings
|
449
403
|
|
450
404
|
response.attributes[:username]
|
@@ -529,7 +483,7 @@ pp(response.attributes.multi(:not_exists))
|
|
529
483
|
pp(response.attributes.fetch(/givenname/))
|
530
484
|
# => "usersName"
|
531
485
|
|
532
|
-
#
|
486
|
+
# Deprecated single_value_compatibility
|
533
487
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
534
488
|
|
535
489
|
pp(response.attributes[:uid])
|
@@ -572,74 +526,170 @@ To add a `saml:AuthnContextDeclRef`, define `settings.authn_context_decl_ref`.
|
|
572
526
|
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
|
573
527
|
building the authrequest object.
|
574
528
|
|
529
|
+
## Service Provider Metadata
|
575
530
|
|
576
|
-
|
531
|
+
To form a trusted pair relationship with the IdP, the SP (you) need to provide metadata XML
|
532
|
+
to the IdP for various good reasons. (Caching, certificate lookups, relaying party permissions, etc)
|
577
533
|
|
578
|
-
The
|
534
|
+
The class `OneLogin::RubySaml::Metadata` takes care of this by reading the Settings and returning XML. All you have to do is add a controller to return the data, then give this URL to the IdP administrator.
|
579
535
|
|
580
|
-
|
536
|
+
The metadata will be polled by the IdP every few minutes, so updating your settings should propagate
|
537
|
+
to the IdP settings.
|
581
538
|
|
582
539
|
```ruby
|
583
|
-
|
584
|
-
|
540
|
+
class SamlController < ApplicationController
|
541
|
+
# ... the rest of your controller definitions ...
|
542
|
+
def metadata
|
543
|
+
settings = Account.get_saml_settings
|
544
|
+
meta = OneLogin::RubySaml::Metadata.new
|
545
|
+
render :xml => meta.generate(settings), :content_type => "application/samlmetadata+xml"
|
546
|
+
end
|
547
|
+
end
|
585
548
|
```
|
586
549
|
|
587
|
-
|
550
|
+
You can add `ValidUntil` and `CacheDuration` to the SP Metadata XML using instead:
|
551
|
+
|
552
|
+
```ruby
|
553
|
+
# Valid until => 2 days from now
|
554
|
+
# Cache duration = 604800s = 1 week
|
555
|
+
valid_until = Time.now + 172800
|
556
|
+
cache_duration = 604800
|
557
|
+
meta.generate(settings, false, valid_until, cache_duration)
|
558
|
+
```
|
559
|
+
|
560
|
+
## Signing and Decryption
|
561
|
+
|
562
|
+
Ruby SAML supports the following functionality:
|
563
|
+
|
564
|
+
1. Signing your SP Metadata XML
|
565
|
+
2. Signing your SP SAML messages
|
566
|
+
3. Decrypting IdP Assertion messages upon receipt (EncryptedAssertion)
|
567
|
+
4. Verifying signatures on SAML messages and IdP Assertions
|
568
|
+
|
569
|
+
In order to use functions 1-3 above, you must first define your SP public certificate and private key:
|
588
570
|
|
589
571
|
```ruby
|
590
|
-
settings.
|
591
|
-
settings.
|
592
|
-
|
593
|
-
settings.security[:want_assertions_signed] = true # Enable or not the requirement of signed assertion
|
594
|
-
settings.security[:metadata_signed] = true # Enable or not signature on Metadata
|
572
|
+
settings.certificate = "CERTIFICATE TEXT WITH BEGIN/END HEADER AND FOOTER"
|
573
|
+
settings.private_key = "PRIVATE KEY TEXT WITH BEGIN/END HEADER AND FOOTER"
|
574
|
+
```
|
595
575
|
|
576
|
+
Note that the same certificate (and its associated private key) are used to perform
|
577
|
+
all decryption and signing-related functions (1-4) above. Ruby SAML does not currently allow
|
578
|
+
to specify different certificates for each function.
|
579
|
+
|
580
|
+
You may also globally set the SP signature and digest method, to be used in SP signing (functions 1 and 2 above):
|
581
|
+
|
582
|
+
```ruby
|
596
583
|
settings.security[:digest_method] = XMLSecurity::Document::SHA1
|
597
584
|
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
|
585
|
+
```
|
586
|
+
|
587
|
+
#### Signing SP Metadata
|
588
|
+
|
589
|
+
You may add a `<ds:Signature>` digital signature element to your SP Metadata XML using the following setting:
|
590
|
+
|
591
|
+
```ruby
|
592
|
+
settings.certificate = "CERTIFICATE TEXT WITH BEGIN/END HEADER AND FOOTER"
|
593
|
+
settings.private_key = "PRIVATE KEY TEXT WITH BEGIN/END HEADER AND FOOTER"
|
598
594
|
|
599
|
-
|
600
|
-
# Note that metadata signature is always embedded regardless of this value.
|
601
|
-
settings.security[:embed_sign] = false
|
602
|
-
settings.security[:check_idp_cert_expiration] = false # Enable or not IdP x509 cert expiration check
|
603
|
-
settings.security[:check_sp_cert_expiration] = false # Enable or not SP x509 cert expiration check
|
595
|
+
settings.security[:metadata_signed] = true # Enable signature on Metadata
|
604
596
|
```
|
605
597
|
|
606
|
-
|
598
|
+
#### Signing SP SAML Messages
|
599
|
+
|
600
|
+
Ruby SAML supports SAML request signing. The Service Provider will sign the
|
601
|
+
request/responses with its private key. The Identity Provider will then validate the signature
|
602
|
+
of the received request/responses with the public X.509 cert of the Service Provider.
|
603
|
+
|
604
|
+
To enable, please first set your certificate and private key. This will add `<md:KeyDescriptor use="signing">`
|
605
|
+
to your SP Metadata XML, to be read by the IdP.
|
606
|
+
|
607
|
+
```ruby
|
608
|
+
settings.certificate = "CERTIFICATE TEXT WITH BEGIN/END HEADER AND FOOTER"
|
609
|
+
settings.private_key = "PRIVATE KEY TEXT WITH BEGIN/END HEADER AND FOOTER"
|
610
|
+
```
|
611
|
+
|
612
|
+
Next, you may specify the specific SP SAML messages you would like to sign:
|
613
|
+
|
614
|
+
```ruby
|
615
|
+
settings.security[:authn_requests_signed] = true # Enable signature on AuthNRequest
|
616
|
+
settings.security[:logout_requests_signed] = true # Enable signature on Logout Request
|
617
|
+
settings.security[:logout_responses_signed] = true # Enable signature on Logout Response
|
618
|
+
```
|
619
|
+
|
620
|
+
Signatures will be handled automatically for both `HTTP-Redirect` and `HTTP-Redirect` Binding.
|
621
|
+
Note that the RelayState parameter is used when creating the Signature on the `HTTP-Redirect` Binding.
|
607
622
|
Remember to provide it to the Signature builder if you are sending a `GET RelayState` parameter or the
|
608
623
|
signature validation process will fail at the Identity Provider.
|
609
624
|
|
610
|
-
|
611
|
-
|
612
|
-
|
625
|
+
#### Decrypting IdP SAML Assertions
|
626
|
+
|
627
|
+
Ruby SAML supports EncryptedAssertion. The Identity Provider will encrypt the Assertion with the
|
628
|
+
public cert of the Service Provider. The Service Provider will decrypt the EncryptedAssertion with its private key.
|
613
629
|
|
614
|
-
|
630
|
+
You may enable EncryptedAssertion as follows. This will add `<md:KeyDescriptor use="encrytion">` to your
|
631
|
+
SP Metadata XML, to be read by the IdP.
|
615
632
|
|
616
|
-
|
633
|
+
```ruby
|
634
|
+
settings.certificate = "CERTIFICATE TEXT WITH BEGIN/END HEADER AND FOOTER"
|
635
|
+
settings.private_key = "PRIVATE KEY TEXT WITH BEGIN/END HEADER AND FOOTER"
|
636
|
+
|
637
|
+
settings.security[:want_assertions_encrypted] = true # Invalidate SAML messages without an EncryptedAssertion
|
638
|
+
```
|
617
639
|
|
618
|
-
|
640
|
+
#### Verifying Signature on IdP Assertions
|
619
641
|
|
620
|
-
|
642
|
+
You may require the IdP to sign its SAML Assertions using the following setting.
|
643
|
+
With will add `<md:SPSSODescriptor WantAssertionsSigned="true">` to your SP Metadata XML.
|
644
|
+
The signature will be checked against the `<md:KeyDescriptor use="signing">` element
|
645
|
+
present in the IdP's metadata.
|
646
|
+
|
647
|
+
```ruby
|
648
|
+
settings.security[:want_assertions_signed] = true # Require the IdP to sign its SAML Assertions
|
649
|
+
```
|
650
|
+
|
651
|
+
#### Certificate and Signature Validation
|
652
|
+
|
653
|
+
You may require SP and IdP certificates to be non-expired using the following settings:
|
654
|
+
|
655
|
+
```ruby
|
656
|
+
settings.security[:check_idp_cert_expiration] = true # Raise error if IdP X.509 cert is expired
|
657
|
+
settings.security[:check_sp_cert_expiration] = true # Raise error SP X.509 cert is expired
|
658
|
+
```
|
621
659
|
|
622
|
-
|
660
|
+
By default, Ruby SAML will raise a `OneLogin::RubySaml::ValidationError` if a signature or certificate
|
661
|
+
validation fails. You may disable such exceptions using the `settings.security[:soft]` parameter.
|
623
662
|
|
624
663
|
```ruby
|
625
|
-
settings.
|
626
|
-
settings.private_key = "PRIVATE KEY TEXT WITH HEAD AND FOOT"
|
664
|
+
settings.security[:soft] = true # Do not raise error on failed signature/certificate validations
|
627
665
|
```
|
628
666
|
|
629
|
-
|
630
|
-
The Service Provider will decrypt the EncryptedAssertion with its private key.
|
667
|
+
#### Key Rollover
|
631
668
|
|
632
|
-
|
669
|
+
To update the SP X.509 certificate and private key without disruption of service, you may define the parameter
|
670
|
+
`settings.certificate_new`. This will publish the new SP certificate in your metadata so that your IdP counterparties
|
671
|
+
may cache it in preparation for rollover.
|
633
672
|
|
673
|
+
For example, if you to rollover from `CERT A` to `CERT B`. Before rollover, your settings should look as follows.
|
674
|
+
Both `CERT A` and `CERT B` will now appear in your SP metadata, however `CERT A` will still be used for signing
|
675
|
+
and encryption at this time.
|
634
676
|
|
635
|
-
|
677
|
+
```ruby
|
678
|
+
settings.certificate = "CERT A"
|
679
|
+
settings.private_key = "PRIVATE KEY FOR CERT A"
|
680
|
+
settings.certificate_new = "CERT B"
|
681
|
+
```
|
636
682
|
|
637
|
-
|
683
|
+
After the IdP has cached `CERT B`, you may then change your settings as follows:
|
638
684
|
|
685
|
+
```ruby
|
686
|
+
settings.certificate = "CERT B"
|
687
|
+
settings.private_key = "PRIVATE KEY FOR CERT B"
|
688
|
+
```
|
639
689
|
|
640
690
|
## Single Log Out
|
641
691
|
|
642
|
-
|
692
|
+
Ruby SAML supports SP-initiated Single Logout and IdP-Initiated Single Logout.
|
643
693
|
|
644
694
|
Here is an example that we could add to our previous controller to generate and send a SAML Logout Request to the IdP:
|
645
695
|
|
@@ -654,7 +704,7 @@ def sp_logout_request
|
|
654
704
|
delete_session
|
655
705
|
else
|
656
706
|
|
657
|
-
logout_request = OneLogin::RubySaml::Logoutrequest.new
|
707
|
+
logout_request = OneLogin::RubySaml::Logoutrequest.new
|
658
708
|
logger.info "New SP SLO for userid '#{session[:userid]}' transactionid '#{logout_request.uuid}'"
|
659
709
|
|
660
710
|
if settings.name_identifier_value.nil?
|
@@ -670,7 +720,7 @@ def sp_logout_request
|
|
670
720
|
session[:transaction_id] = logout_request.uuid
|
671
721
|
session[:logged_out_user] = logged_user
|
672
722
|
|
673
|
-
relayState =
|
723
|
+
relayState = url_for(controller: 'saml', action: 'index')
|
674
724
|
redirect_to(logout_request.create(settings, :RelayState => relayState))
|
675
725
|
end
|
676
726
|
end
|
@@ -752,38 +802,6 @@ def logout
|
|
752
802
|
end
|
753
803
|
```
|
754
804
|
|
755
|
-
|
756
|
-
|
757
|
-
## Service Provider Metadata
|
758
|
-
|
759
|
-
To form a trusted pair relationship with the IdP, the SP (you) need to provide metadata XML
|
760
|
-
to the IdP for various good reasons. (Caching, certificate lookups, relaying party permissions, etc)
|
761
|
-
|
762
|
-
The class `OneLogin::RubySaml::Metadata` takes care of this by reading the Settings and returning XML. All you have to do is add a controller to return the data, then give this URL to the IdP administrator.
|
763
|
-
|
764
|
-
The metadata will be polled by the IdP every few minutes, so updating your settings should propagate
|
765
|
-
to the IdP settings.
|
766
|
-
|
767
|
-
```ruby
|
768
|
-
class SamlController < ApplicationController
|
769
|
-
# ... the rest of your controller definitions ...
|
770
|
-
def metadata
|
771
|
-
settings = Account.get_saml_settings
|
772
|
-
meta = OneLogin::RubySaml::Metadata.new
|
773
|
-
render :xml => meta.generate(settings), :content_type => "application/samlmetadata+xml"
|
774
|
-
end
|
775
|
-
end
|
776
|
-
```
|
777
|
-
|
778
|
-
You can add ValidUntil and CacheDuration to the XML Metadata using instead
|
779
|
-
```ruby
|
780
|
-
# Valid until => 2 days from now
|
781
|
-
# Cache duration = 604800s = 1 week
|
782
|
-
valid_until = Time.now + 172800
|
783
|
-
cache_duration = 604800
|
784
|
-
meta.generate(settings, false, valid_until, cache_duration)
|
785
|
-
```
|
786
|
-
|
787
805
|
## Clock Drift
|
788
806
|
|
789
807
|
Server clocks tend to drift naturally. If during validation of the response you get the error "Current time is earlier than NotBefore condition", this may be due to clock differences between your system and that of the Identity Provider.
|
@@ -798,13 +816,33 @@ response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], :allowed_cloc
|
|
798
816
|
|
799
817
|
Make sure to keep the value as comfortably small as possible to keep security risks to a minimum.
|
800
818
|
|
819
|
+
## Deflation Limit
|
820
|
+
|
821
|
+
To protect against decompression bombs (a form of DoS attack), SAML messages are limited to 250,000 bytes by default.
|
822
|
+
Sometimes legitimate SAML messages will exceed this limit,
|
823
|
+
for example due to custom claims like including groups a user is a member of.
|
824
|
+
If you want to customize this limit, you need to provide a different setting when initializing the response object.
|
825
|
+
Example:
|
826
|
+
|
827
|
+
```ruby
|
828
|
+
def consume
|
829
|
+
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], { settings: saml_settings })
|
830
|
+
...
|
831
|
+
end
|
832
|
+
|
833
|
+
private
|
834
|
+
|
835
|
+
def saml_settings
|
836
|
+
OneLogin::RubySaml::Settings.new(message_max_bytesize: 500_000)
|
837
|
+
end
|
838
|
+
```
|
839
|
+
|
801
840
|
## Attribute Service
|
802
841
|
|
803
842
|
To request attributes from the IdP the SP needs to provide an attribute service within it's metadata and reference the index in the assertion.
|
804
843
|
|
805
844
|
```ruby
|
806
845
|
settings = OneLogin::RubySaml::Settings.new
|
807
|
-
|
808
846
|
settings.attributes_index = 5
|
809
847
|
settings.attribute_consuming_service.configure do
|
810
848
|
service_name "Service"
|
@@ -815,3 +853,27 @@ end
|
|
815
853
|
```
|
816
854
|
|
817
855
|
The `attribute_value` option additionally accepts an array of possible values.
|
856
|
+
|
857
|
+
## Custom Metadata Fields
|
858
|
+
|
859
|
+
Some IdPs may require to add SPs to add additional fields (Organization, ContactPerson, etc.)
|
860
|
+
into the SP metadata. This can be achieved by extending the `OneLogin::RubySaml::Metadata`
|
861
|
+
class and overriding the `#add_extras` method as per the following example:
|
862
|
+
|
863
|
+
```ruby
|
864
|
+
class MyMetadata < OneLogin::RubySaml::Metadata
|
865
|
+
def add_extras(root, _settings)
|
866
|
+
org = root.add_element("md:Organization")
|
867
|
+
org.add_element("md:OrganizationName", 'xml:lang' => "en-US").text = 'ACME Inc.'
|
868
|
+
org.add_element("md:OrganizationDisplayName", 'xml:lang' => "en-US").text = 'ACME'
|
869
|
+
org.add_element("md:OrganizationURL", 'xml:lang' => "en-US").text = 'https://www.acme.com'
|
870
|
+
|
871
|
+
cp = root.add_element("md:ContactPerson", 'contactType' => 'technical')
|
872
|
+
cp.add_element("md:GivenName").text = 'ACME SAML Team'
|
873
|
+
cp.add_element("md:EmailAddress").text = 'saml@acme.com'
|
874
|
+
end
|
875
|
+
end
|
876
|
+
|
877
|
+
# Output XML with custom metadata
|
878
|
+
MyMetadata.new.generate(settings)
|
879
|
+
```
|