ruby-saml 1.13.0 → 1.14.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: ded4e8f9560644f26e90079ecf0021f81fb8fb90
4
- data.tar.gz: 034e0d8ee8d11aa443435b20d071015dfbcf5161
2
+ SHA256:
3
+ metadata.gz: 045af020626d4568d7d509d0c076f826de5cd57a729816676bd1624980714000
4
+ data.tar.gz: 74c3609fc7882d772aca5dc47b8baa7edbdae0ecdcfb9ca9502ad0b743b7ac8e
5
5
  SHA512:
6
- metadata.gz: 957e2b7598309e9b770019902f28bdec07a28a19a77abfb7e72d503ab3c8b4c57138451d3bb0bced671aca4d454d6637821a3931e91e6f4d79ef4d5d1a91a25e
7
- data.tar.gz: 74d06dcdc7ba3f3c0dc797ad3e329987f0bd32bfc5b0bdee62f9c081688dd97bb4892ef42de795c09c59b2c48487b673476a6dd12aedca0770b600c770e2c4b7
6
+ metadata.gz: eddf856fb0e603d48046306999ad9f0574dfc5155068e047cb0eba3e2a2afbdc120a55ca975382d48093d39be54427ff4e961cacdca7e6fc4b5ab62bfd69c657
7
+ data.tar.gz: e0b5449786268ea5a058f607d7ad44c6c96856a99c4e027b7c9db7671ebda83e9304c1438b49b030cc3030af3d24d33b0f242ce06ca4963a5a42e95e441982c2
@@ -9,7 +9,7 @@ jobs:
9
9
  fail-fast: false
10
10
  matrix:
11
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]
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, '3.1', jruby-9.1.17.0, jruby-9.2.17.0, truffleruby]
13
13
  runs-on: ${{ matrix.os }}
14
14
  steps:
15
15
  - uses: actions/checkout@v2
data/CHANGELOG.md CHANGED
@@ -1,4 +1,9 @@
1
1
  # Ruby SAML Changelog
2
+ ### 1.14.0 (Feb 01, 2022)
3
+ * [#627](https://github.com/onelogin/ruby-saml/pull/627) Support escape downcasing for validating SLO Signatures of ADFS/Azure
4
+ * [#633](https://github.com/onelogin/ruby-saml/pull/633) Support ability to change ID prefix
5
+ * Make the uuid editable on the SAML Messages generated by the toolkit
6
+ * [#622](https://github.com/onelogin/ruby-saml/pull/622) Add security setting to more strictly enforce audience validation
2
7
 
3
8
  ### 1.13.0 (Sept 06, 2021)
4
9
  * [#611](https://github.com/onelogin/ruby-saml/pull/601) Replace MAX_BYTE_SIZE constant with setting: message_max_bytesize
data/README.md CHANGED
@@ -66,7 +66,7 @@ However, ruby-saml never enables this dangerous Nokogiri configuration;
66
66
  ruby-saml never enables DTDLOAD, and it never disables NONET.
67
67
 
68
68
  The OneLogin::RubySaml::IdpMetadataParser class does not validate in any way the URL
69
- that is introduced in order to be parsed.
69
+ that is introduced in order to be parsed.
70
70
 
71
71
  Usually the same administrator that handles the Service Provider also sets the URL to
72
72
  the IdP, which should be a trusted resource.
@@ -664,6 +664,29 @@ validation fails. You may disable such exceptions using the `settings.security[:
664
664
  settings.security[:soft] = true # Do not raise error on failed signature/certificate validations
665
665
  ```
666
666
 
667
+ #### Audience Validation
668
+
669
+ A service provider should only consider a SAML response valid if the IdP includes an <AudienceRestriction>
670
+ element containting an <Audience> element that uniquely identifies the service provider. Unless you specify
671
+ the `skip_audience` option, Ruby SAML will validate that each SAML response includes an <Audience> element
672
+ whose contents matches `settings.sp_entity_id`.
673
+
674
+ By default, Ruby SAML considers an <AudienceRestriction> element containing only empty <Audience> elements
675
+ to be valid. That means an otherwise valid SAML response with a condition like this would be valid:
676
+
677
+ ```xml
678
+ <AudienceRestriction>
679
+ <Audience />
680
+ </AudienceRestriction>
681
+ ```
682
+
683
+ You may enforce that an <AudienceRestriction> element containing only empty <Audience> elements
684
+ is invalid using the `settings.security[:strict_audience_validation]` parameter.
685
+
686
+ ```ruby
687
+ settings.security[:strict_audience_validation] = true
688
+ ```
689
+
667
690
  #### Key Rollover
668
691
 
669
692
  To update the SP X.509 certificate and private key without disruption of service, you may define the parameter
@@ -767,7 +790,13 @@ Here is an example that we could add to our previous controller to process a SAM
767
790
  # Method to handle IdP initiated logouts
768
791
  def idp_logout_request
769
792
  settings = Account.get_saml_settings
770
- logout_request = OneLogin::RubySaml::SloLogoutrequest.new(params[:SAMLRequest])
793
+ # ADFS URL-Encodes SAML data as lowercase, and the toolkit by default uses
794
+ # uppercase. Turn it True for ADFS compatibility on signature verification
795
+ settings.security[:lowercase_url_encoding] = true
796
+
797
+ logout_request = OneLogin::RubySaml::SloLogoutrequest.new(
798
+ params[:SAMLRequest], settings: settings
799
+ )
771
800
  if !logout_request.is_valid?
772
801
  logger.error "IdP initiated LogoutRequest was not valid!"
773
802
  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 (NOT YET RELEASED)
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
- attr_reader :uuid
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.
@@ -442,7 +442,7 @@ module OneLogin
442
442
  priority = Array(priority)
443
443
  if priority.any?
444
444
  values = nodes.map(&:text)
445
- Array(priority).detect { |candidate| values.include?(candidate) }
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
- attr_reader :uuid
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.
@@ -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'])
@@ -613,7 +613,12 @@ module OneLogin
613
613
  #
614
614
  def validate_audience
615
615
  return true if options[:skip_audience]
616
- return true if audiences.empty? || settings.sp_entity_id.nil? || settings.sp_entity_id.empty?
616
+ return true if settings.sp_entity_id.nil? || settings.sp_entity_id.empty?
617
+
618
+ if audiences.empty?
619
+ return true unless settings.security[:strict_audience_validation]
620
+ return append_error("Invalid Audiences. The <AudienceRestriction> element contained only empty <Audience> elements. Expected audience #{settings.sp_entity_id}.")
621
+ end
617
622
 
618
623
  unless audiences.include? settings.sp_entity_id
619
624
  s = audiences.count > 1 ? 's' : '';
@@ -280,7 +280,9 @@ module OneLogin
280
280
  :digest_method => XMLSecurity::Document::SHA1,
281
281
  :signature_method => XMLSecurity::Document::RSA_SHA1,
282
282
  :check_idp_cert_expiration => false,
283
- :check_sp_cert_expiration => false
283
+ :check_sp_cert_expiration => false,
284
+ :strict_audience_validation => false,
285
+ :lowercase_url_encoding => false
284
286
  }.freeze
285
287
  }.freeze
286
288
  end
@@ -248,32 +248,8 @@ module OneLogin
248
248
  return true unless options.has_key? :get_params
249
249
  return true unless options[:get_params].has_key? 'Signature'
250
250
 
251
- # SAML specifies that the signature should be derived from a concatenation
252
- # of URI-encoded values _as sent by the IDP_:
253
- #
254
- # > Further, note that URL-encoding is not canonical; that is, there are multiple legal encodings for a given
255
- # > value. The relying party MUST therefore perform the verification step using the original URL-encoded
256
- # > values it received on the query string. It is not sufficient to re-encode the parameters after they have been
257
- # > processed by software because the resulting encoding may not match the signer's encoding.
258
- #
259
- # <http://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf>
260
- #
261
- # If we don't have the original parts (for backward compatibility) required to correctly verify the signature,
262
- # then fabricate them by re-encoding the parsed URI parameters, and hope that we're lucky enough to use
263
- # the exact same URI-encoding as the IDP. (This is not the case if the IDP is ADFS!)
264
- options[:raw_get_params] ||= {}
265
- if options[:raw_get_params]['SAMLRequest'].nil? && !options[:get_params]['SAMLRequest'].nil?
266
- options[:raw_get_params]['SAMLRequest'] = CGI.escape(options[:get_params]['SAMLRequest'])
267
- end
268
- if options[:raw_get_params]['RelayState'].nil? && !options[:get_params]['RelayState'].nil?
269
- options[:raw_get_params]['RelayState'] = CGI.escape(options[:get_params]['RelayState'])
270
- end
271
- if options[:raw_get_params]['SigAlg'].nil? && !options[:get_params]['SigAlg'].nil?
272
- options[:raw_get_params]['SigAlg'] = CGI.escape(options[:get_params]['SigAlg'])
273
- end
251
+ options[:raw_get_params] = OneLogin::RubySaml::Utils.prepare_raw_get_params(options[:raw_get_params], options[:get_params], settings.security[:lowercase_url_encoding])
274
252
 
275
- # If we only received the raw version of SigAlg,
276
- # then parse it back into the decoded params hash for convenience.
277
253
  if options[:get_params]['SigAlg'].nil? && !options[:raw_get_params]['SigAlg'].nil?
278
254
  options[:get_params]['SigAlg'] = CGI.unescape(options[:raw_get_params]['SigAlg'])
279
255
  end
@@ -335,7 +311,6 @@ module OneLogin
335
311
 
336
312
  true
337
313
  end
338
-
339
314
  end
340
315
  end
341
316
  end
@@ -13,7 +13,7 @@ module OneLogin
13
13
  class SloLogoutresponse < SamlMessage
14
14
 
15
15
  # Logout Response ID
16
- attr_reader :uuid
16
+ attr_accessor :uuid
17
17
 
18
18
  # Initializes the Logout Response. A SloLogoutresponse Object that is an extension of the SamlMessage class.
19
19
  # Asigns an ID, a random uuid.
@@ -32,6 +32,7 @@ module OneLogin
32
32
  (\d+)W # 8: Weeks
33
33
  )
34
34
  $)x.freeze
35
+ UUID_PREFIX = '_'
35
36
 
36
37
  # Checks if the x509 cert provided is expired
37
38
  #
@@ -166,27 +167,36 @@ module OneLogin
166
167
  #
167
168
  # @param rawparams [Hash] Raw GET Parameters
168
169
  # @param params [Hash] GET Parameters
170
+ # @param lowercase_url_encoding [bool] Lowercase URL Encoding (For ADFS urlencode compatiblity)
169
171
  # @return [Hash] New raw parameters
170
172
  #
171
- def self.prepare_raw_get_params(rawparams, params)
173
+ def self.prepare_raw_get_params(rawparams, params, lowercase_url_encoding=false)
172
174
  rawparams ||= {}
173
175
 
174
176
  if rawparams['SAMLRequest'].nil? && !params['SAMLRequest'].nil?
175
- rawparams['SAMLRequest'] = CGI.escape(params['SAMLRequest'])
177
+ rawparams['SAMLRequest'] = escape_request_param(params['SAMLRequest'], lowercase_url_encoding)
176
178
  end
177
179
  if rawparams['SAMLResponse'].nil? && !params['SAMLResponse'].nil?
178
- rawparams['SAMLResponse'] = CGI.escape(params['SAMLResponse'])
180
+ rawparams['SAMLResponse'] = escape_request_param(params['SAMLResponse'], lowercase_url_encoding)
179
181
  end
180
182
  if rawparams['RelayState'].nil? && !params['RelayState'].nil?
181
- rawparams['RelayState'] = CGI.escape(params['RelayState'])
183
+ rawparams['RelayState'] = escape_request_param(params['RelayState'], lowercase_url_encoding)
182
184
  end
183
185
  if rawparams['SigAlg'].nil? && !params['SigAlg'].nil?
184
- rawparams['SigAlg'] = CGI.escape(params['SigAlg'])
186
+ rawparams['SigAlg'] = escape_request_param(params['SigAlg'], lowercase_url_encoding)
185
187
  end
186
188
 
187
189
  rawparams
188
190
  end
189
191
 
192
+ def self.escape_request_param(param, lowercase_url_encoding)
193
+ CGI.escape(param).tap do |escaped|
194
+ next unless lowercase_url_encoding
195
+
196
+ escaped.gsub!(/%[A-Fa-f0-9]{2}/) { |match| match.downcase }
197
+ end
198
+ end
199
+
190
200
  # Validate the Signature parameter sent on the HTTP-Redirect binding
191
201
  # @param params [Hash] Parameters to be used in the validation process
192
202
  # @option params [OpenSSL::X509::Certificate] cert The Identity provider public certtificate
@@ -333,8 +343,12 @@ module OneLogin
333
343
  end
334
344
  end
335
345
 
346
+ def self.set_prefix(value)
347
+ UUID_PREFIX.replace value
348
+ end
349
+
336
350
  def self.uuid
337
- RUBY_VERSION < '1.9' ? "_#{@@uuid_generator.generate}" : "_#{SecureRandom.uuid}"
351
+ "#{UUID_PREFIX}" + (RUBY_VERSION < '1.9' ? "#{@@uuid_generator.generate}" : "#{SecureRandom.uuid}")
338
352
  end
339
353
 
340
354
  # Given two strings, attempt to match them as URIs using Rails' parse method. If they can be parsed,
@@ -1,5 +1,5 @@
1
1
  module OneLogin
2
2
  module RubySaml
3
- VERSION = '1.13.0'
3
+ VERSION = '1.14.0'
4
4
  end
5
5
  end
data/ruby-saml.gemspec CHANGED
@@ -41,7 +41,7 @@ Gem::Specification.new do |s|
41
41
  s.add_runtime_dependency('nokogiri', '>= 1.5.10', '<= 1.6.8.1')
42
42
  s.add_runtime_dependency('json', '< 2.3.0')
43
43
  elsif RUBY_VERSION < '2.3'
44
- s.add_runtime_dependency('nokogiri', '>= 1.9.1', '<= 1.10.0')
44
+ s.add_runtime_dependency('nokogiri', '>= 1.9.1', '< 1.10.0')
45
45
  else
46
46
  s.add_runtime_dependency('nokogiri', '>= 1.10.5')
47
47
  s.add_runtime_dependency('rexml')
@@ -54,7 +54,12 @@ Gem::Specification.new do |s|
54
54
  s.add_development_dependency('shoulda', '~> 2.11')
55
55
  s.add_development_dependency('simplecov')
56
56
  s.add_development_dependency('systemu', '~> 2')
57
- s.add_development_dependency('timecop', '<= 0.6.0')
57
+
58
+ if RUBY_VERSION < '2.1'
59
+ s.add_development_dependency('timecop', '<= 0.6.0')
60
+ else
61
+ s.add_development_dependency('timecop', '~> 0.9')
62
+ end
58
63
 
59
64
  if defined?(JRUBY_VERSION)
60
65
  # All recent versions of JRuby play well with pry
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-saml
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.13.0
4
+ version: 1.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OneLogin LLC
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-06 00:00:00.000000000 Z
11
+ date: 2022-02-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -140,16 +140,16 @@ dependencies:
140
140
  name: timecop
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
- - - "<="
143
+ - - "~>"
144
144
  - !ruby/object:Gem::Version
145
- version: 0.6.0
145
+ version: '0.9'
146
146
  type: :development
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
- - - "<="
150
+ - - "~>"
151
151
  - !ruby/object:Gem::Version
152
- version: 0.6.0
152
+ version: '0.9'
153
153
  - !ruby/object:Gem::Dependency
154
154
  name: pry-byebug
155
155
  requirement: !ruby/object:Gem::Requirement
@@ -237,8 +237,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
237
237
  - !ruby/object:Gem::Version
238
238
  version: '0'
239
239
  requirements: []
240
- rubyforge_project:
241
- rubygems_version: 2.5.2.1
240
+ rubygems_version: 3.0.6
242
241
  signing_key:
243
242
  specification_version: 4
244
243
  summary: SAML Ruby Tookit